c# WPF Tree View Item conditional styling - c#

How do you conditionally style treeviewitemson binded properties
I'm running into an issue where I want to style items in a Tree View, those that are valid selections vs hierarchy items.
I've tried to move my style tag into a TreeView.Resources and a TreeView.ItemContainerStyle tag but it still doesn't cause any formatting.
<TabItem Name="Queries" Header="Queries"
d:DataContext="{d:DesignInstance views:QuerySettingsView}">
<Grid>
<TreeView ItemsSource="{Binding QueryTreeModels}"
Width="500" Height="200" VerticalAlignment="Top"
HorizontalAlignment="Center" Margin="0,0,175,0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style >
<Setter Property="TreeViewItem.IsExpanded"
Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="TreeViewItem.IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsQuery}">
<Setter Property="TextBlock.Foreground" Value="Chartreuse" />
</DataTrigger>
<DataTrigger Binding="{Binding IsFolder}">
<Setter Property="TextBlock.Foreground" Value="Crimson" />
</DataTrigger>
</Style.Triggers>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</TabItem>

For DataTrigger you need to complete the condition by adding a value to DataTrigger bound field. like <DataTrigger Binding="{Binding IsQuery}" Value="true"> Refer the below code.
<TreeView x:Name="tree" Width="500" Height="200"
VerticalAlignment="Top" HorizontalAlignment="Center"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style >
<Setter Property="TreeViewItem.IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="TreeViewItem.IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsQuery}" Value="true">
<Setter Property="TreeViewItem.Foreground" Value="Chartreuse" />
</DataTrigger>
<DataTrigger Binding="{Binding IsFolder}" Value="true">
<Setter Property="TreeViewItem.Foreground" Value="Crimson" />
</DataTrigger>
</Style.Triggers>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<TextBlock Text="{Binding NodeName}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

Related

Trigger Content of ContentControl

I've got a DataTemplate which looks like this
<DataTemplate DataType="{x:Type viewModel:TreeViewLeafViewModel}">
<StackPanel Orientation="Horizontal">
<Image Name="leafImage"/>
<TextBlock Name="leafTextBlockDisplayName" VerticalAlignment="Center"/>
<TextBlock Name="leafTextBlockKeyGesture" VerticalAlignment="Center"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Row, Converter={StaticResource MatchTypeConverter},
ConverterParameter={x:Type viewModel:TreeViewLeafViewModel}}" Value="True">
<Setter Property="Source" TargetName="leafImage" Value="{Binding Path=Row.Icon, Mode=OneTime}" />
<Setter Property="Text" TargetName="leafTextBlockDisplayName" Value="{Binding Path=Row.DisplayName, Mode=OneTime}" />
<Setter Property="Text" TargetName="leafTextBlockKeyGesture" Value="{Binding Path=Row.KeyGesture.KeyModifierString, Mode=OneTime}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
I would like to replace the leafTextBlockKeyGesture by a TextBox if the IsEditing flag of the corresponding viewmodel is set to true. My Idea was to use a ContentControl inside the DataTemplate and change its Content depending on the IsEditing flag. I tried several solutions but I cannot find a working one.
Does anyone know how to do this?
Based on this answer you need something like this:
<StackPanel>
<StackPanel.Resources>
<DataTemplate x:Key="textbox">
<TextBox Text="edit me"/>
</DataTemplate>
<DataTemplate x:Key="textblock">
<TextBlock Text="can't edit"/>
</DataTemplate>
</StackPanel.Resources>
<CheckBox IsChecked="{Binding IsEditable}" Content="Editable"/>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource textblock}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditable}" Value="true">
<Setter Property="ContentTemplate" Value="{StaticResource textbox}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</StackPanel>

Style DataTrigger not evaluating on initial load

Title says it all
<Page.Resources>
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<DataTemplate x:Key="level1">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="GroupTitle" FontSize="16" FontWeight="Bold" Foreground="#FF000532" Text="{Binding name}"/>
<TextBlock x:Name="GroupDescription" Text="{Binding description}"/>
<TextBlock x:Name="Depth" Text="{Binding childDepth}"/>
<ItemsControl ItemsSource="{Binding settings}">
</ItemsControl>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="level2">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="GroupTitle" FontSize="16" FontWeight="Bold" Foreground="#FF000532" Text="{Binding name}"/>
<TextBlock x:Name="GroupDescription" Text="{Binding description}"/>
<TextBlock x:Name="Depth" Text="{Binding childDepth}"/>
<ItemsControl ItemsSource="{Binding settings}">
</ItemsControl>
<TabControl ItemsSource="{Binding groups}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource level1}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
<ContentControl x:Name="SettingsDisplayer" Content="{Binding settingGroup, ElementName=page}">
<ContentControl.Resources>
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</ContentControl.Resources>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource level1}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=childDepth}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource level1}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=childDepth}" Value="2">
<Setter Property="ContentTemplate" Value="{StaticResource level2}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=childDepth}" Value="3">
<Setter Property="ContentTemplate" Value="{StaticResource level3}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=childDepth}" Value="4">
<Setter Property="ContentTemplate" Value="{StaticResource level4}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
The getter for childDepth is not even being accessed because I put a console trace in there to know when it is. The property is populated as it shows the proper amount when seen in the Depth TextBlock in the DataTemplates.
The relevant property in my class:
public int childDepth { get { Console.WriteLine(this.getDepth()); return this.getDepth(); } }
No mater what the childDepth is it alwayse skipps the triggers and just uses the level1 template.
I feel silly, So apparently binding the ContentControl's Content is not the equivalent of binding data to the ContentControl, thus I needed to target my DataTrigger Bindings more specifically at my original DP since it doesn't just take the parent containers binding.
This worked:
<DataTrigger Binding="{Binding settingGroup.childDepth, ElementName=page}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource level1}" />
</DataTrigger>

