Show element only when selected and only when is not empty - wpf

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>

Related

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>

Hide checkbox until mouseover and make it stay once checked WPF

The checkbox is invisible until I mouse over which is what I want, but now I want it to stay visible once it is checked. I've tried implementing a multi trigger, but it doesn't seem to be working:
<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>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True"></Condition>
<Condition Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True"></Condition>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Visibility" Value="Visible"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
A MultiDataTrigger will only apply the setter when all conditions are met.
Represents a trigger that applies property values or performs actions when the bound data meet a set of conditions.
Apart from that, binding to the DataGridRow does not work, as it has no IsChecked property. Instead, add a Trigger that acts on the IsChecked property of the associated CheckBox.
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</Trigger>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>

Hide checkbox until mouse over WPF

I am new to XAML, but I would like the CheckBox option to be hidden on my application until the user mouses over the row and can check the box from there. Here is what I currently have and I'm not sure why it doesn't work.
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="cbkSelect"
IsChecked="{Binding Path=IsSelectedForOrder, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbkSelect, Path=IsMouseOver}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
If you want to display the CheckBox only if a user hovers the row, meaning any cell of a row, you can use a RelativeSource binding to the IsMouseOver property the parent row.
<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>
If you want it to be dislayed only if a user hovers over the CheckBox column, your style will not work as you do not receive the mouse events on a hidden control. You can work around this with a Border that is visible.
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border x:Name="cbkBorder" Background="Transparent">
<CheckBox Name="cbkSelect"
IsChecked="{Binding Path=IsSelectedForOrder, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbkBorder, Path=IsMouseOver}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

DataTrigger setter doesn't fire up

I have a two radioButtons and two textboxs
<RadioButton x:Name="AdmLnkRadio1" GroupName="AdmLnkgr1" Content="Link #1"/>
<RadioButton x:Name="AdmLnkRadio2" GroupName="AdmLnkgr1" Content="Link #2"/>
<TextBox x:Name="AdmLnkTextBoxName1" />
<TextBox x:Name="AdmLnkTextBoxName2" IsEnabled="False" >
<TextBox.Style>
<Style BasedOn="{StaticResource TextBoxBase}" TargetType="TextBox" >
<Style.Triggers>
<!--Trigger 1 -->
<DataTrigger Binding="{Binding ElementName=AdmLnkRadio1, Path=IsChecked}" Value="True">
<Setter Property="Text" Value="{Binding ElementName=AdmLnkTextBoxName1, Path=Text}"></Setter>
<Setter Property="IsEnabled" Value="False"></Setter>
</DataTrigger>
<!--Trigger 2 - Doesn't Fires UP!!!! -->
<DataTrigger Binding="{Binding ElementName=AdmLnkRadio2, Path=IsChecked}" Value="True">
<Setter Property="Text" Value=""></Setter>
<Setter Property="IsEnabled" Value="True"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
When user checks radiobutton 1 the trigger 1 works and second textbox text binds to the text of the first one. But when the second radiobutton is checked, the second trigger should fire up, but it doesn't. Thanks for any help
It does fire but because you're setting IsEnabled to fixed False value your style does not override it (Dependency Property Setting Precedence List). Try setting IsEnabled in your Style, like this
<TextBox x:Name="AdmLnkTextBoxName1" />
<TextBox x:Name="AdmLnkTextBoxName2">
<TextBox.Style>
<Style TargetType="TextBox" >
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=AdmLnkRadio1, Path=IsChecked}" Value="True">
<Setter Property="Text" Value="{Binding ElementName=AdmLnkTextBoxName1, Path=Text}"/>
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=AdmLnkRadio2, Path=IsChecked}" Value="True">
<Setter Property="Text" Value=""/>
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

Xamly determine if a ListBox.Items.Count > 0

Is there a way in XAML to determine if the ListBox has data?
I wanna set its IsVisibile property to false if no data.
The ListBox contains a HasItems property you can bind to. So you can just do this:
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
...
<ListBox
Visibility="{Binding HasItems,
RelativeSource={RelativeSource Self},
Converter=BooleanToVisibility}" />
Or as a Trigger so you don't need the converter:
<ListBox>
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger
Binding="{Binding HasItems, RelativeSource={RelativeSource Self}}"
Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
I haven't tested the bindings so there might be some typos but you should get the idea.
Do it in a trigger and you won't need a ValueConverter:
<ListBox>
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Triggers>
<DataTrigger
Binding="Items.Count, {Binding RelativeSource={RelativeSource Self}}"
Value="0">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
So that shows the ListBox by default, but if Items.Count is ever 0, the ListBox is hidden.
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Visibility" Value="Hidden"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Style>
You can probably make this work using a ValueConverter and normal binding.
Set Visibility to be:
Visibility = "{Binding myListbox.Items.Count, Converter={StaticResource VisibilityConverter}}"
Then set up your converter to return Visibility.Collapsed etc based on the value of the count.

Resources