#c# #winforms #keyboard #keyevent
#c# #winforms #клавиатура #keyevent
Вопрос:
У меня в приложении несколько «горячих клавиш». Все последовательности «горячих клавиш» уникальны в области применения (так, например, клавиша F12 всегда запускает одну и ту же задачу). В нескольких местах они обрабатываются так, как здесь:
if (e.Modifiers == Keys.Shift amp;amp; e.KeyCode == Keys.Delete)
{
this.Close();
}
if (e.Modifiers == Keys.Shift amp;amp; 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.Модификаторы == Ключи.Shift amp;amp; e.keyCode == Keys.Up
Все советы по хранению всех определений «горячих клавиш» приложения в одном месте приветствуются 🙂 Итак, однажды, если мне понадобится изменить Keys.F12 на Keys.F10 Я смогу просто отредактировать его в одном месте вместо инструмента поиска и замены..
Ответ №1:
Полным решением этого было бы использовать некоторую форму управления командами, где вы определяете команды всего приложения и (необязательно) назначаете им горячие клавиши.
WPF имеет встроенную поддержку управления командами, и вы можете развернуть свою собственную без особых усилий.
Если вы не хотите идти по этому пути, вы можете создать фильтр для ключевых сообщений в масштабах всего приложения и работать с ними.
Ниже приведен код для класса хранилища ключей. Это одноэлементный класс, который действует как фильтр сообщений для ключей.
/// <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(). Это регистрирует хранилище ключей в качестве обработчика ключей.
Затем вы можете добавить ключи в хранилище ключей в любом месте, где захотите, например, в Form_Load вашей основной формы:
KeyStore.Instance.AddKeyDefinition("CloseApp", Keys.F12, Keys.None);
KeyStore.Instance.AddKeyDefinition("Save", Keys.S, Keys.Control);
Здесь регистрируются две комбинации: F12 и Control S.
Затем зарегистрируйте обработчик событий, чтобы вы могли фиксировать закрытие приложения и сохранять нажатия клавиш.
KeyStore.Instance.KeyPress = new KeyStoreEventHandler(KeyStore_KeyPress);
В моем примере кода я использовал MessageBox.Показать(), чтобы доказать, что событие было запущено:
void KeyStore_KeyPress(string name)
{
MessageBox.Show(String.Format("Key '{0}' was pressed!", name));
}
Вы можете регистрировать или отменять регистрацию обработчиков событий по своему усмотрению, например, когда формы открываются и закрываются или имеют обработчики для всего приложения. Это зависит от вас.
Поскольку хранилище ключей является одноэлементным, вы можете добавлять определения ключей из любого места вашего приложения. Вы можете сделать это в методе Main (), перед вызовом Application.Run(), или вы можете добавить некоторый код для загрузки определений из файла конфигурации.
Комментарии:
1. Спасибо Edwin за такое комплексное решение! Приветствую!