C# - System.Windows.Forms.Keys - How to keep application hotkeys in one place

前端 未结 1 1722
南笙
南笙 2021-01-06 15:21

I have few \"hotkeys\" in my application. All \"hotkeys\" sequences are unique in application scope (so for example F12 key will always fire the same single task). In few pl

相关标签:
1条回答
  • 2021-01-06 15:51

    A complete solution to this would be to use some form of Command Management, where you define application-wide commands and (optionally) assign hot-keys to them.

    WPF has support for Command Management built-in, and you can roll out your own without too much effort.

    If you don't want to go down that route, you can create an application-wide filter for key messages, and work with those.

    Below is code for a KeyStore class. It's a singleton class that acts as a message filter for keys.

    /// <summary>
    /// The KeyStoreEventHandler is used by the KeyPress event of the KeyStore
    /// class. It notifies listeners of a named key press.
    /// </summary>
    /// <param name="name">The name of the key.</param>
    public delegate void KeyStoreEventHandler(string name);
    
    class KeyStore : IMessageFilter
    {
        // Interop
        [DllImport("user32.dll")]
        static extern short GetKeyState(Keys key);
    
        // Windows message constants
        private const int WM_KEYDOWN = 0x100;
        private const int WM_KEYUP = 0x101;
    
        // The singleton instance
        private static KeyStore s_instance = null;
    
        // The modifier keys
        private bool _shift = false;
        private bool _control = false;
    
        // The definitions
        private Dictionary<Keys, string> _definitions;
    
        // The KeyPressed Event
        public event KeyStoreEventHandler KeyPress;
    
        /// <summary>
        /// Adds a key definition to the store.
        /// </summary>
        /// <param name="name">The name of the key.</param>
        /// <param name="key">The key</param>
        /// <param name="modifiers">The modifiers (shift, control)</param>
        public void AddKeyDefinition(string name, Keys key, Keys modifiers)
        {
            Keys combined = key | modifiers;
    
            _definitions[combined] = name;
        }
    
        /// <summary>
        /// The filter message.
        /// </summary>
        public bool PreFilterMessage(ref Message m)
        {
            bool handled = false;
            Keys key = Keys.None;
    
            switch (m.Msg)
            {
                case WM_KEYUP:
                    key = (Keys)m.WParam;
                    handled = HandleModifier(key, false);
                    break;
    
                case WM_KEYDOWN:
                    key = (Keys)m.WParam;
                    handled = HandleModifier(key, true);
                    if (false == handled)
                    {
                        // If one of the defined keys was pressed then we
                        // raise an event.
                        handled = HandleDefinedKey(key);
                    }
                    break;
            }
    
            return handled;
        }
    
        /// <summary>
        /// Compares a key against the definitions, and raises an event
        /// if there is a match.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>True if the key was one of the defined key combinations.</returns>
        private bool HandleDefinedKey(Keys key)
        {
            bool handled = false;
    
            Keys combined = key;
            if (_shift) combined |= Keys.Shift;
            if (_control) combined |= Keys.Control;
    
            // If we have found a matching combination then we
            // raise an event.
            string name = null;
            if (true == _definitions.TryGetValue(combined, out name))
            {
                OnKeyPress(name);
    
                handled = true;
            }
            return handled;
        }
    
        /// <summary>
        /// Attempt to handle a modifier key, and return a boolean indicating if a modifier key was
        /// handled.
        /// </summary>
        /// <param name="key">The key</param>
        /// <param name="isDown">True if the key is pressed; False if it is released.</param>
        /// <returns>True if a modifier key was selected; False otherwise.</returns>
        private bool HandleModifier(Keys key, bool isDown)
        {
            bool handled = false;
    
            switch (key)
            {
                case Keys.RControlKey:
                case Keys.ControlKey:
                    _control = isDown;
                    handled = true;
                    break;
    
                case Keys.RShiftKey:
                case Keys.ShiftKey:
                    _shift = isDown;
                    handled = true;
                    break;
            }
    
            return handled;
        }
    
        /// <summary>
        /// Raises the KeyPress event.
        /// </summary>
        /// <param name="name">The name of the key.</param>
        private void OnKeyPress(string name)
        {
            // Raise event
            if (null != KeyPress) KeyPress(name);
    
            // Check if modifier keys were released in the mean time.
            _control =
                -127 == GetKeyState(Keys.ControlKey) ||
                -127 == GetKeyState(Keys.RControlKey);
    
            _shift =
                -127 == GetKeyState(Keys.ShiftKey) ||
                -127 == GetKeyState(Keys.RShiftKey);
    
        }
    
        /// <summary>
        /// Returns the singleton instance.
        /// </summary>
        public static KeyStore Instance
        {
            get
            {
                if (null == s_instance) 
                    s_instance = new KeyStore();
    
                return s_instance;
            }
        }
    
        // The constructor is private because this is a singleton class.
        private KeyStore()
        {
            _definitions = new Dictionary<Keys, string>();
        }
    }
    

    To use it, first assign the key filter to the Application class. In your Main() method (probably in the Program.cs file), add the following line before Application.Run():

    Application.AddMessageFilter(KeyStore.Instance);
    

    Make sure this line is inserted before Application.Run(). This registers the KeyStore as a key handler.

    Then, you can add keys to the KeyStore anywhere you want, for example in the Form_Load of your main form:

    KeyStore.Instance.AddKeyDefinition("CloseApp", Keys.F12, Keys.None);
    KeyStore.Instance.AddKeyDefinition("Save", Keys.S, Keys.Control);
    

    This registers two combinations: F12 and Control+S.

    Then, register an event handler so you can capture the CloseApp and Save key presses.

    KeyStore.Instance.KeyPress += new KeyStoreEventHandler(KeyStore_KeyPress);
    

    In my example code, I used MessageBox.Show() to prove that the event was fired:

    void KeyStore_KeyPress(string name)
    {
        MessageBox.Show(String.Format("Key '{0}' was pressed!", name));
    }
    

    You can register or unregister event handlers at will, when forms are opened and closed for example, or have application-wide handlers. It's up to you.

    Because the KeyStore is a singleton, you can add the key definitions from anywhere in your application. You can do it in the Main() method, before calling Application.Run(), or you can add some code to load definitions from a configuration file.

    0 讨论(0)
提交回复
热议问题