Search code examples
c#wpfbindingdatatrigger

WPF bind to ancestor from UserControl that is added from code behind


I have a user control which I use to dynamically populate a ListBox from code behind. I want the colors of an icon reversed when the parent ListBoxItem is selected.

However the datatrigger doesn't work. I get the following error message: "Source not found: RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ListBoxItem', AncestorLevel='1'"

However, I got 2 cases in which the DataTrigger (as below) starts working.

  1. If I hard code the user control in my XAML. Which is not an option.
  2. If I change something about the Style (e.g. the default value from true and back to false). So basically if I force the style to be reevaluated.

So I think I know what is happening, but I don't know what to do about it: I create a new instance of the UserControl in code behinde and the Style and DataTrigger are immediately evaluated and throw an error (which makes sense, since it is not yet added to the VisualTree and therefore there is no ancestor to be found).

Heres the content of my user control:

<UserControl.Resources>
    <Style x:Key="FontAwesomeIconInvertedColorOnSelection" TargetType="fonts:FontAwesomeIcon">
        <Setter Property="ReverseColors" Value="False" />
        <Style.Triggers>
            <DataTrigger Binding="{Binding
                            RelativeSource={RelativeSource
                            Mode=FindAncestor,
                            AncestorType={x:Type ListBoxItem}},
                            Path=IsSelected}"
                         Value="True">
                <Setter Property="ReverseColors" Value="True" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</UserControl.Resources>
<Grid>
    <fonts:FontAwesomeIcon 
        Style="{StaticResource FontAwesomeIconInvertedColorOnSelection}" />
</Grid>

Can I somehow force the Style to be reevaluate upon UserControl.Loaded? Or do you maybe have another suggestion on how to get my desired behviour?


Solution

  • Your UserControl should expose an appropriate property, e.g. IsSelected that you would bind to the ListBoxItem's IsSelected property in the DataTemplate.

    <ListBox>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <local:MyIconControl
                    IsSelected="{Binding IsSelected,
                        RelativeSource={RelativeSource AncestorType=ListBoxItem}}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    In the UserControl's XAML, the property would be bound by another RelativeSource Binding:

    <Grid>
        <fonts:FontAwesomeIcon
            ReverseColors="{Binding IsSelected,
                RelativeSource={RelativeSource AncestorType=UserControl}}" />
    </Grid>