简体   繁体   中英

WPF Show Menu in Keyboard Navigation Mode

Backstory aka My Configuration

Like many, I have an application which has a Menu docked to the top. I wanted this menu to be hidden until I pressed the alt key, where I'd then be able to navigate this menu either using my keyboard or by clicking my way through the MenuItems.

In my ViewModel, I made a boolean property called ShowMenu , an ICommand called ShowMenuCommand which operates as a flip flop for ShowMenu , and proceeded to wire things up with data binding. You can see that here:

ShowMenuCommand = new RelayCommand(
    _ => ShowMenu = !ShowMenu);

The Menu is set up as such:

<Menu Name="MainMenu" DockPanel.Dock="Top"
    Visibility="{Binding ShowMenu, Converter={StaticResource BoolToVis}}"
    LostFocus="MainMenu_OnLostFocus">

I also configured keybindings for left and right alt that fire the ShowMenuCommand .

<Window.InputBindings>
    <KeyBinding Key="F1" Command="{Binding AboutCommand}" />
    <KeyBinding Key="LeftAlt" Modifiers="Alt" Command="{Binding ShowMenuCommand}" />
    <KeyBinding Key="RightAlt" Modifiers="Alt" Command="{Binding ShowMenuCommand}" />
</Window.InputBindings>

This works exactly as I configured it to work: The menu is normally hidden, but when I press alt it appears and allows me to navigate the menu items until I click away or I select an item and it loses focus, the LostFocus handler setting ShowMenu to false.

The Problem

Having done this, I seem to have lost the ability to enter Keyboard Navigation Mode. For those who don't know what I mean, normally when you press alt in a gui app, certain characters become underlined, and you can press those characters on your keyboard to navigate the UI. I don't know the formal name for this, so bonus points to anyone who can provide the actual name.

Sources for getting this far

How to make WPF menu bar visible when ALT-key is pressed

LeftAlt Keybinding in WPF

What Now?

I've been searching high and low, crawling through keyboard classes and UIElement in hopes of finding something to fix this, and I've come up with a couple possible solutions. That said, the reason I have resorted to StackOverflow is because I don't know how to pose my google search in such a way that I find what I'm looking for.

My proposed solutions are as follows:

  1. Modify ShowMenuCommand to toggle keyboard navigation mode in addition to modifying visibility.
  2. Remove my keybindings and wire the menu's visibility to whether keyboard navigation mode is enabled.
    • I thought I would've had this with UIElement#IsInputMethodEnabled , however this does not appear to be the case. That said, I don't know if it matters what element you select, and I don't remember if I tried targeting the Menu or the Window.

If anyone else has any third idea or might know something I'm missing, please do let me know. Hiding the menu bar until you unhide it with alt should be something terribly trivial to set up, so I'd not be surprised if I'm missing something.

Additional Context

If you would like to see any code surrounding the provided snippets, you can view the project source code on GitHub.

So after some additional research and a some help from @Vlad, the final solution is the following:

  1. Menu visibility is bound to ShowMenu property in View Controller.
  2. Window has KeyUp event handler which watches for the alt key.
private void MainWindow_OnKeyUp(object sender, KeyEventArgs e)
{
    if (e.Key == Key.System && (e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt))
    {
        MainWindowViewModel mwvm = (MainWindowViewModel)DataContext;

        mwvm.ShowMenu = !mwvm.ShowMenu;
    }
}

This works, except the menu will remain visible after an item is selected. I tried creating an item template to take care of it all at once, but it didn't want to fire.

  1. Handle Click event on relevant menu items, skipping those that are just categories.

The only thing this doesn't take care of is when the user clicks away from the menu. I tried attaching a LostFocus event to the Menu, however this event fires when the menu opens, perhaps because the focus is being taken away from the Menu itself and onto the ContextMenu provider or whatever actually handles drawing the open menu.

I'll need to do some more research and some code cleanup, but the above solution works relatively okay.

Edit: I'll leave this answer unselected for a couple days just in case anyone else has any other ideas.

Edit 2: I found a solution for my use. Since I only have one top-level menu item on this menu, I hooked the MenuItem's SubmenuClosed event. When this happens (either because of a user selecting an option or them clicking away), it hides the menu by setting ShowMenu to false.

It's probably not the most elegant solution, but it's functional. I'll look into cleaning it up later.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM