2

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.

Foxtrek_64
  • 357
  • 5
  • 14
  • Whether menu should be shown or not, is not a responsibility of the VM. It’s a pure UI logic. – Vlad Apr 20 '19 at 16:57
  • 1
    What if you just subscribe on PreviewKeyDown and toggle the menu visibility there? – Vlad Apr 20 '19 at 17:00
  • Using `PreviewKeyDown` and `KeyDown` creates odd behavior where sometimes it doesn't toggle correctly. Pulling out the data binding methodology, I "hard wired" it through events (still toggling `ShowMenu`) and it seems to work, except it doesn't auto-hide after an item is selected within the menu. – Foxtrek_64 Apr 20 '19 at 17:19
  • Perhaps you should set focus on menu when showing it and hide the menu when focus lost (including subitems, of course) – Vlad Apr 20 '19 at 17:27

1 Answers1

1

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.

Foxtrek_64
  • 357
  • 5
  • 14