Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Setting style based on datatype?

Here's the problem. I'm binding a TreeView with a few different types of objects. Each object is a node, and SOME objects have a property called IsNodeExpanded, and of course, some others don't. Here's my style:

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

Now, the problem is when binding the items that DON'T have this property, we get this error in the output:

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')

Of course we get it a ton of times. So I'm trying to come up with a way to switch the style of the TreeViewItem based on the DataType it holds. Any idea on how to do this?

Some info: I can't do it manually for each item because I'm not creating them in XAML, they are created dynamically from a data source.

EDIT: I found this answer but it didn't work for me.

like image 834
Carlo Avatar asked Dec 31 '09 01:12

Carlo


2 Answers

Try using the TreeView.ItemContainerStyleSelector property with a custom StyleSelector class which changes the style depending if the bound object has that property or not.

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;
    }
}

In the XAML Resources:

<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}" />

In the XAML:

<TreeView ItemsSource="{Binding ...}"
          ItemContainerStyleSelector="{StaticResource TreeItemStyleSelector}">
like image 171
codekaizen Avatar answered Sep 28 '22 20:09

codekaizen


UPDATE

<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>

ALTERNATIVE WAY

Instead of Switching Styles, you should use HierarchicalDataTemplate and DataTemplate in order to style your TreeViewItem, they will work similarly unless you want to change certain inherited framework properties.

You can define different "DataTemplate" and "HeirarchicalDataTemplate" based on different types of object that are bound to for Item Template of TreeView.

And that is why these templates are designed to completely seperate your UI logic and code behind, using Selector etc or any such coding, you will introduce UI dependency more on your code behind, which WPF is not intended for.

Here is the link, TreeView DataBinding

And see how you can define item templates in resources,

<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>
like image 29
Akash Kava Avatar answered Sep 28 '22 19:09

Akash Kava