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
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));
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?
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.
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>
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.