【问题标题】:Separately Binding to Multiple Instances of a Templated Control分别绑定到模板化控件的多个实例
【发布时间】:2016-09-05 17:56:06
【问题描述】:

我正在寻找使用模板和 MVVM 模式创建类似控件的网格。这是我遇到的问题的简单描述。 我首先为 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 类中实例化一些 Elements,它是 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>

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

    <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 内而不是 Element 内寻找字段 dText。这是因为 ControlTemplate 忽略了 Content="{Binding Path=element0}"

尝试修复它的明显方法是使用 DataTemplate 而不是 ControlTemplate。我已经做到了 - 创建了一个与上述 ControlTemplate 具有完全相同的内部结构的 DataTemplate,其中 Key="DTemplate"。然后我将实例化 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 元素中居中,并且橙色不再填充 Grid 单元格),因为我丢失了 ControlTemplate

所以,我可以让它正确渲染,也可以绑定,但不能两者兼而有之。

【问题讨论】:

    标签: c# wpf xaml mvvm


    【解决方案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>
    

    【讨论】:

    • 我想我更了解您现在想要做什么,并且认为您正在寻找样式而不是模板。无论您使用的是 DataTemplate 还是 ControlTemplate,都需要设置 ContentControl 的水平和垂直对齐属性,以使实际内容在包含元素中居中。如果您想一次性定义这种行为并在任何地方使用,您应该使用带有 setter 的样式作为对齐属性,然后将样式应用于 ContentControl。
    • 谢谢;效果很好。我保留了 DataTemplate,并将其扩展为包含我需要的其他元素,并用您的 Style 替换了 ControlTemplate。 TemplateBinding 属性是我需要的另一部分。您是否要编辑您的帖子以消除 1.、2.、3. 点,我将标记为答案。
    • 完成。很高兴它有帮助。
    猜你喜欢
    • 1970-01-01
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多