Bind MenuItem Icon Color to MenuItem's Foreground

Viewed 702

Problem

I have a MenuItem with a control template that changes the MenuItem's Foreground like so:

<ControlTemplate TargetType="MenuItem">
    <StackPanel Background="{Binding Background}" Orientation="Horizontal">
        <ContentPresenter Content="{TemplateBinding Icon}"/>
        <TextBlock Text="{TemplateBinding Header}"/>
    </StackPanel>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="true">
            <Setter Property="Foreground" Value="Red"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

I want to bind the icon's color to this foreground color. The icon is just a circle in this case. I tried the following binding:

<MenuItem Header="Sub">
    <MenuItem.Icon>
        <Ellipse Width="16" Height="16" Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=MenuItem}}"/>
    </MenuItem.Icon>
</MenuItem>

However, this gives the following binding error on application start and the ellipse ends up not being rendered:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.MenuItem', AncestorLevel='1''. BindingExpression:Path=Foreground; DataItem=null; target element is 'Ellipse' (Name=''); target property is 'Fill' (type 'Brush')

I also tried to put a dummy element in the icon that gets the foreground propagated and bind to this:

<MenuItem.Icon>
    <Grid>
        <TextBlock Name="foregroundCapture"/>
        <Ellipse Width="16" Height="16" Fill="{Binding Foreground, ElementName=foregroundCapture}"/>
    </Grid>
</MenuItem.Icon>

But this gives a similar error:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=foregroundCapture'. BindingExpression:Path=Foreground; DataItem=null; target element is 'Ellipse' (Name=''); target property is 'Fill' (type 'Brush')

How can I make the binding work? FYI, the icon will be an object in a resource dictionary in the end. So it will not have access to the actual menu item.

Alternative Solutions

I can think of a couple of alternative solutions. But none of them are actually very nice:

  1. Create a subclass of MenuItem that has a MouseOverIcon property and have the trigger use it.
  2. Create custom controls for each icon, which have the Foreground property propagated. This would allow a direct binding directly within the control.

Full Example

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Style TargetType="MenuItem">
            <Style.Triggers>
                <Trigger Property="Role" Value="SubmenuItem">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="MenuItem">
                                <StackPanel Background="{Binding Background}" Orientation="Horizontal">
                                    <ContentPresenter Content="{TemplateBinding Icon}"/>
                                    <TextBlock Text="{TemplateBinding Header}"/>
                                </StackPanel>
                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsMouseOver" Value="true">
                                        <Setter Property="Foreground" Value="Red"/>
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>

            </Style.Triggers>
        </Style>
    </Window.Resources>
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="Header">
                <MenuItem Header="Sub">
                    <MenuItem.Icon>
                        <Grid>
                            <TextBlock Name="foregroundCapture"/>
                            <Ellipse Width="16" Height="16" Fill="{Binding Foreground, ElementName=foregroundCapture}"/>
                        </Grid>
                    </MenuItem.Icon>
                </MenuItem>
            </MenuItem>
        </Menu>

        <Grid>
        </Grid>
    </DockPanel>

</Window>
0 Answers
Related