wpfxamldatatemplatedatatrigger

XAML WPF: Set Background Property on AncestorType from DataTemplate DataTrigger


I'm using WPF with a ListView (I could use a ListBox, problem is still the same).

I have Custom Style for my ListViewItem, and a DataTemplate for my Model. I want to change the background colour of the ListViewItem using a DataTrigger on my DataTemplate.

Is there a way to create the DataTrigger on the DataTemplate, and have it target an AncestorType of ListViewItem to set its background?

Below is a simplified version of my code.

Here is my model (ObservableObject implements INotifyPropertyChanged). For Context, It's an RFID Tag read, and the SeenRecently bool will indicate the tag was seen within the last second. When this property is true, I want my ListViewItem background colour set to 'Green'.

public class TagEntry : ObservableObject
{
  private string? serialNumber;

  public string? SerialNumber
  {
    get { return serialNumber; }
    set { SetProperty(ref serialNumber, value); }
  }

  private string? tagType;

  public string? TagType
  {
    get { return tagType; }
    set { SetProperty(ref tagType, value); }
  }

  private bool seenRecently;

  public bool SeenRecently
  {
    get { return seenRecently; }
    set { SetProperty(ref seenRecently, value); }
  }
}

Here is my ListViewItem Style (Removed some Setters for brevity). It has 2 triggers, which make sense since IsSelected and IsMouseOver are properties found on ListViewItem.

A 3rd trigger, for SeenRecently property on my Data Model to set the background. This does in fact work, but now I'm coupling this style with my Data Model, what if I want to re-use this style for some other data model?

<Style x:Key="myItemContainerStyle" 
       TargetType="{x:Type ListViewItem}">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListViewItem}">
                <Border Background="{TemplateBinding Background}">
                    <ContentPresenter/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsSelected" Value="True">
            <Setter Property="Background" Value="Beige"/>
            <Setter Property="BorderBrush" Value="OrangeRed"/>
        </Trigger>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background" Value="MediumPurple"/>
        </Trigger>
        <!-- This here works, but i'm now coupling my ListViewItem Style, with my Model -->
        <DataTrigger Binding="{Binding SeenRecently}" Value="True">
            <Setter Property="Background" Value="Green"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

Here is my DataTemplate, this is where the DataTrigger makes sense to be placed, but I don't how/if I can target an Ancestor of Type ListViewItem to set its background from here.

    <DataTemplate x:Key="TagTypeDataTemplate" DataType="model:TagEntry">
        <Grid Grid.Column="1" Margin="4">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Text="{Binding SerialNumber}" FontSize="24" FontWeight="SemiBold"/>
            <TextBlock Grid.Row="1" Text="{Binding TagType}" FontSize="18"/>
        </Grid>
    </DataTemplate>

Here's a sample image. Purple background for mouseover, and Green for SeenRecently Items. Example of ListViewItems Showing changed background. Maybe I'm thinking about this wrong, maybe I should have a special <Style TargetType="ListViewItem"> specifically for my Data model?


Solution

  • After some playing around, I think the best option for me is to create a base style for my ListViewItem. Then create a sub-Style, for my model, that triggers the change.

    I can easily re-use the base Style, and create a new small style for any other model I want to Trigger a change on.

        <Style x:Key="TagEntryListViewContainerStyle" 
               TargetType="ListViewItem" 
               BasedOn="{StaticResource BaseListViewContainerStyle}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=(model:TagEntry.SeenRecently)}" Value="True">
                    <Setter Property="Background" Value="LightGreen"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>