Kairan
Kairan

Reputation: 5542

How can I programmatically find a list of all Shortcut keys assigned in C# Winform?

How can I programmatically get a list of shortcut keys that are used in my app? I have a winform with many menus and menu items (including dynamically generated menu items) having assigned shortcut keys.

Really having a hard time keeping track of all the shortcut keys and thought it would be nice to display a list to the user.

Upvotes: 0

Views: 526

Answers (1)

Harald Coppoolse
Harald Coppoolse

Reputation: 30502

I see several problems in your requirement:

  • For File-New you have to type <alt-F> <alt-N>; for Edit-Names you need to type <Alt-E> <Alt-N>
  • Therefore <Alt-N> is not enough in your shortcut collection, You need the full combination: <alt-F> <alt-N> for menu File-New, and <Alt-E> <Alt-N> for Edit-Names
  • What if the designer forgot to add the <alt-F> in the file part and the <alt-E> in the edit part? Then you would have two <alt-N> shortcuts!
  • Short-cuts also work on other controls, like Buttons
  • What about the standard shortcuts like <Ctrl-C> and <Ctrl-V>?

Let's assume your shortcuts are unique, you only want shortcuts for menu items and you want them from the root menu. Alas, class System.Windows.Forms.Shortcut doesn't have values for <Alt-..> shortcuts, so we'll just remember the shortcuts as uppercase characters: so "FN" means <alt-F> <alt-N>"

You need to iterate each menu item with all sub-menu items, and check if the Text property contains an ampersand.

I'll do this as extension methods for MenuStrip and ToolStripItem. A menu strip has the main menu's as ToolStripItems in property Items; a ToolStripItem has the sub menu's in property DropDownItems.

If you are not familiar with extension methods see extension methods demystified

First the class to put the results:

using System.Windows.Forms;
class ShortCut
{
    public string ShortcutSequence {get; set;} = String.Empty;
    ToolStripItem ToolStripItem {get; set;}
}

Get the ShortCut of a ToolStripMenuItem or Default if it has no shortcut:

public static char GetShortcutCharOrDefault(this ToolStripMenuItem menuItem)
{
    // Return the character after the ampersand in property Text,
    // or null if there is no such thing
    return menuItem?.Text?.SkipWhile(c => c != '&') // skip until &
        .Skip(1)                                    // skip the &
        .Select(c => char.ToUpper(c))               // make the char uppercase
        .FirstOrDefault()                           // char after the ampersand
        ?? default(char);                           // or default char if no text
}

From a sequence of ToolStripMenuItems to a sequence of Shortcuts.

public static IEnumerable<Shortcut> ToShortcuts(
    this IEnumerable<ToolStripMenuItem> menuItems,
    string parentShortcutSequence)
{
    // TODO: handle invalid input

    foreach (var menuItem in menuItems)
    {
        char shortcutCar = menuItem.GetShortcutChar();
        if (shortCutChar != default(char))
        {
           // This menuItem has a shortcut.
           var shortcut = new ShortCut
           {
               ShortcutSequence = parentShortcutSequence + shortcutChar,
               ToolstripItem = menuItem,
           }

           // return this shortcut
           yield return shortcut;

           // and check for the shortcuts of the sub menu items
           // recursively!
           var subShortcuts = menuItem.Items.ToShortcuts(shortcut.ShortcutSequence);
           foreach (var subShortcut in subShortcuts)
           {
               yield return subShortCut);
           }
        }
    }
}

Usage:

MenuStrip mainMenuStrip = ...
IEnumerable<ToolStripItem> menus = mainMenuStrip.Items.Cast<ToolStripItem>();
IEnumerable<Shortcut> shortcuts = menus.ToShortCusts(String.Empty);

Upvotes: 1

Related Questions