Using multiple data triggers does not work - wpf

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?

Related

Show element only when selected and only when is not empty

I am in a similar case that this question:
Displaying Content only when ListViewItem is Selected
I have a ComboBox that I only want to show when the ListViewItem that contains it is selected and when the ComboBox is not empty (both conditions must be true). It is very easy to bind visibility to a readonly property that checks if the ItemsSource property in the ViewModel has any items, and with the above link it is also solved how to show it only when its ListViewItem is selected, but I am not able to join both conditions. How can I only show the ComboBox when the item is selected and the combo is not empty?
This Style in the ComboBox does the trick for showing only when is selected:
<ComboBox ItemsSource="{Binding DataContext.ListaPedidosPendientes, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" DisplayMemberPath="numero">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
How can I add there the second condition (ListaPedidosPendientes.Count > 0)?
Thank you
You can evaluate the HasItems property of the ComboBox https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.itemscontrol.hasitems?view=net-5.0
and invert the conditions: Visible by default, collapse when not selected or when no items. Untested Aircode:
<ComboBox ItemsSource="{Binding DataContext.ListaPedidosPendientes, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" DisplayMemberPath="numero">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<Trigger Property="HasItems" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
There are two triggers to be set.
And the conditions are opposite.
Since the trigger only checks for equality, so you can compare Items.Count with zero.
But the condition >0 cannot be checked.
<ComboBox ItemsSource="{Binding DataContext.ListaPedidosPendientes, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" DisplayMemberPath="numero">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Items.Count}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>

How to change the text color for an unchecked row in xaml

I would like to gray out the text associated to an unchecked row in my application using xaml. I tried the following, but it is getting overwritten:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="cbkSelect"
IsChecked="{Binding Path=IsSelectedForOrder, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Foreground" Value="Gray"/>
</Trigger>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
As the Foreground color should be applied to all cells of a given row, you can remove the trigger. The trigger inside the CellTemplate will only apply to to the CheckBox that the style is applied to.
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="cbkSelect"
IsChecked="{Binding Path=IsSelectedForOrder, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
If you want to apply the foreground color in normal state, but not in selected state, add this RowStyle.
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelectedForOrder}" Value="True">
<Setter Property="Foreground" Value="Gray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
If you want to apply the style in selected state, too, add this CellStyle. In this case a RelativeSource binding is used to access the data context of the row which contains IsSelectedForOrder.
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsSelectedForOrder, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True">
<Setter Property="Foreground" Value="Gray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>

Why is the DataTrigger to set the header of a DataGridColunm not working?

I am trying to set the header of the DataGrid columns according to a value of a property in my view model. I am using this code:
<DataGridTextColumn.Header>
<TextBlock>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="My Default Header"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ucPrincipal, Path=DataContext.MyPrperty.ID}" Value="3" >
<Setter Property="Text" Value="Header 2"/>
</DataTrigger>
<DataTrigger Binding="{Binding DataContext.MyProperty.ID, RelativeSource={RelativeSource AncestorType={x:Type local:myUcViewType}}}" Value="4" >
<Setter Property="Text" Value="Header 3"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataGridTextColumn.Header>
But the header is always set to the default value. The values of ID are set to 3 and 4 correctly.
I am trying to use two different ways to get the value of the ID which is the source property that is used by the DataTrigger to set the header text.
Thanks.

binding a checkbox value with a textblock text on xaml

I want to bind a checkbox values ischecked=true or ischecked=false with a textblock text "Activé" or "Désactivé" only on Xaml and without using a code behind is that possible?
Check out datatriggers.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.datatrigger?view=netframework-4.7.2
https://www.wpf-tutorial.com/styles/trigger-datatrigger-event-trigger/
And here is an example that should work for you.
<CheckBox Name="MyCheckBox" Content="IsActive"/>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="Activé"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyCheckBox, Path=IsChecked}" Value="False">
<Setter Property="Text" Value="Désactivé"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>

How to bind a DataTrigger to a non-bound attribute?

This is not in a template, it is a <Label> in the body of my XAML document. Notice that the Content of the label is set to 'PENDING'. This is monitoring a server connection and at various times the code-behind might change the value of the content to CONNECTED or ERROR. When that happens, I would like for the color of the text to change. I thought this would do it, but it doesn't... all I get is black text.
<Label x:Name="lbl_Connected" Content="PENDING" FontWeight="Bold" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content.Value}" Value="CONNECTED">
<Setter Property="Label.Foreground" Value="Green"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content.Value}" Value="PENDING">
<Setter Property="Label.Foreground" Value="Yellow"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content.Value}" Value="ERROR">
<Setter Property="Label.Foreground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
Can someone tell me how I should be doing this?
Just remove the ".Value" part from the Binding's Path, i.e.:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content}" Value="CONNECTED">
<Setter Property="Label.Foreground" Value="Green"></Setter>
</DataTrigger>
Anyway if I were you, I would use a Binding in order to set the Label content and a converter to handle the Foreground color.

Resources