Conditional binding in column of datagrid - wpf

I have a DataGrid which itemsSource is an ObservableCollection<MyType>. This type has this properties:
long ID;
long IDCategory;
long? IDState01;
long? IDEstate02;
long? IDEste03;
I have 3 categories, if it is category 1, IDState01 is not null and the others states are null. If category is 2, the IDState02 is not null and the others are null and son on.
I have a state column in my DataGrid, which value depends on the category. So I would like to binding to the correct property depending on the category, so if the category is 1, it would bind property state01, if the category is 3 it would bind property state02 and so on.
I think that my DataGrid would be something like that:
<DataGrid HorizontalAlignment="Stretch" Margin="5,5,5,5" VerticalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Header="State">
<!--Something here, perhaps a datatrigger.-->
</DateGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Thanks.
EDIT: I would like to do it in XAML if it is possible, instead of using a converter.

introduce a property which will get/set the correct state based on category:
public long? State
{
get
{
if (IDCategory == 1) return IDState01;
if (IDCategory == 2) return IDState02;
return null;
}
set
{
if (IDCategory == 1) IDState01 = value;
else if (IDCategory == 2) IDState02 = value;
}
}
in DataGird bind column to a new property:
<DataGridTextColumn Header="State" Binding="{Binding State}"/>

I would like to do it in XAML if it is possible ...
You could use a DataGridTemplateColumn:
<DataGridTemplateColumn Header="State">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IDCategory}" Value="1">
<Setter Property="Text" Value="{Binding IDState01}" />
</DataTrigger>
<DataTrigger Binding="{Binding IDCategory}" Value="2">
<Setter Property="Text" Value="{Binding IDEstate02}" />
</DataTrigger>
<DataTrigger Binding="{Binding IDCategory}" Value="3">
<Setter Property="Text" Value="{Binding IDEste03}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IDCategory}" Value="1">
<Setter Property="Text" Value="{Binding IDState01}" />
</DataTrigger>
<DataTrigger Binding="{Binding IDCategory}" Value="2">
<Setter Property="Text" Value="{Binding IDEstate02}" />
</DataTrigger>
<DataTrigger Binding="{Binding IDCategory}" Value="3">
<Setter Property="Text" Value="{Binding IDEste03}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

I think you will need own DataTemplate for each category and DataTemplateSelector to assign DataTemplates based on category type.

Related

wpf datatrigger binding datacontext in textblock doesn't work

I'm struggeling with binding a datatrigger to a textblock property. I have added a working Datatrigger example in the TextBox.
The issue is the TextBlock DataTrigger I can not get working. At startup IsNameActive has value 'false' and BackGround is PaleVioletRed, but it does not change when IsNameActive changes.
public bool IsNameActive
{
get => !string.IsNullOrEmpty(FirstName);
}
public string FirstName
{
get => _firstName;
set
{
if (value != _firstName)
{
_firstName = value;
OnPropertyChanged(FirstName);
OnPropertyChanged(IsNameActive);
}
}
}
<TextBox
Grid.Row="0"
Grid.Column="1"
Margin="15"
Text="{Binding UpdateSourceTrigger=PropertyChanged, Path=FirstName, ValidatesOnDataErrors=true, NotifyOnValidationError=true}">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="RosyBrown" />
<Style.Triggers>
<DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="aa">
<Setter Property="Background" Value="DarkOliveGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="">
<Setter Property="Background" Value="CornflowerBlue" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBlock
Grid.Row="2"
Grid.Column="1"
Margin="15"
Text="TextBlock with binding">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.IsNameActive, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged}" Value="true">
<Setter Property="Background" Value="CornflowerBlue" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=DataContext.IsNameActive, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged}" Value="false">
<Setter Property="Background" Value="PaleVioletRed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
As #Ash pointed out in the comment section, you need to wrap nameof around IsNameActive when you call OnPropertyChanged:
OnPropertyChanged(nameof(IsNameActive));

Using multiple data triggers does not work

I want to detect when the selected item changes in a ListBox and a TreeView via DataTriggers
so this is my code
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=DirTreeView, Path=SelectedItemChanged}" Value="{x:Null}">
<Setter Property="Text" Value="{Binding ElementName=DirTreeView, Path=SelectedItem, Converter={StaticResource contentConverter}}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=listbox, Path=SelectionChanged}" Value="{x:Null}">
<Setter Property="Text" Value="{Binding ElementName=listbox, Path=SelectedItem, Converter={StaticResource contentConverter}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
if i comment out the last DataTrigger it works fine, but when I use both nothing works.
Why is that and how do I get this to work using both triggers?

Setting different styles for Dual level grouping with DataTrigger

I have a dual level grouping and thought I could define different styles with DataTriggers.
Thinking that GroupStyles.HeaderTemplate would bind to CollectionViewGroup I tried DataBinding to IsBottomLevel property.
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock x:Name="GroupName"
Text="{Binding Path=Name}"
Foreground="Red" />
<DataTemplate.Triggers>
<DataTrigger Binding="IsBottomLevel" Value="True" >
<Setter TargetName="GroupName" Property="Foreground" Value="Blue" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
Can I get this to work somehow?
Define your trigger in the Style of the TextBlock itself, TargetName normally is for ControlTemplates, then you can just drop that.
This is not a binding:
Binding="IsBottomLevel"
You should replace it with the following of course:
Binding="{Binding IsBottomLevel}"
<TextBlock Text="{Binding Name}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsBottomLevel}" Value="True">
<Setter Property="Foreground" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
If you set the red Foreground directly in the TextBlock declaration the trigger will have no effect due to precedence.

setting text on DataGridTextColumn

I am using a DataTrigger to replace empty cells with '-' text. My code:
<DataGridTextColumn Header="Time taken" Binding="{Binding Path=finish}" Width="Auto" x:Name="x">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=finish}" Value="{x:Null}">
<Setter Property="TextBlock.Text" Value="-" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
But I couldn't find the text being set. I tried changing the background of the TextBlock and its working. Why cant I set the Text property?
The Binding in the column might be overriding the Setter.
But you don't need a data trigger to do this. As there is a property in the binding that you can set for these kinds of scenarios.
TargetNullValue allows you to set a value in the case that the bindings path is null.
Binding="{Binding Path=finish, TargetNullValue=Whatever you want}"
Taken From:
What's the simplest way to display NULL values as "NULL" with WPF Data Binding?
Namespace
xmlns:sys="clr-namespace:System;assembly=mscorlib"
<DataGridTextColumn Header="Time taken" Binding="{Binding Path=finish}" Width="Auto" x:Name="x">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="{Binding Path=finish}"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=finish}" Value="{x:Static sys:String.Empty}">
<Setter Property="Text" Value="-"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>

WPF, display attribute value in TreeView instead of node name

If I have the following data template for a TreeView, what do I need to change so that each TreeViewItem shows the value of the name attribute on each XML node, instead of the node name?
<HierarchicalDataTemplate x:Key="NodeTemplate">
<TextBlock x:Name="tb"/>
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::node()" />
</HierarchicalDataTemplate.ItemsSource>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Text">
<Setter TargetName="tb" Property="Text" Value="{Binding Path=Value}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
<Setter TargetName="tb" Property="Text" Value="{Binding Path=Name}"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
Replace your binding with this:
<Setter TargetName="tb" Property="Text" Value="{Binding Path=Attributes[Name].Value}" />
Found the answer in this question.
Neeeeever mind, just had to replace Path=Name and Path=Value with XPath=#name in the two Setters.

Resources