wpfxamldata-bindingwpf-controls

WPF XAML Menu Binding in Control Template


I have a customized menu with an ItemsSource bound to a ObservableCollection in my view model. To customize the menu, I'm using a ControlTemplate in the style and the ControlTemplate contains a label. I would like to show or hide this label depending on the IsDefault property in my MenuItem class.

Here's the simplified xaml:

<Menu x:Name="RewriteMenu" ItemsSource="{Binding RewriteMenuItems}"Margin="0,10,10,0" >
    <Menu.ItemContainerStyle>
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Header" Value="{Binding Name}" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type MenuItem}">
                        <Border x:Name="Border" Background="{TemplateBinding Background}"
                                    BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1"
                                    CornerRadius="6">
                            <Grid Margin="0,0,0,0" Background="{TemplateBinding Background}"
                                      VerticalAlignment="Center">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition x:Name="Col0" MinWidth="17" Width="Auto"
                                                          SharedSizeGroup="MenuItemIconColumnGroup" />
                                    <ColumnDefinition Width="Auto"
                                                          SharedSizeGroup="MenuTextColumnGroup" />
                                    <ColumnDefinition Width="Auto"
                                                          SharedSizeGroup="MenuItemIGTColumnGroup" />
                                    <ColumnDefinition x:Name="Col3" Width="150" />
                                    <ColumnDefinition x:Name="Col4" Width="Auto" />
                                </Grid.ColumnDefinitions>

                                <!--ContentPresenter to show an Icon if needed-->

                                <ContentPresenter Grid.Column="0" Margin="14,0,6,0" x:Name="Icon" VerticalAlignment="Center" ContentSource="Icon" Width="18" Height="18" />

                                <!--Content for the menu text etc-->
                                <TextBlock Height="27" FontFamily="{StaticResource PoppinsRegular}" VerticalAlignment="Center" Width="260" Grid.Column="1" x:Name="HeaderHost"
               Text="{TemplateBinding Header}" />

<Label Grid.Column="2" Style="{StaticResource DefaultVoiceStyle}" Background="#BFCBFF" Visibility="{Binding Content.IsDefault, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource InverseBoolToVisConverter}}"/>
                                <
        </Style>
    </Menu.ItemContainerStyle>
    <Menu.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Vertical" Width="340" />
        </ItemsPanelTemplate>
    </Menu.ItemsPanel>
</Menu>

I found this answer so I tried something like: <Label Visibility="{Binding Content.IsDefault, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource InverseBoolToVisConverter}}"/> but this binding only gets me to the MenuItem (the WPF version, not my custom class. Sorry, poor naming on my part) and I can't bind to the IsDefault property.

Is this possible? Is there a better way?

UDPATE

I tried to make a custom MenuItem that inherits from System.Windows.Controls.MenuItem:

public class VoiceRewriteMenuItem : MenuItem
{
    public bool IsDefault
    {
        get { return (bool)GetValue(IsDefaultProperty); }
        set { SetValue(IsDefaultProperty, value); }
    }

    // Using a DependencyProperty as the backing store for IsDefault.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsDefaultProperty =
        DependencyProperty.Register("IsDefault", typeof(bool), typeof(VoiceRewriteMenuItem), new PropertyMetadata(false));
}

This allows me to bind to my property but now the menu doesn't appear. Any thoughts?


Solution

  • I managed to find a work around. In System.Windows.Control.MenuItem there are a few boolean properties that I don't care about in this case. I bound the IsChecked property to my IsDefault property in my view model and set the label visibility to visible only if MenuItem.IsChecked is true. So it works like this:

    <Menu x:Name="RewriteMenu" ItemsSource="{Binding RewriteMenuItems}"Margin="0,10,10,0" >
        <Menu.ItemContainerStyle>
            <Style TargetType="{x:Type MenuItem}">
                <Setter Property="Header" Value="{Binding Name}" />
                <Setter Property="IsChecked" Value="{Binding IsDefault}"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type MenuItem}">
                            <Border x:Name="Border" Background="{TemplateBinding Background}"
                                        BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1"
                                        CornerRadius="6">
                                <Grid Margin="0,0,0,0" Background="{TemplateBinding Background}"
                                          VerticalAlignment="Center">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition x:Name="Col0" MinWidth="17" Width="Auto"
                                                              SharedSizeGroup="MenuItemIconColumnGroup" />
                                        <ColumnDefinition Width="Auto"
                                                              SharedSizeGroup="MenuTextColumnGroup" />
                                        <ColumnDefinition Width="Auto"
                                                              SharedSizeGroup="MenuItemIGTColumnGroup" />
                                        <ColumnDefinition x:Name="Col3" Width="150" />
                                        <ColumnDefinition x:Name="Col4" Width="Auto" />
                                    </Grid.ColumnDefinitions>
    
                                    <!--ContentPresenter to show an Icon if needed-->
    
                                    <ContentPresenter Grid.Column="0" Margin="14,0,6,0" x:Name="Icon" VerticalAlignment="Center" ContentSource="Icon" Width="18" Height="18" />
    
                                    <!--Content for the menu text etc-->
                                    <TextBlock Height="27" FontFamily="{StaticResource 
                                               PoppinsRegular}" 
                                               VerticalAlignment="Center" Width="260" 
                                               Grid.Column="1" x:Name="HeaderHost"
                                               Text="{TemplateBinding Header}" />
    
                                     <Label Grid.Column="2" Style="{StaticResource DefaultVoiceStyle}" Background="#BFCBFF" Visibility="{Binding Content.IsChecked, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisConverter}}"/>
                                    
            </Style>
        </Menu.ItemContainerStyle>
        <Menu.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Vertical" Width="340" />
            </ItemsPanelTemplate>
        </Menu.ItemsPanel>
    </Menu>
    

    It's not the elegant binding solution I was looking for but it works.