C # - System.Windows.Forms.Keys - Как сохранить горячие клавиши приложений в одном месте - PullRequest
3 голосов
/ 21 мая 2011

У меня есть несколько "горячих клавиш" в моем приложении.Все последовательности «горячих клавиш» уникальны в области приложения (поэтому, например, клавиша F12 всегда запускает одну и ту же задачу).В некоторых местах они обрабатываются как здесь:

        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();
        }

Но у меня есть идея сохранить все сочетания клавиш, используемые в моем приложении, в одном месте.Я думал поместить их в файл Constants.cs:

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

Но как этого добиться в случае такой последовательности: e.Modifiers == Keys.Shift && e.KeyCode == Keys.Up

Все советы, как хранить все определения "горячих клавиш" приложения в одном месте, приветствуются :) Так что однажды, если мне понадобится изменить Keys.F12 на Keys.F10, я смогу просто отредактировать его в одном месте.инструмента поиска и замены ..

1 Ответ

4 голосов
/ 21 мая 2011

Полное решение этой проблемы заключается в использовании некоторой формы управления командами, в которой вы определяете команды всего приложения и (необязательно) назначаете им горячие клавиши.

WPF имеет встроенную поддержку управления командами, и вы можете развернуть свою собственную без особых усилий.

Если вы не хотите идти по этому пути, вы можете создать в приложении фильтр ключевых сообщений и работать с ними.

Ниже приведен код для класса KeyStore. Это одноэлементный класс, который действует как фильтр сообщений для ключей.

/// <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>();
    }
}

Чтобы использовать его, сначала назначьте ключевой фильтр классу Application. В вашем методе Main () (возможно, в файле Program.cs) добавьте следующую строку перед Application.Run ():

Application.AddMessageFilter(KeyStore.Instance);

Убедитесь, что эта строка вставлена ​​перед Application.Run (). Это регистрирует KeyStore как обработчик ключа.

Затем вы можете добавить ключи в хранилище ключей в любом месте, например, в Form_Load вашей основной формы:

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

Здесь регистрируются две комбинации: F12 и Control + S.

Затем зарегистрируйте обработчик событий, чтобы вы могли захватывать нажатия клавиш CloseApp и Save.

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

В моем примере кода я использовал MessageBox.Show (), чтобы доказать, что событие было запущено:

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

Вы можете регистрировать или отменять регистрацию обработчиков событий по желанию, например, когда формы открываются и закрываются, или имеют обработчики для всего приложения. Это зависит от вас.

Поскольку KeyStore является одноэлементным, вы можете добавлять определения ключей из любой точки вашего приложения. Вы можете сделать это в методе Main () перед вызовом Application.Run () или добавить код для загрузки определений из файла конфигурации.

...