1

I decomposed the problem to a simpler form but I still can't figure it out. I have a blank WPF application with a single button in the main XAML file.

The button has 2 icons that change depending on the state of the window. The icon's brush property is bound to the button's foreground property that changes if the window is active or inactive. All is fine until I maximize the window but the second icon is not shown. I know the maximize window state is working as I am able to change the background of the button to blue.

I get a binding failure error "Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Button', AncestorLevel='1'."

I use the same binding for the default state that works fine. It only fails to update inside the data trigger for the second icon.

Snapshot showing the problem & error

<Button Width="32" Height="32" HorizontalAlignment="Right" VerticalAlignment="Top">
<Button.Style>
    <Style TargetType="{x:Type Button}">

        <!--Default-->
        <Setter Property="Foreground" Value="Red"/>
        <Setter Property="Content">
            <Setter.Value>
                <Viewbox Width="16" Height="16" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
                    <Rectangle Width="16" Height="16">
                        <Rectangle.Fill>
                            <DrawingBrush>
                                <DrawingBrush.Drawing>
                                    <DrawingGroup>
                                        <DrawingGroup.Children>
                                            <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
                                            <GeometryDrawing Brush="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}}" Geometry="F1M12,12L4,12 4,4 12,4z M3,13L13,13 13,3 3,3z" />
                                        </DrawingGroup.Children>
                                    </DrawingGroup>
                                </DrawingBrush.Drawing>
                            </DrawingBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                </Viewbox>
            </Setter.Value>
        </Setter>

        <Style.Triggers>
            <!--Window inactive-->
            <DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Value="false">
                <Setter Property="Foreground" Value="White"/>
            </DataTrigger>

            <!--Window maximized-->
            <DataTrigger Binding="{Binding Path=WindowState, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Value="Maximized">
                <Setter Property="Background" Value="Blue"/>
                <Setter Property="Content">
                    <Setter.Value>
                        <Viewbox Width="16" Height="16" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
                            <Rectangle Width="16" Height="16">
                                <Rectangle.Fill>
                                    <DrawingBrush>
                                        <DrawingBrush.Drawing>
                                            <DrawingGroup>
                                                <DrawingGroup.Children>
                                                    <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
                                                    <GeometryDrawing Brush="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}}" Geometry="F1M11.999,10.002L10.998,10.002 10.998,5.002 5.998,5.002 5.998,4.001 11.999,4.001z M10.002,11.999L4.001,11.999 4.001,5.998 10.002,5.998z M5.002,3L5.002,5.002 3,5.002 3,13 10.998,13 10.998,10.998 13,10.998 13,3z" />
                                                </DrawingGroup.Children>
                                            </DrawingGroup>
                                        </DrawingBrush.Drawing>
                                    </DrawingBrush>
                                </Rectangle.Fill>
                            </Rectangle>
                        </Viewbox>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Button.Style>
</Button>

UPDATE

Thank you all for the help! Both of the below answers work perfectly. I also had a look at the suggested similar question by Sergey:

Datatrigger in WPF Style: Change content doesn't work

By putting the icon's path content inside the Resources and using those in the DataTrigger also works. However, with this method the icon's are not shown in the designer therefore I prefer the other two methods from the comments.

I've marked Sergey's answer as the accepted answer as the explanation of the RelativeSource Binding FindAncestor Mode and where I went wrong was very useful.

Thanks again all!

0

2 Answers 2

2

How about using 'ControlTemplate' instead of changing 'Content' itself in DataTrigger?

Here is an sample.

<Button>
    <Button.Resources>

        <DataTemplate x:Key="DEFAULT">
            <Border Background="#DDDDDD"
                            BorderBrush="#AA111111"
                            BorderThickness="1">
                <Viewbox Width="16" Height="16">
                    <Canvas Width="16" Height="16">
                        <Path Data="F1M16,16L0,16 0,0 16,0z" Fill="#00FFFFFF"/>
                        <Path Data="F1M12,12L4,12 4,4 12,4z M3,13L13,13 13,3 3,3z" 
                                      Fill="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}}, Path=Foreground}"/>
                    </Canvas>
                </Viewbox>
            </Border>
        </DataTemplate>

        <DataTemplate x:Key="MAX">
            <Border Background="Blue"
                            BorderBrush="#AA111111"
                            BorderThickness="1">
                <Viewbox Width="16" Height="16">
                    <Canvas Width="16" Height="16">
                        <Path Data="F1M16,16L0,16 0,0 16,0z" Fill="#00FFFFFF"/>
                        <Path Data="F1M11.999,10.002L10.998,10.002 10.998,5.002 5.998,5.002 5.998,4.001 11.999,4.001z M10.002,11.999L4.001,11.999 4.001,5.998 10.002,5.998z M5.002,3L5.002,5.002 3,5.002 3,13 10.998,13 10.998,10.998 13,10.998 13,3z" 
                                      Fill="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}}, Path=Foreground}"/>
                    </Canvas>
                </Viewbox>
            </Border>
        </DataTemplate>

        <Style TargetType="{x:Type Button}">
            <Setter Property="Width" Value="32"/>
            <Setter Property="Height" Value="32"/>
            <Setter Property="HorizontalAlignment" Value="Right"/>
            <Setter Property="VerticalAlignment" Value="Top"/>
            <Setter Property="ContentTemplate" Value="{StaticResource DEFAULT}"/>
            <Setter Property="Foreground" Value="Red"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <ContentPresenter/>
                        <ControlTemplate.Triggers>
                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=IsActive}" Value="False">
                                <Setter Property="Foreground" Value="#FFFFFF"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=WindowState}" Value="Maximized">
                                <Setter Property="ContentTemplate" Value="{StaticResource MAX}"/>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Button.Resources>
</Button>

It is recommended that you define the ControlTemplate and use the Trigger in the ControlTemplate to change the content.

Sign up to request clarification or add additional context in comments.

Comments

1

You misunderstand how the RelativeSource Binding FindAncestor Mode works. It does NOT work in such a way that property is binded to the nearest ancestor of the desired type at any given time. The ancestor is found only once , when the binding is created. In your case, the Viewbox created inside the DataTrigger setter does not have a button type ancestor at the time of creation (since it is not yet part of the visual tree). Therefore, this binding gives an error at the initialization.

The possible solutions are to use DataTemplate, as suggested by elena.kim, or ControlTemplate, as in the example below:

    <Button Width="32" Height="32" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Top">
        <Button.Template>
            <ControlTemplate>
                <Button x:Name="btn">
                    <Grid>
                        <Path Fill="#00FFFFFF" Stretch="None" Data="F1M16,16L0,16 0,0 16,0z"/>
                        <Path x:Name="icn" Fill="Red" Stretch="None" Data="F1M12,12L4,12 4,4 12,4z M3,13L13,13 13,3 3,3z"/>
                    </Grid>
                </Button>
                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Value="false">
                        <Setter TargetName="icn" Property="Fill" Value="White"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding Path=WindowState, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Value="Maximized">
                        <Setter TargetName="btn" Property="Background" Value="Blue"/>
                        <Setter TargetName="icn" Property="Data" Value="F1M11.999,10.002L10.998,10.002 10.998,5.002 5.998,5.002 5.998,4.001 11.999,4.001z M10.002,11.999L4.001,11.999 4.001,5.998 10.002,5.998z M5.002,3L5.002,5.002 3,5.002 3,13 10.998,13 10.998,10.998 13,10.998 13,3z"/>
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Button.Template>
    </Button>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.