I have a ListView
, with its items represented by an ItemTemplate
like so:
<ListView dependencyObjects:InterestingItem.Interesting="{Binding InterestingItem}"
ItemsSource="{Binding Quotations}" >
<ListView.ItemTemplate>
<DataTemplate>
<Border>
<Grid>
<StackPanel x:Name="NotImportant">
</StackPanel>
<Grid x:Name="HiddenGrid"
Background="Red"
Visibility="Hidden" >
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<Grid.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.InterestingItem,
RelativeSource={RelativeSource AncestorType={x:Type ListView }}}"
Value="*this instance here*!">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Grid.Triggers>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The ListView
has an attached property InterestingItem
that is one of the items in the ListView
.
What I can't hook up is when the InterestingItem
is the same as one of the items, the second Grid
should become visible.
I would prefer not to change and bind to the actual objects in the list - but rather have the ListView
control which item is to be altered.
What is the Value
in the DataTrigger
that I need?
CodePudding user response:
There are multiple issues in your XAML and conceptually that prevent it from working.
To bind attached properties, you have to use the correct syntax with parentheses.
Path="{Binding (local:InterestingItem.Interesting), RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
See the Binding path syntax documenation for reference.
The
Triggers
property does only supportEventTriggers
, seeFrameworkElement.Triggers
.Note that the collection of triggers established on an element only supports EventTrigger, not property triggers (Trigger). If you require property triggers, you must place these within a style or template and then assign that style or template to the element either directly through the Style property, or indirectly through an implicit style reference.
You cannot bind the
Value
property ofDataTrigger
, since it is not a dependency property.
You could of course change the bound type to expose a property that indicates if it is a special object or not and bind that in XAML using a DataTrigger
, similar to this (where IsSpecial
is the new bool
property).
<Grid x:Name="HiddenGrid"
Background="Red">
<TextBlock Text="Hidden Grid"/>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSpecial}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
If you want to stick to your current approach, you could create a custom IMultiValueConverter
that enables binding multiple properties. It would check if all of the bound values are equal and return Visibility.Visible
or Visibility.Hidden
otherwise. This example uses Linq to check this and supports an arbitrary number of values bound, but there are many other options.
public class EqualityToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values is null || values.Length < 2)
return Binding.DoNothing;
return values.Distinct().Count() == 1 ? Visibility.Visible : Visibility.Hidden;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
Next, instantiate the converter in the resources of the ListView
or any other resource dictionary in scope and bind the Visibility
property of the Grid
to both the current item (just <Binding/>
) and the attached property local:InterestingItem.Interesting
with a MultiBinding
that uses the converter to convert them to a Visibility
.
<ListView local:InterestingItem.Interesting="{Binding InterestingItem}"
ItemsSource="{Binding Quotations}">
<ListView.Resources>
<local:EqualityToVisibilityConverter x:Key="EqualityToVisibilityConverter"/>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<Border>
<Grid>
<StackPanel x:Name="NotImportant">
<TextBlock Text="Not Important"/>
</StackPanel>
<Grid x:Name="HiddenGrid"
Background="Red">
<Grid.Visibility>
<MultiBinding Converter="{StaticResource EqualityToVisibilityConverter}">
<Binding/>
<Binding Path="(local:InterestingItem.Interesting)"
RelativeSource="{RelativeSource AncestorType={x:Type ListView}}"/>
</MultiBinding>
</Grid.Visibility>
<TextBlock Text="Hidden Grid"/>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
There are two other things to note here. I have added two dummy TextBlock
s, otherwise the result will not be visible, as the panels are empty. Replace them with your content. Furthermore, both the StackPanel
and the Grid
are overlapping in the parent Grid
, I do not know if this is intentional or not, but you can change it by adding rows or columns and moving the elements there.
CodePudding user response:
What is the Value in the DataTrigger that I need?
I am afraid XAML has no support for something like the this
keyword in C#.
You may use a MultiBinding
with an IMultiValueConverter
implementation that determines whether the items are equal:
<Grid x:Name="HiddenGrid" Background="Red">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding>
<MultiBinding.Converter>
<local:MultiConverter />
</MultiBinding.Converter>
<Binding Path="{Binding Path=DataContext.InterestingItem,
RelativeSource={RelativeSource AncestorType={x:Type ListView }}}" />
<Binding Path="{Binding}" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
Converter:
public class MultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) =>
values != null && values.Length == 2 && values[0] == values[1];
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
throw new NotSupportedException();
}
Note that you cannot set the Visibility
property of the Grid
to a local value if you want to be able to override the value using a Style
setter.
<Grid x:Name="HiddenGrid" Background="Red" Visibility="Hidden">