Peter
Peter

Reputation: 14098

Keyboard navigation no longer works in WPF menu with templates

In a WPF menu that is data bound to a collection, I can style everything correctly, but navigation with the keyboard no longer works as expected.

Consider the XAML below (you can paste it in a tool like KaXaml).

Two things:

How can I ensure the 'default' menu behavior when navigating with the keys?

This is the XAML you can test it with:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid>  
    <Menu VerticalAlignment="Top">
      <MenuItem Header="One">
        <MenuItem Header="A">
          <MenuItem Header="I" />
          <MenuItem Header="II" />
          <MenuItem Header="III" />
        </MenuItem>
        <MenuItem Header="B"/>
        <MenuItem Header="C"/>
      </MenuItem>

      <MenuItem Header="Two">
        <MenuItem.ItemContainerStyle>
            <Style TargetType="MenuItem">
              <Setter Property="Template">
                <Setter.Value>
                  <ControlTemplate>
                    <MenuItem>
                      <MenuItem.HeaderTemplate>
                        <DataTemplate>
                          <TextBlock>
                              <Run Text="Bound property" />
                          </TextBlock>
                        </DataTemplate>
                      </MenuItem.HeaderTemplate>
                      <MenuItem Header="Something"/>
                      <MenuItem Header="Something else"/>
                    </MenuItem>
                  </ControlTemplate>
                </Setter.Value>
              </Setter>
            </Style>
          </MenuItem.ItemContainerStyle>
        <MenuItem Header="A"/>
        <MenuItem Header="B"/>
        <MenuItem Header="C"/>
      </MenuItem>

      <MenuItem Header="Three">
        <MenuItem Header="A">
          <MenuItem Header="I" />
          <MenuItem Header="II" />
          <MenuItem Header="III" />
        </MenuItem>
        <MenuItem Header="B"/>
        <MenuItem Header="C"/>
      </MenuItem>

    </Menu>
  </Grid>
</Page>

Update

The special thing is that the sub MenuItems in my ControlTemplate should be added for each DataBound MenuItem. This is because I have an ObservableCollection with which I will build up the MenuItems inside MenuItem "Two". For each of these MenuItems, I need the same sub MenuItems. They will be bound to a Command that is the same for each of the MenuItems, except for the CommandParameter.

So what I want in the end is:

Two
    Bound property 1
        Something
        Something else
    Bound property 2
        Something
        Something else
    Bound property 3
        Something
        Something else

Upvotes: 1

Views: 722

Answers (2)

pushpraj
pushpraj

Reputation: 13669

I did restyle the menu item Two while keeping your desired needs

    <MenuItem Header="Two">
        <MenuItem.ItemContainerStyle>
            <Style TargetType="MenuItem">
                <Setter Property="Header"
                        Value="Bound property" />
                <Setter Property="ItemsSource">
                    <Setter.Value>
                        <!--binding sub menu items to a collection-->
                        <x:ArrayExtension Type="sys:String"
                                          xmlns:sys="clr-namespace:System;assembly=mscorlib">
                            <sys:String>Something</sys:String>
                            <sys:String>Something else</sys:String>
                        </x:ArrayExtension>
                    </Setter.Value>
                </Setter>
            </Style>
        </MenuItem.ItemContainerStyle>
        <MenuItem />
        <MenuItem />
        <MenuItem />
    </MenuItem>

example above solves the navigation issue while keeping the binding to child items as desired. give it a try and see how close it is.

Upvotes: 1

filhit
filhit

Reputation: 2154

You should not create MenuItem elements inside the template of your MenuItem. You should style your items so they have the bound content.

I've added XML as a source for your menu items, so "Two" item has content bound to XML (you can replace it with your collection). For this solution you should have a collection for your submenu items (one for all top items, not per a single top item). The keyboard navigation works correct in the example below.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Page.Resources>
        <XmlDataProvider x:Key="MenuProvider" XPath="Items">
            <x:XData>
                <Items xmlns="">
                    <Item Title="Bound property 1" Parameter="1" />
                    <Item Title="Bound property 2" Parameter="2" />
                    <Item Title="Bound property 3" Parameter="3" />
                </Items>
            </x:XData>
        </XmlDataProvider>
        <XmlDataProvider x:Key="SubMenuProvider" XPath="Items">
            <x:XData>
                <Items xmlns="">
                    <Item Title="Something" />
                    <Item Title="Something else" />
                </Items>
            </x:XData>
        </XmlDataProvider>
    </Page.Resources>
    <Grid>
        <Menu VerticalAlignment="Top">
            <MenuItem Header="One">
                <MenuItem Header="A">
                    <MenuItem Header="I" />
                    <MenuItem Header="II" />
                    <MenuItem Header="III" />
                </MenuItem>
                <MenuItem Header="B" />
                <MenuItem Header="C" />
            </MenuItem>

            <MenuItem Header="Two" ItemsSource="{Binding Source={StaticResource MenuProvider}, XPath=*}">
                <MenuItem.ItemContainerStyle>
                    <Style TargetType="MenuItem">
                        <Setter Property="Header" Value="{Binding XPath=@Title}" />
                        <Setter Property="ItemsSource" Value="{Binding Source={StaticResource SubMenuProvider}, XPath=*}" />
                        <Setter Property="Tag" Value="{Binding XPath=@Parameter}" />
                        <Setter Property="ItemContainerStyle">
                            <Setter.Value>
                                <Style TargetType="MenuItem">
                                    <Setter Property="Header" Value="{Binding XPath=@Title}" />
                                    <Setter Property="CommandParameter" Value="{Binding Path=Tag, RelativeSource={RelativeSource AncestorType=MenuItem}}" />
                                </Style>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </MenuItem.ItemContainerStyle>
            </MenuItem>

            <MenuItem Header="Three">
                <MenuItem Header="A">
                    <MenuItem Header="I" />
                    <MenuItem Header="II" />
                    <MenuItem Header="III" />
                </MenuItem>
                <MenuItem Header="B" />
                <MenuItem Header="C" />
            </MenuItem>
        </Menu>
    </Grid>
</Page>

Upvotes: 2

Related Questions