开发者

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

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 places there are handled like here:

        if (e.Modifiers == Keys.Shift && e.KeyCode == Keys.Delete)
        {
             this.Close();
        }

        if (e.Modifiers == Keys.Shift && e.KeyCode == Keys.Up)
        {
            if (Program.PerformLogin())
            {
                frmConfigurationSetup frmSetup = new frmConfigurationSetup();
                frmSetup.Show();
            }
        }

        if (e.KeyCode == Keys.F12)
        {
            frmAbout formAbout = new frmAbout();
            formAbout.Show();
        }

But I have idea to keep all keyboard shortcuts used in my app in one place. I was thinking to put them in Constants.cs file:

    public const System.Windows.Forms.Keys ShowAboutForm = System.Windows.Forms.Keys.F12;

But how to achieve this in case of this sequence: e.Modifiers == Keys.Shift && e.KeyCode == Keys.Up

All advices how to store all application "hotkeys" definitions in one place are welco开发者_C百科me :) So one day if I need to change Keys.F12 to Keys.F10 I will be able to just edit it in one place instead of search&replace tool..


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