0

I am attempting to construct a dynamic WPF menu with different appearance for the top level items and the subsequent level items. In particular, I would like my top level items to display a 32 pixel square image on top of the menu caption, and the subsequent items to display a 16 pixel image in the regular Icon location. (I posted a related question some days ago, that one was using templates instead of styles).

The XAML code below produces a menu with the following characteristics:

. All menu items bind to and display the 32 pixel icon, leading me to believe that only the second ItemContainerStyle is being used (the one not in the Resources section).

. The Command and CommandParameter properties bind correctly; when I click a menu item the command is executed.

. The Name property is not bound correctly, all Headers are empty strings. No binding errors are generated for this property in the Output window.

. The template information given in the Grid (which is supposed to apply to the top-level items) is never rendered.

Based on this example I believe that part of the problem may be that I have to move my Resources higher in the XAML tree, to the WrapPanel. When I do this, however, the StaticResources cannot be located in the menu.

How can I modify my menu to apply different styles (or templates -- I'm not picky!) to the top-level items and the other items?

<WrapPanel Height="Auto">
    <Menu ItemsSource="{Binding DataContext.EventMenu.TopLevel, ElementName=UserControl}">
        <Menu.Resources>
            <Image x:Key="MenuIconResource16" Height="16" Width="16" Source="{Binding Icon16}" x:Shared="False" />
            <Image x:Key="MenuIconResource32" Height="32" Width="32" Source="{Binding Icon32}" x:Shared="False" />
            <HierarchicalDataTemplate x:Key="SubItemStyle" ItemsSource="{Binding Children}">
                <HierarchicalDataTemplate.ItemContainerStyle>
                    <Style TargetType="{x:Type MenuItem}">
                        <Setter Property="IsEnabled" Value="true"/>
                        <Setter Property="Command" Value="{Binding Command}" />
                        <Setter Property="CommandParameter" Value="{Binding EventType}"/>
                        <Setter Property="Header" Value="{Binding Name}"/>
                        <Setter Property="Icon" Value="{StaticResource MenuIconResource16}"/>
                        <!--<Setter Property="ItemsSource" Value="{Binding Children}"/>-->
                    </Style>
                </HierarchicalDataTemplate.ItemContainerStyle>
            </HierarchicalDataTemplate>
        </Menu.Resources>
        <Menu.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
            <HierarchicalDataTemplate.ItemContainerStyle>
                <Style TargetType="{x:Type MenuItem}">
                    <Setter Property="IsEnabled" Value="true"/>
                    <Setter Property="Command" Value="{Binding Command}" />
                    <Setter Property="CommandParameter" Value="{Binding EventType}"/>
                    <Setter Property="Header" Value="{Binding Name}"/>
                    <Setter Property="Icon" Value="{StaticResource MenuIconResource32}"/>
                    <!--<Setter Property="ItemsSource" Value="{Binding Children}"/>-->
                    <Setter Property="ItemTemplate" Value="{StaticResource SubItemStyle}"></Setter>
                </Style>
            </HierarchicalDataTemplate.ItemContainerStyle>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Image Grid.Row="0" Width="32" Height="32" VerticalAlignment="Center" Source="{Binding Icon32}"/>
                <TextBlock Grid.Row="1" Text="{Binding Name}"/>
            </Grid>
        </HierarchicalDataTemplate>
        </Menu.ItemTemplate>
    </Menu>
</WrapPanel>
Community
  • 1
  • 1
Larry Lustig
  • 49,320
  • 14
  • 110
  • 160

1 Answers1

3

You seem to have confused the ItemContainerStyle, which is a Style with a DataTemplate that could be used in the ItemTemplate property. The former is the Style for the container, or in your case the MenuItem, whereas the actual data items should be defined with a DataTemplate.

You could achieve your requirements just using DataTemplates and a DataTemplateSelector, ignoring the ItemContainerStyle for now. This method would require you to declare one DataTemplate for the normal items and another for each variation that you want. You would then use the DataTemplateSelector.SelectTemplate method to decide which DataTemplate to apply to each MenuItem.

This example is taken from the linked page on MSDN and demonstrates how you can use the DataTemplateSelector:

public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
    FrameworkElement element = container as FrameworkElement;

    if (element != null && item != null && item is Task)
    {
        Task taskitem = item as Task;

        if (taskitem.Priority == 1)
            return
                element.FindResource("importantTaskTemplate") as DataTemplate;
        else 
            return
                element.FindResource("myTaskTemplate") as DataTemplate;
    }

    return null;
}
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Thanks, Sheridan. This code goes in the code-behind, correct? Anyway to accomplish it without code-behind (which is frowned on by this client)? Also, where do I have to define those templates in order for them to be found by `FindResource()`? – Larry Lustig Dec 18 '14 at 17:12
  • And yes -- I'm quite confused between Styles and Templates in general. I tried reading http://msdn.microsoft.com/en-us/library/cc295273.aspx but it pre-supposes more knowledge of WPF than I have. Any recommendation for a "Styles and Templates for WPF-Dummies" greatly appreciated. – Larry Lustig Dec 18 '14 at 17:20
  • *This code goes in the code-behind, correct?*... nope, it goes in it's own class that extends the `DataTemplateSelector` class... see the linked page for further help. You can use the [`Application.FindResource` Method](http://msdn.microsoft.com/en-us/library/system.windows.application.findresource(v=vs.110).aspx) and put your `DataTemplate`s in the `App.xaml Resources` section. – Sheridan Dec 18 '14 at 17:27
  • @LarryLustig You can declare the data template selector as a resource in your XAML http://msdn.microsoft.com/en-us/library/system.windows.controls.datatemplateselector%28v=vs.110%29.aspx – Dean Kuga Dec 18 '14 at 17:28
  • Try looking at the [Styling and Templating](http://msdn.microsoft.com/en-us/library/ms745683(v=vs.110).aspx) and maybe [Control Authoring Overview](http://msdn.microsoft.com/en-us/library/ms745025(v=vs.110).aspx) pages on MSDN for some background info. – Sheridan Dec 18 '14 at 17:28
  • Thank you. I did get this working, although I'm not certain why one specific part (setting the Icon property's style) works correctly. That question (and my working code) can be seen here: http://stackoverflow.com/questions/27746792/why-does-my-menuitem-have-an-icon-when-i-have-overridden-the-datatemplate – Larry Lustig Jan 02 '15 at 18:32