0
votes

I'm learning WPF and developing a dynamic Menu which is driven by data binding of it's ItemsSource to an ObservableCollection. To do this I have a simple MenuItemViewModel and a HierarchicalDataTemplate for automatic binding of MenuItems to it.

The problem I have is that Command property doesn't work for top level menu items. Despite it is set, a MenuItem doesn't react on mouse click and doesn't get disabled if Command cannot be executed. Simply it's like it's not getting bound.

For lower level menu items though, it works as intended. I think this should be a problem of my HierarchicalDataTemplate but I can't find it, because as I see there's no code in template which might affect command binding of only top-level MenuItems.

MenuItemViewModel implements INotifyPropertyChanged and contains following public properties:

string Text
Uri ImageSource
ICommand Command
ObservableCollection<MenuItemViewModel> Children

HierarchicalDataTemplate for MenuItem in my Window.Resources is as follows:

<HierarchicalDataTemplate DataType="{x:Type common:MenuItemViewModel}"
                          ItemsSource="{Binding Path=Children}">

    <HierarchicalDataTemplate.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Command"
                Value="{Binding Command}" />
        </Style>
    </HierarchicalDataTemplate.ItemContainerStyle>

    <StackPanel Orientation="Horizontal">
        <Image Source="{Binding ImageSource}" />
        <TextBlock Text="{Binding Text}" VerticalAlignment="Center"/>
    </StackPanel>

</HierarchicalDataTemplate>

Can you please point me on my mistake?

EDIT: Top-level MenuItem doesn't contain any children (i.e. Children collection of associated ViewModel is empty).

1
I think you should also write code for ItemContainerStyle of your ItemControl or something similar. I'm not sure, but if you'r implementing HierarchicalDataTemplate.ItemContainerStyle, then this will apply only on any MenuItem inside your containers. I mean, why should your style apply on first layer, if you'r saying: To any item of type menuItem, that will be in menuItem(container), add custom style.sTrenat
@sTrenat so that's how it works... You've nudged on the right thoughts. Thank you!1valdis

1 Answers

0
votes

Thanks to @sTrenat comment I've come to solution below.

<Menu.Resources>
    <!-- cancel sharing of image so Icon will work properly -->
    <Image x:Key="MenuIcon" Source="{Binding ImageSource}" x:Shared="False"/>
</Menu.Resources>

<Menu.ItemContainerStyle>
    <Style TargetType="{x:Type MenuItem}"
           BasedOn="{StaticResource {x:Type MenuItem}}">
        <Setter Property="Header" Value="{Binding Text}" />
        <Setter Property="Icon" Value="{StaticResource MenuIcon}"/>
        <Setter Property="Command" Value="{Binding Command}"/>
        <Setter Property="ItemsSource" Value="{Binding Children}" />

        <!-- centering MenuItem's Header -->
        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate>
                    <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding}" />
                </DataTemplate>
            </Setter.Value>
        </Setter>

        <!-- setting Icon to null when ImageSource isn't specified -->
        <Style.Triggers>
            <DataTrigger Binding="{Binding ImageSource}"
                        Value="{x:Null}">
                <Setter Property="Icon" Value="{x:Null}"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Menu.ItemContainerStyle>