WPF基于数据类型的设置样式?

时间:2009-12-31 01:03:29

标签: wpf data-binding styles

这是问题所在。我正在使用几种不同类型的对象绑定TreeView。每个对象都是一个节点,而某些对象有一个名为IsNodeExpanded的属性,当然,其他一些对象则没有。这是我的风格:

<Style TargetType="{x:Type TreeViewItem}">
    <Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}" />
</Style>

现在,问题是当绑定没有此属性的项时,我们在输出中出现此错误:

System.Windows.Data Error: 39 : BindingExpression path error: 'IsNodeExpanded' property not found on 'object' ''CompensationChannel' (HashCode=56992474)'. BindingExpression:Path=IsNodeExpanded; DataItem='CompensationChannel' (HashCode=56992474); target element is 'TreeViewItem' (Name=''); target property is 'IsExpanded' (type 'Boolean')

当然,我们得到了很多次。所以我试图想出一种基于它所拥有的DataType来切换TreeViewItem样式的方法。关于如何做到这一点的任何想法?

一些信息:我无法手动为每个项目执行此操作,因为我没有在XAML中创建它们,它们是从数据源动态创建的。

编辑:我找到this answer,但它对我不起作用。

4 个答案:

答案 0 :(得分:8)

尝试将TreeView.ItemContainerStyleSelector属性与自定义StyleSelector类一起使用,该类根据绑定对象是否具有该属性来更改样式。

public class TreeItemStyleSelector : StyleSelector
{
    public Style HasExpandedItemStyle { get; set; }
    public Style NoExpandedItemStyle { get; set; }

    public override Style SelectStyle(object item, DependencyObject container)
    {
        // Choose your test
        bool hasExpandedProperty = item.GetType().GetProperty("IsExpanded") != null;

        return hasExpandedProperty
                   ? HasExpandedItemStyle
                   : NoExpandedItemStyle;
    }
}

在XAML资源中:

<Style x:Key="IsExpandedStyle" TargetType="{x:Type TreeViewItem}">
    <Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}" />
</Style>

<Style x:Key="NoExpandedStyle" TargetType="{x:Type TreeViewItem}">
</Style>

<x:TreeViewItemStyleSelector x:Key="TreeViewItemStyleSelector"
                             HasExpandedItemStyle="{StaticResource IsExpandedStyle}"
                             NoExpandedItemStyle="{StaticResource NoExpandedStyle}" />

在XAML中:

<TreeView ItemsSource="{Binding ...}"
          ItemContainerStyleSelector="{StaticResource TreeItemStyleSelector}">

答案 1 :(得分:1)

<强>更新

<TreeView.Resources>

    ... following is only for one type of data
    <HierarchicalDataTemplate 
      DataType="{x:Type local:RegionViewModel}" 
      ItemsSource="{Binding Children}"
      >

      ... define your style
      <HierarchicalDataTemplate.ItemContainerStyle>
           <Style TargetType="{x:Type TreeViewItem}" 
                  ... following line is necessary
                  BasedOn="{StaticResource {x:Type TreeViewItem}}">
                ..... your binding stuff....
           </Style>
      </HierarchicalDataTemplate.ItemContainerStyle>

      <StackPanel Orientation="Horizontal">
        <Image Width="16" Height="16" 
           Margin="3,0" Source="Images\Region.png" />
        <TextBlock Text="{Binding RegionName}" />
      </StackPanel>
    </HierarchicalDataTemplate>
...
</TreeView.Resources>

替代方式

除了切换样式之外,您应该使用HierarchicalDataTemplate和DataTemplate来设置TreeViewItem的样式,除非您想要更改某些继承的框架属性,否则它们的工作方式类似。

您可以根据TreeView项目模板绑定的不同类型的对象定义不同的“DataTemplate”和“HeirarchicalDataTemplate”。

这就是为什么这些模板被设计为完全分离你的UI逻辑和代码,使用Selector等或任何这样的编码,你将更多地介绍你的代码背后的UI依赖,WPF不适合。

这是链接, TreeView DataBinding

了解如何在资源中定义项目模板

<TreeView.Resources>
    <HierarchicalDataTemplate 
      DataType="{x:Type local:RegionViewModel}" 
      ItemsSource="{Binding Children}"
      >
      <StackPanel Orientation="Horizontal">
        <Image Width="16" Height="16" 
           Margin="3,0" Source="Images\Region.png" />
        <TextBlock Text="{Binding RegionName}" />
      </StackPanel>
    </HierarchicalDataTemplate>

    <HierarchicalDataTemplate 
      DataType="{x:Type local:StateViewModel}" 
      ItemsSource="{Binding Children}"
      >
      <StackPanel Orientation="Horizontal">
        <Image Width="16" Height="16" 
          Margin="3,0" Source="Images\State.png" />
        <TextBlock Text="{Binding StateName}" />
      </StackPanel>
    </HierarchicalDataTemplate>

    <DataTemplate DataType="{x:Type local:CityViewModel}">
      <StackPanel Orientation="Horizontal">
        <Image Width="16" Height="16" 
           Margin="3,0" Source="Images\City.png" />
        <TextBlock Text="{Binding CityName}" />
      </StackPanel>
    </DataTemplate>
  </TreeView.Resources>

答案 2 :(得分:0)

在绑定工作上使用FallbackValue会对你有用吗?如果绑定失败,这将适用...

<Style TargetType="{x:Type TreeViewItem}">
    <Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay, FallbackValue=False}" />
</Style>

答案 3 :(得分:0)

基于DataTrigger的解决方案:

 <UserControl.Resources>
      <converters:DataTypeConverter x:Key="DataTypeConverter"/>
 </UserControl.Resources>
 <!-- .... -->
 <Style TargetType="{x:Type TreeViewItem}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Converter={StaticResource DataTypeConverter}}" 
                         Value="{x:Type yourClasses:ClassWithIsNodeExpanded}">
                <Setter Property="IsExpanded" Value="{Binding IsNodeExpanded}" />
            </DataTrigger>
  </Style>         

和DataTypeConverter:

namespace Converters
{
/// <summary>
///     Implement an IValueConverter named DataTypeConverter, which accepts an object and returns its Type(as a
///     System.Type):
///     Usage:
///     Change your DataTrigger to use the Converter, and set the value to the Type:
///     <DataTrigger Binding="{Binding SelectedItem,  
///       Converter={StaticResource DataTypeConverter}}"
///         Value="{x:Type local:MyType}">
///         ...
///     </DataTrigger>
///     Declare DataTypeConverter in the resources:
///     <UserControl.Resources>
///         <v:DataTypeConverter x:Key="DataTypeConverter"></v:DataTypeConverter>
///     </UserControl.Resources>
/// </summary>
[ValueConversion(typeof(object), typeof(Type))]
public class DataTypeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        return value.GetType();
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
}