none
Is there anyway to bind a multi-key sequence to a RoutedUICommand? For example "Ctrl-B, T".

    Question

  • Is there any native WPF support for creating an input binding that is a sequence of multiple keys?  For example "Ctrl-B, T"  is key sequence in VS2005 to toggle a bookmark. How would I create a binding like that in WPF?

    Below is an example from my code, the Ctrl-L and Ctrl-H act as two different key gestures that do the same thing. I'd like to be able to type "Ctrl-L, Ctrl-H" instead.

    Thanks, Rich

    public static RoutedUICommand CellResizeShowHiddenCmd =

    new RoutedUICommand("_Show Hidden", "CellRezieShowHidden", typeof(MainWindow),

    new InputGestureCollection(

    new KeyGesture[] {

    new KeyGesture(Key.L, ModifierKeys.Control, "Ctrl-L"),

    new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl-H") }));

     

    Here's some partial help from VS2005 on KeyGesture for quick lookup:

    ms_help://MS.VSCC.v80/MS.MSDN.v80/MS.NETFX30SDK4VS.1033/cpref33/html/T_System_Windows_Input_KeyGesture.htm

    XAML Attribute Usage
    <object property="Key"/>
    - or -
    <object property="oneOrMoreModifierKeys"/>
    - or -
    <object property="Key+oneOrMoreModifierKeys"/>
    

    XAML Values

    Key

    Key

    A single key. For more information, see the Key property.

    oneOrMoreModifierKeys

    One or more modifier keys, defined by the ModifierKeys enumeration, delimited with a "+" character. For more information, see the Modifiers property.

    Monday, March 26, 2007 3:58 AM

Answers

  • I got the idea for a solution to this reading the following article. My source is below. If you see a problem please post :)

    http://www.eggheadcafe.com/forumarchives/windowsdeveloperwinfxavalon/dec2005/post24766129.asp 

    // Simple Chorded Key Gesture
    
        public class MultiKeyGesture : InputGesture
        {
            KeyGesture[] m_keys;
            int m_checkIdx = 0;
    
    
            public MultiKeyGesture(KeyGesture[] keys)
            {
                m_keys = keys;
            }
    
            public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
            {
                if (!(inputEventArgs is KeyEventArgs))
                {
                    return false; // only supporting keystroke combos
                }
    
                KeyEventArgs keyArgs = (KeyEventArgs)inputEventArgs;
    
                if (keyArgs.IsRepeat) // Holding down Ctrl keeps firing Matches with IsRepeat true, let's eat'em
                {
                    Console.WriteLine("Key ({0}) is repeat, ignoring", keyArgs.Key);
                    return false;
                }
    
                Console.WriteLine("Checking Key ({0}) against index ({1})", keyArgs.Key, m_checkIdx);
    
                if (m_keys[m_checkIdx].Matches(null, inputEventArgs))
                {
                    m_checkIdx++;
                    Console.WriteLine("** Advanced check index to ({0})", m_checkIdx);
                }
                else
                {
                    Console.WriteLine("** No Match resetting");
                    m_checkIdx = 0;
                }
    
                if (m_checkIdx == m_keys.Length)
                {
                    m_checkIdx = 0;
                    inputEventArgs.Handled = true;
                    Console.WriteLine("** Found a match!");
                    return true;
                }
    
                return false;
            }
        }
    
    
    
    // Usage
    
            public static RoutedUICommand CellResizeShowHiddenCmd = new RoutedUICommand("_Show Hidden", "CellRezieShowHidden", typeof(MainWindow),
                new InputGestureCollection(
                new MultiKeyGesture[] {
                    new MultiKeyGesture(new KeyGesture[] {
                        new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl-H"), new KeyGesture(Key.L, ModifierKeys.Control, "Ctrl-L")
                    })
                }
                )
                );
    
    
    Monday, March 26, 2007 7:33 AM
  • Here's an ugly hack to get the menu strings working again:

    // inherit from a dummy KeyGesture to get menu strings working again, not sure of the consequeces of using this hack!
    // Menu seem to want a KeyGesture object to make that magic work
    
           public class MultiKeyGesture : KeyGesture 
           {
        
            KeyGesture[] m_keys;
            int m_checkIdx = 0;
    
            public MultiKeyGesture(KeyGesture[] keys, string displayString) : base(Key.Oem1, ModifierKeys.None, displayString)
            {
                m_keys = keys;
                m_displayString = displayString;
            }
    
            private string m_displayString;
            public string DisplayString
            {
                get
                {
                    return m_displayString;
                }
                set
                {
                    m_displayString = value;
                }
            }
    
    
        // ... the rest is the same As Above
        }
    
    
    // Go crazy with 3 keys ! as stated in the above link, the ModifierKey is still mandetory
    
            public static RoutedUICommand CellResizeShowHiddenCmd = new RoutedUICommand("_Show Hidden", "CellRezieShowHidden", typeof(MainWindow),
                new InputGestureCollection(
                new MultiKeyGesture[] {
                    new MultiKeyGesture(new KeyGesture[] {
                        new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl-H"), new KeyGesture(Key.L, ModifierKeys.Control, "Ctrl-L"), new KeyGesture(Key.Y, ModifierKeys.Control, "Ctrl-Y")
                    }, "Ctrl-H, Ctrl-L, Ctrl-Y")
                }
                )
                );
            
    
    
    Monday, March 26, 2007 8:06 AM
  • The code below fixes some issues from earlier versions in this post, specifically the need to reset state! Please post insights if you see problems.
        public class MultiKeyGesture : KeyGesture
        {
            static List s_allGestures = new List();
    
            static void ResetAllGestures()
            {
                foreach (MultiKeyGesture gesture in s_allGestures)
                {
                    gesture.m_checkIdx = 0;
                }
            }
    
            KeyGesture[] m_keys;
            int m_checkIdx = 0;
    
            static string CreateDisplayStringFromKeyGestures(KeyGesture[] keys)
            {
                StringBuilder displayString = new StringBuilder();
                foreach (KeyGesture key in keys)
                {
                    displayString.AppendFormat("{0}, ", key.DisplayString);
                }
    
                displayString.Remove(displayString.Length - 2, 2);
                return displayString.ToString();
            }
            
            public MultiKeyGesture(KeyGesture[] keys, string displayString)
                : base(Key.Oem1, ModifierKeys.None, displayString)
            {
                m_keys = keys;
                s_allGestures.Add(this);
            }
    
            public MultiKeyGesture(KeyGesture[] keys)
                : base(Key.Oem1, ModifierKeys.None, CreateDisplayStringFromKeyGestures(keys))
            {
                m_keys = keys;
                s_allGestures.Add(this);
            }
    
    
            
            public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
            {
                if (!(inputEventArgs is KeyEventArgs))
                {
                    return false; // only supporting keystroke combos
                }
    
                KeyEventArgs keyArgs = (KeyEventArgs)inputEventArgs;
    
                if (keyArgs.IsRepeat) // Holding down Ctrl keeps firing Matches with IsRepeat true, let's eat'em
                {
                    return false;
                }
    
                Console.WriteLine("Checking Key ({0}) against index ({1}) for sequence ({2})", keyArgs.Key, m_checkIdx, DisplayString);
    
                if (m_keys[m_checkIdx].Matches(null, inputEventArgs))
                {
                    m_checkIdx++;
                    Console.WriteLine("** Matched! Advanced check index to ({0}) for ({1})", m_checkIdx,DisplayString);
                }
                else
                {
                    Console.WriteLine("** No Match resetting for ({0})",DisplayString);
                    m_checkIdx = 0;
                }
    
                if (m_checkIdx == m_keys.Length)
                {
                    m_checkIdx = 0;
                    ResetAllGestures();
                    inputEventArgs.Handled = true;
                    Console.WriteLine("** Found a match!");
                    return true;
                }
    
                return false;
            }
        }
    
    Monday, March 26, 2007 1:03 PM

All replies

  • I got the idea for a solution to this reading the following article. My source is below. If you see a problem please post :)

    http://www.eggheadcafe.com/forumarchives/windowsdeveloperwinfxavalon/dec2005/post24766129.asp 

    // Simple Chorded Key Gesture
    
        public class MultiKeyGesture : InputGesture
        {
            KeyGesture[] m_keys;
            int m_checkIdx = 0;
    
    
            public MultiKeyGesture(KeyGesture[] keys)
            {
                m_keys = keys;
            }
    
            public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
            {
                if (!(inputEventArgs is KeyEventArgs))
                {
                    return false; // only supporting keystroke combos
                }
    
                KeyEventArgs keyArgs = (KeyEventArgs)inputEventArgs;
    
                if (keyArgs.IsRepeat) // Holding down Ctrl keeps firing Matches with IsRepeat true, let's eat'em
                {
                    Console.WriteLine("Key ({0}) is repeat, ignoring", keyArgs.Key);
                    return false;
                }
    
                Console.WriteLine("Checking Key ({0}) against index ({1})", keyArgs.Key, m_checkIdx);
    
                if (m_keys[m_checkIdx].Matches(null, inputEventArgs))
                {
                    m_checkIdx++;
                    Console.WriteLine("** Advanced check index to ({0})", m_checkIdx);
                }
                else
                {
                    Console.WriteLine("** No Match resetting");
                    m_checkIdx = 0;
                }
    
                if (m_checkIdx == m_keys.Length)
                {
                    m_checkIdx = 0;
                    inputEventArgs.Handled = true;
                    Console.WriteLine("** Found a match!");
                    return true;
                }
    
                return false;
            }
        }
    
    
    
    // Usage
    
            public static RoutedUICommand CellResizeShowHiddenCmd = new RoutedUICommand("_Show Hidden", "CellRezieShowHidden", typeof(MainWindow),
                new InputGestureCollection(
                new MultiKeyGesture[] {
                    new MultiKeyGesture(new KeyGesture[] {
                        new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl-H"), new KeyGesture(Key.L, ModifierKeys.Control, "Ctrl-L")
                    })
                }
                )
                );
    
    
    Monday, March 26, 2007 7:33 AM
  • Here's an ugly hack to get the menu strings working again:

    // inherit from a dummy KeyGesture to get menu strings working again, not sure of the consequeces of using this hack!
    // Menu seem to want a KeyGesture object to make that magic work
    
           public class MultiKeyGesture : KeyGesture 
           {
        
            KeyGesture[] m_keys;
            int m_checkIdx = 0;
    
            public MultiKeyGesture(KeyGesture[] keys, string displayString) : base(Key.Oem1, ModifierKeys.None, displayString)
            {
                m_keys = keys;
                m_displayString = displayString;
            }
    
            private string m_displayString;
            public string DisplayString
            {
                get
                {
                    return m_displayString;
                }
                set
                {
                    m_displayString = value;
                }
            }
    
    
        // ... the rest is the same As Above
        }
    
    
    // Go crazy with 3 keys ! as stated in the above link, the ModifierKey is still mandetory
    
            public static RoutedUICommand CellResizeShowHiddenCmd = new RoutedUICommand("_Show Hidden", "CellRezieShowHidden", typeof(MainWindow),
                new InputGestureCollection(
                new MultiKeyGesture[] {
                    new MultiKeyGesture(new KeyGesture[] {
                        new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl-H"), new KeyGesture(Key.L, ModifierKeys.Control, "Ctrl-L"), new KeyGesture(Key.Y, ModifierKeys.Control, "Ctrl-Y")
                    }, "Ctrl-H, Ctrl-L, Ctrl-Y")
                }
                )
                );
            
    
    
    Monday, March 26, 2007 8:06 AM
  • The code below fixes some issues from earlier versions in this post, specifically the need to reset state! Please post insights if you see problems.
        public class MultiKeyGesture : KeyGesture
        {
            static List s_allGestures = new List();
    
            static void ResetAllGestures()
            {
                foreach (MultiKeyGesture gesture in s_allGestures)
                {
                    gesture.m_checkIdx = 0;
                }
            }
    
            KeyGesture[] m_keys;
            int m_checkIdx = 0;
    
            static string CreateDisplayStringFromKeyGestures(KeyGesture[] keys)
            {
                StringBuilder displayString = new StringBuilder();
                foreach (KeyGesture key in keys)
                {
                    displayString.AppendFormat("{0}, ", key.DisplayString);
                }
    
                displayString.Remove(displayString.Length - 2, 2);
                return displayString.ToString();
            }
            
            public MultiKeyGesture(KeyGesture[] keys, string displayString)
                : base(Key.Oem1, ModifierKeys.None, displayString)
            {
                m_keys = keys;
                s_allGestures.Add(this);
            }
    
            public MultiKeyGesture(KeyGesture[] keys)
                : base(Key.Oem1, ModifierKeys.None, CreateDisplayStringFromKeyGestures(keys))
            {
                m_keys = keys;
                s_allGestures.Add(this);
            }
    
    
            
            public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
            {
                if (!(inputEventArgs is KeyEventArgs))
                {
                    return false; // only supporting keystroke combos
                }
    
                KeyEventArgs keyArgs = (KeyEventArgs)inputEventArgs;
    
                if (keyArgs.IsRepeat) // Holding down Ctrl keeps firing Matches with IsRepeat true, let's eat'em
                {
                    return false;
                }
    
                Console.WriteLine("Checking Key ({0}) against index ({1}) for sequence ({2})", keyArgs.Key, m_checkIdx, DisplayString);
    
                if (m_keys[m_checkIdx].Matches(null, inputEventArgs))
                {
                    m_checkIdx++;
                    Console.WriteLine("** Matched! Advanced check index to ({0}) for ({1})", m_checkIdx,DisplayString);
                }
                else
                {
                    Console.WriteLine("** No Match resetting for ({0})",DisplayString);
                    m_checkIdx = 0;
                }
    
                if (m_checkIdx == m_keys.Length)
                {
                    m_checkIdx = 0;
                    ResetAllGestures();
                    inputEventArgs.Handled = true;
                    Console.WriteLine("** Found a match!");
                    return true;
                }
    
                return false;
            }
        }
    
    Monday, March 26, 2007 1:03 PM

  •    <KeyBinding x:Name="mykeybinding" Gesture="CTRL+P" Key="E" 
                     Command="mycommand"/>

    That seems to do trick at my end, I have have to press ctrl+P+E to execute "mycommand"

    Based on http://msdn.microsoft.com/en-in/library/system.windows.input.keybinding.aspx

    Tuesday, April 16, 2013 8:49 AM
  • This won't work, my command is invoked on press CTRL+E as well.
    Monday, June 18, 2018 9:52 PM