【问题标题】:What is the standard way to configure an ItemTemplate without violating the Item separation?在不违反项目分离的情况下配置 ItemTemplate 的标准方法是什么?
【发布时间】:2015-03-14 18:10:40
【问题描述】:

在使用 WPF 一段时间后,这实际上是我第一次遇到这样一种情况,即我有一个 ListBox 的 ItemTemplate,我希望根据项目本身之外的属性对其进行配置。当我想要一个字体选择器对话框时出现问题,用户可以在其中单击复选框以启用字体预览(实际上我改变了对这个实现的想法,但我仍然想知道答案)。

看来,因为 DataTemplate 可以在提供该类型的任何情况下使用,所以它被认为是一种很好的做法,不要绑定到项目配置之外的任何参数(似乎绑定到包含 DataTemplate 的属性的代码是特别迟钝。)

我想知道我应该如何实现这种情况。下面的代码有效,但绑定的是可视元素,而我宁愿绑定到 ViewModel 中的属性。

<Window x:Class="ScreenWriter.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}" >
    <Grid>
        <StackPanel>
            <CheckBox Name="ShowPreview" IsChecked="{Binding IsShowPreviewChecked}">
                Show Preview
            </CheckBox>
            <ListBox ItemsSource="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}">
                            <TextBlock.Style>
                                <Style>
                                    <Setter Property="TextBlock.FontFamily" Value="Arial" />
                                    <Style.Triggers>
                                        <DataTrigger Value="True" Binding="{Binding IsChecked, ElementName=ShowPreview}">
                                            <Setter Property="TextBlock.FontFamily" Value="{Binding}"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </TextBlock.Style>
                        </TextBlock>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
    </Grid>
</Window>

这似乎不是一个不寻常的情况,但我找不到任何不以“这是一种巧妙的方法来解决你不应该做的事情”为前缀的解决方案。 感谢您的帮助...

【问题讨论】:

    标签: c# wpf xaml data-binding datatemplate


    【解决方案1】:

    对于这个问题没有“最佳实践”解决方案。

    正如您所写,“DataTemplate 可用于提供该类型的任何情况......不绑定到项目配置之外的任何参数”。

    Binding/DataContext 基于LogicalTree。因此,您的 Trigger 绑定会尝试在树上找到最近的数据上下文(在本例中是您的 ListItem DataContext = FontFamily)

    您必须从“LogicalTree Island”到主树进行相同的“跳转”——就像您的控件/元素进行了跳转一样。 所以真正的问题是,如何才能顺利更改绑定源。

    您可以直接绑定目标元素 DataContext(CheckBox 没有 DataContext,但它的祖父 - Windows 有):

    <DataTrigger Binding="{Binding DataContext.IsShowPreviewChecked, ElementName=ShowPreview}" Value="True" >    
    

    或者直接查找 ListItem 祖先:

    <DataTrigger Binding="{Binding DataContext.IsShowPreviewChecked, RelativeSource={RelativeSource AncestorType=ListBox, Mode=FindAncestor}}" Value="True" >
    

    如果你会多次使用这个跳转,你会尝试使用BindingProxy 这将为您的绑定提供快捷方式:

    public class BindingProxy : Freezable
    {
        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }
    
        public object Context
        {
            get { return (object)GetValue(ContextProperty); }
            set { SetValue(ContextProperty, value); }
        }
    
        public static readonly DependencyProperty ContextProperty =
            DependencyProperty.Register("Context", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }    
    

    将此代理添加为资源:

    <Window.Resources>
        <this:BindingProxy x:Key="Proxy" Context="{Binding}" />
    </Window.Resources> 
    

    你的绑定是:

    <DataTrigger Binding="{Binding Context.IsShowPreviewChecked, Source={StaticResource Proxy}}" Value="True" >    
    

    不要忘记将 INotifyPropertyChanged 接口添加到您的 ViewModel

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多