WPF TabControl.Resources - Style one tab only

I am defining a TabControl as follows:
<TabControl Name="TabsControl"
ItemsSource="{Binding Tabs}"
SelectedItem="{Binding SelectedTabViewModel}">
<TabControl.Resources>
<!-- Rate Tables Tab -->
<DataTemplate DataType="{x:Type vm:RateTablesViewModel}">
<v:RateTablesUserControl />
</DataTemplate>
<!-- Rate Tables Tab -->
<DataTemplate DataType="{x:Type vm:RulesViewModel}">
<v:RulesUserControl />
</DataTemplate>
<!-- Rate Tables Tab -->
<DataTemplate DataType="{x:Type vm:CreateEmployeeMatchViewModel}">
<v:CreateEmployeeMatchUserControl />
</DataTemplate>
<!-- Rate Tables Tab -->
<DataTemplate DataType="{x:Type vm:ReportViewModel}">
<v:ReportUserControl />
</DataTemplate>
<!-- System Setup Tab -->
<DataTemplate DataType="{x:Type vm:SystemSetupViewModel}">
<v:SystemSetupUserControl />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Header}" />
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
In my Dictionary I have the following style:
<!-- Style for showing/hiding Setup tab -->
<Style x:Key="SetupTabStyle" TargetType="{x:Type TabItem}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=DataContext.StartArg}" Value="Setup">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=DataContext.StartArg}" Value="Edit">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
How do I apply this style to just the SystemSetup tab?

Change the colour of the particular element in Treeview WPF

I have a Treeview in my WPF application. On the fly, in run time, If the element of the Tree meets certain condition, It should change its Font color from Black To Red.!
XAML
<TreeView Grid.Column="0" Grid.Row="0" HorizontalAlignment="Stretch" Name="treeView1"
VerticalAlignment="Stretch"
SelectedItemChanged="treeView1_SelectedItemChanged" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Top" BorderThickness="0,0,0,1" BorderBrush="LightGray">
<TreeViewItem Header="Head Tree" ItemsSource="{Binding MainComps}">
<TreeViewItem.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Foreground" Value="RED" />
</DataTrigger>
</Style.Triggers>
</Style>
</TreeViewItem.ItemContainerStyle>
<TreeViewItem.Resources>
<HierarchicalDataTemplate DataType="{x:Type TextBlock}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Head Tree" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MainCompViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Maincompname}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:FeatureViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FeatureName}" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:CompViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Component}" />
</StackPanel>
</DataTemplate>
</TreeViewItem.Resources>
</TreeViewItem>
</TreeView>
Code behind
private void treeView1_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if(selected Item meets certain condition)
{
//Change color of tree node
}
}
How can I change the color of particular Node and leave it in the same color SO that when expanded again It should be in RED.
Any help would be appreciated.
You could create a boolean property in the model which is true when the elements meets the condition. Then you bind the Foreground like so:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=BoolProp}" Value="False">
<Setter Property="Foreground" Value="Blue"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=BoolProp}" Value="True">
<Setter Property="Foreground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
or with converter:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Foreground" Value="{Binding Path=BoolProp, Converter={StaticResource ResourceKey=TheKey}}"/>
</Style>
</TreeView.ItemContainerStyle>
Just change the Foreground:
TreeViewItem ti = (TreeViewItem)treeView1.SelectedItem;
ti.Foreground = Brushes.Red;
That is embedded into the template. You can only change the color by copying the default Aero-Style for the control and changing the hard-coded value.
Or by drilling down the visual tree on-load to change it that way.
To get the default style & tenmplate go through this MSDN
Can also check step wise EXAMPLE from here.

UserControl databinding with a treeview

I have a UserControl which contains a TreeView and a TextBlock:
<TreeView ItemsSource="{Binding FirstGeneration}" AllowDrop="True" Drop="TreeView_Drop" Width="300">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Normal" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" >
<Image Source="{Binding Path=Image}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<TextBlock Height="23" Name="textBlock1" Text="{Binding= ???}" Width="187" />
When I select an item of the treeview, I want do show some informations contained in the item (eg : the name of the selected item).
My problem is that I don't know how doing this binding, because when I select an item, the setter IsSelected of the item class is called.
Have you a best practice for doing that ?
Have a look at this in MSDN. And also the BindableSelectedItemBehaviour here.

Categories