单独绑定到模板化控件的多个实例

时间:2016-09-05 17:56:07

标签: c# wpf xaml mvvm

我正在寻找使用模板和MVVM模式创建类似控件的Grid。这是我遇到的问题的简单描述。 我首先定义一个要为Grid的每个元素实例化的类Element:

public class Element : INotifyPropertyChanged
{
    private string _dText = "Default";
    public string dText { get { return _dText; } set { _dText = value; NotifyPropertyChanged("dText"); } }

    internal Element(string aText) { dText = aText; }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String info)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(info));
    }
}

然后我在ViewModel类中实例化一些元素,这是一个Page的DataContext,如下所示:

public class MinVM
{
    private Element _element0 = new Element("Element0 initialization text 0");
    public Element element0 { get { return _element0; } set { _element0 = value; } }
    private Element _element1 = new Element("Element1 initialization text 1");
    public Element element1 { get { return _element1; } set { _element1 = value; } }
}

现在,在XAML Page.PageResources中,我创建了一个ControlTemplate,旨在为我提供一些很好的居中文本,如下所示:

    <ControlTemplate x:Name="cTemplate" x:Key="CTemplate" TargetType="ContentControl">
        <Grid x:Name="ctGrid" Background="Orange" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <TextBox x:Name ="dtBox" Background="Orange" BorderThickness="0" 
                VerticalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"
                Text="{Binding dText, FallbackValue=Default control template text}">
            </TextBox>
        </Grid>
    </ControlTemplate>

最后,在Page内容本身,我实例化其中的网格和元素,如下所示:

    <ContentControl x:Name="element0" Grid.Row="0" Template="{StaticResource CTemplate}" Content="{Binding Path=element0}"></ContentControl>
    <ContentControl x:Name="element1" Grid.Row="1" Template="{StaticResource CTemplate}" Content="{Binding Path=element1}"></ContentControl>

这一切都很好,正如你所料,但绑定不起作用,即我无法更改默认文本,因为WPF正在MinVM中寻找字段dText,而不是在Element内部。这是因为ControlTemplate忽略了Content =&#34; {Binding Path = element0}&#34;

尝试修复它的显而易见的方法是使用DataTemplate而不是ControlTemplate。我已经完成了 - 创建了一个与上述ControlTemplate完全相同的DataTemplate,Key =&#34; DTemplate&#34;。然后我将实例化XAML更改为

    <ContentControl x:Name="element0" Grid.Row="0" ContentTemplate="{StaticResource DTemplate}" Content="{Binding Path=element0}"></ContentControl>
    <ContentControl x:Name="element1" Grid.Row="1" ContentTemplate="{StaticResource DTemplate}" Content="{Binding Path=element1}"></ContentControl>

现在绑定正常工作,正如您所期望的那样 - 我可以愉快地更改控件文本。但渲染不再正常(文本不再在Grid元素中居中,而Orange不再填充Grid单元格)因为我已经丢失了ControlTemplate

所以,我可以让它正确渲染,也可以绑定,但不能同时渲染。

1 个答案:

答案 0 :(得分:1)

如果我理解你的需要,那么下面的XAML适合我(你可以提取样式):

<Window x:Class="WpfScratch.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfScratch"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="ContentTemplate">
            <TextBox TextAlignment="Center" Text="The obvious way to try and fix it is to use a DataTemplate instead of a ControlTemplate. I've done that - created a DataTemplate with exactly the same internals as the above ControlTemplate, with Key=DTemplate. I then change the instantiation XAML to" TextWrapping="Wrap"/>
        </DataTemplate>
        <Style x:Key="ContentControlStyle1" TargetType="{x:Type ContentControl}">
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="ContentTemplate" Value="{StaticResource ContentTemplate}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ContentControl}">
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <ContentControl Margin="4" Style="{DynamicResource ContentControlStyle1}"/>
    </Grid>
</Window>