In a WPF ListView
I have 3 columns (GridViewColumn
s). The first column is auto-incremental (1, 2, 3, and so on). The second one is simply a string and the last column contains 3 buttons, delete, move up and move down (all in the same column). Let's focus on the last one which is the one I am interested in.
What do I try to achieve?
I want to collapse the move up button (Up Arrow) from the first row of ListView
and also collapse the move down button (Down Arrow) from the last row of ListView
. For me, first row is that which start from 1. See below image.
What did I do? I applied a style to the buttons to collapse the move up button in the first row and it worked, but now I do not know how to do the same for the move down button in the last row (see button marked with a red circle in the above image).
Here is my code:
<Style x:Key="myButtonStyle" TargetType="Button">
<Style.Triggers>
<!-- Collapse move up button from the first row of the ListView -->
<DataTrigger Binding="{Binding (ItemsControl.AlternationIndex),
RelativeSource={RelativeSource AncestorType=ListViewItem}}" Value="1">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<!-- HOWTO: Collapse move down button from the last row of the ListView here??? -->
</Style.Triggers>
</Style>
As you see above I use ItemsControl.AlternationIndex
which I am using it to number the first column of the ListView
.
CodePudding user response:
What you would need to do is the compare the current alternation index to the alternation count. If Index == Alternation Count - 1 then it is the last row and you can hide the button. However, the Value
property of DataTrigger
is not a dependency property, so you cannot bind the alternation count as value. Moreover, you would need to compare the alternation count minus one to the alternation index.
What you can do instead is use a MultiBinding
to bind the alternation index and the alternation count. Then you can create a custom value converter that checks for the condition above and returns true
or false
. As Value
of the DataTrigger
you would only have to assign True
.
Let's create the converter, which returns if there are two values that are off by one. You could make this less abstract, but this way it does not matter in which order you bind index and count.
public class OffByOneToBoolConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length != 2 || !(values[0] is int value1) || !(values[1] is int value2))
return Binding.DoNothing;
return Math.Abs(value1 - value2) == 1;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
For the down button, create a new style with the MultiBinding
.
<Style x:Key="myDownButtonStyle" TargetType="Button">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource OffByOneToBoolConverter}">
<Binding Path="(ItemsControl.AlternationIndex)"
RelativeSource="{RelativeSource AncestorType=ListViewItem}"/>
<Binding Path="(ItemsControl.AlternationCount)" RelativeSource="{RelativeSource AncestorType={x:Type ListView}}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
Do not forget to create an instance of the OffByOneToBoolConverter
in any resource dictionary in scope. By the way, you need separate button styles for the Up and Down buttons, since both have exclusive conditions. For the Up button consider Visibility
set to Hidden
, then the button down button stays in place even if the Up button is not shown.