c# .net wpf windows hotkeys

c# - ¿Cómo puedo registrar una tecla rápida global para decir CTRL+MAYÚS+(LETRA) usando WPF y.NET 3.5?



windows hotkeys (11)

Estoy construyendo una aplicación en C # usando WPF. ¿Cómo puedo enlazar algunas teclas?

Además, ¿cómo puedo vincularme a la clave de Windows ?


Aunque RegisterHotKey a veces es precisamente lo que desea, en la mayoría de los casos probablemente no quiera utilizar teclas de acceso directo en todo el sistema. Terminé usando código como el siguiente:

using System.Windows; using System.Windows.Interop; namespace WpfApp { public partial class MainWindow : Window { const int WM_KEYUP = 0x0101; const int VK_RETURN = 0x0D; const int VK_LEFT = 0x25; public MainWindow() { this.InitializeComponent(); ComponentDispatcher.ThreadPreprocessMessage += ComponentDispatcher_ThreadPreprocessMessage; } void ComponentDispatcher_ThreadPreprocessMessage( ref MSG msg, ref bool handled) { if (msg.message == WM_KEYUP) { if ((int)msg.wParam == VK_RETURN) MessageBox.Show("RETURN was pressed"); if ((int)msg.wParam == VK_LEFT) MessageBox.Show("LEFT was pressed"); } } } }


El registro de accesos directos a nivel del sistema operativo casi nunca es algo bueno: los usuarios no quieren que te metas con su sistema operativo.

Dicho esto, hay una manera mucho más simple y fácil de usar para hacer esto en WPF, si estás de acuerdo con que la tecla de acceso directo solo funcione en la aplicación (es decir, siempre que tu aplicación WPF tenga el foco):

En App.xaml.cs:

protected override void OnStartup(StartupEventArgs e) { EventManager.RegisterClassHandler(typeof(Window), Window.PreviewKeyUpEvent, new KeyEventHandler(OnWindowKeyUp)); } private void OnWindowKeyUp(object source, KeyEventArgs e)) { //Do whatever you like with e.Key and Keyboard.Modifiers }

Es así de simple


Encontré Global Hotkeys en el proyecto de WPF en codeproject.com, que hace el trabajo por mí. Es relativamente reciente, no necesita una referencia a System.Windows.Forms y funciona "globalmente" en términos de reacción a la tecla presionada incluso si "tu" aplicación no es la ventana activa.


Esta es una solución de trabajo completa, espero que ayude ...

Uso:

_hotKey = new HotKey(Key.F9, KeyModifier.Shift | KeyModifier.Win, OnHotKeyHandler);

...

private void OnHotKeyHandler(HotKey hotKey) { SystemHelper.SetScreenSaverRunning(); }

Clase:

using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.Mime; using System.Runtime.InteropServices; using System.Text; using System.Windows; using System.Windows.Input; using System.Windows.Interop; namespace UnManaged { public class HotKey : IDisposable { private static Dictionary<int, HotKey> _dictHotKeyToCalBackProc; [DllImport("user32.dll")] private static extern bool RegisterHotKey(IntPtr hWnd, int id, UInt32 fsModifiers, UInt32 vlc); [DllImport("user32.dll")] private static extern bool UnregisterHotKey(IntPtr hWnd, int id); public const int WmHotKey = 0x0312; private bool _disposed = false; public Key Key { get; private set; } public KeyModifier KeyModifiers { get; private set; } public Action<HotKey> Action { get; private set; } public int Id { get; set; } // ****************************************************************** public HotKey(Key k, KeyModifier keyModifiers, Action<HotKey> action, bool register = true) { Key = k; KeyModifiers = keyModifiers; Action = action; if (register) { Register(); } } // ****************************************************************** public bool Register() { int virtualKeyCode = KeyInterop.VirtualKeyFromKey(Key); Id = virtualKeyCode + ((int)KeyModifiers * 0x10000); bool result = RegisterHotKey(IntPtr.Zero, Id, (UInt32)KeyModifiers, (UInt32)virtualKeyCode); if (_dictHotKeyToCalBackProc == null) { _dictHotKeyToCalBackProc = new Dictionary<int, HotKey>(); ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage); } _dictHotKeyToCalBackProc.Add(Id, this); Debug.Print(result.ToString() + ", " + Id + ", " + virtualKeyCode); return result; } // ****************************************************************** public void Unregister() { HotKey hotKey; if (_dictHotKeyToCalBackProc.TryGetValue(Id, out hotKey)) { UnregisterHotKey(IntPtr.Zero, Id); } } // ****************************************************************** private static void ComponentDispatcherThreadFilterMessage(ref MSG msg, ref bool handled) { if (!handled) { if (msg.message == WmHotKey) { HotKey hotKey; if (_dictHotKeyToCalBackProc.TryGetValue((int)msg.wParam, out hotKey)) { if (hotKey.Action != null) { hotKey.Action.Invoke(hotKey); } handled = true; } } } } // ****************************************************************** // Implement IDisposable. // Do not make this method virtual. // A derived class should not be able to override this method. public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } // ****************************************************************** // Dispose(bool disposing) executes in two distinct scenarios. // If disposing equals true, the method has been called directly // or indirectly by a user''s code. Managed and unmanaged resources // can be _disposed. // If disposing equals false, the method has been called by the // runtime from inside the finalizer and you should not reference // other objects. Only unmanaged resources can be _disposed. protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this._disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. Unregister(); } // Note disposing has been done. _disposed = true; } } } // ****************************************************************** [Flags] public enum KeyModifier { None = 0x0000, Alt = 0x0001, Ctrl = 0x0002, NoRepeat = 0x4000, Shift = 0x0004, Win = 0x0008 } // ****************************************************************** }


Esto es similar a las respuestas ya dadas, pero me parece un poco más limpio:

using System; using System.Windows.Forms; namespace GlobalHotkeyExampleForm { public partial class ExampleForm : Form { [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk); [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern bool UnregisterHotKey(IntPtr hWnd, int id); enum KeyModifier { None = 0, Alt = 1, Control = 2, Shift = 4, WinKey = 8 } public ExampleForm() { InitializeComponent(); int id = 0; // The id of the hotkey. RegisterHotKey(this.Handle, id, (int)KeyModifier.Shift, Keys.A.GetHashCode()); // Register Shift + A as global hotkey. } protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == 0x0312) { /* Note that the three lines below are not needed if you only want to register one hotkey. * The below lines are useful in case you want to register multiple keys, which you can use a switch with the id as argument, or if you want to know which key/modifier was pressed for some particular reason. */ Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF); // The key of the hotkey that was pressed. KeyModifier modifier = (KeyModifier)((int)m.LParam & 0xFFFF); // The modifier of the hotkey that was pressed. int id = m.WParam.ToInt32(); // The id of the hotkey that was pressed. MessageBox.Show("Hotkey has been pressed!"); // do something } } private void ExampleForm_FormClosing(object sender, FormClosingEventArgs e) { UnregisterHotKey(this.Handle, 0); // Unregister hotkey with id 0 before closing the form. You might want to call this more than once with different id values if you are planning to register more than one hotkey. } } }

Lo encontré en fluxbytes.com .


La solución de Baboon funciona mejor porque puede tener varias ventanas. Lo modifiqué para que use PreviewKeyDownEvent en lugar de PreviewKeyUpEvent para manejar la repetición con las teclas.

Aconsejaría que no se registre a nivel de sistema operativo a menos que esté escribiendo algo así como una herramienta de recorte o una aplicación de grabación de audio, ya que le permitirá acceder a la funcionalidad cuando la ventana no esté enfocada.


No estoy seguro acerca de WPF, pero esto puede ayudar. Usé la solución descrita en RegisterHotKey (user32) (modificada a mis necesidades, por supuesto) para una aplicación C # Windows Forms para asignar una combinación CTRL-KEY dentro de Windows para mostrar un formulario C #, y funcionó maravillosamente (incluso en Windows Vista) . ¡Espero que ayude y buena suerte!


No estoy seguro de a qué te refieres con "global" aquí, pero aquí va (supongo que te refieres a un comando en el nivel de aplicación, por ejemplo, Save All que puede activarse desde cualquier lugar mediante Ctrl + Shift + S .)

Encontrará el UIElement global de su elección, por ejemplo, la ventana de nivel superior que es el padre de todos los controles donde necesita este enlace. Debido al "burbujeo" de los eventos WPF, los eventos en los elementos secundarios burbujearán hasta la raíz del árbol de control.

Ahora, primero necesitas

  1. vincular Key-Combo con un Comando usando un InputBinding como este
  2. a continuación, puede conectar el comando a su controlador (por ejemplo, código que recibe una llamada de SaveAll ) a través de un CommandBinding .

Para la clave de Windows , utiliza el miembro enumerado Key correcto, Key.LWin o Key.RWin

public WindowMain() { InitializeComponent(); // Bind Key InputBinding ib = new InputBinding( MyAppCommands.SaveAll, new KeyGesture(Key.S, ModifierKeys.Shift | ModifierKeys.Control)); this.InputBindings.Add(ib); // Bind handler CommandBinding cb = new CommandBinding( MyAppCommands.SaveAll); cb.Executed += new ExecutedRoutedEventHandler( HandlerThatSavesEverthing ); this.CommandBindings.Add (cb ); } private void HandlerThatSavesEverthing (object obSender, ExecutedRoutedEventArgs e) { // Do the Save All thing here. }


Si vas a mezclar Win32 y WPF, así es como lo hice:

using System; using System.Runtime.InteropServices; using System.Windows.Interop; using System.Windows.Media; using System.Threading; using System.Windows; using System.Windows.Input; namespace GlobalKeyboardHook { public class KeyboardHandler : IDisposable { public const int WM_HOTKEY = 0x0312; public const int VIRTUALKEYCODE_FOR_CAPS_LOCK = 0x14; [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool UnregisterHotKey(IntPtr hWnd, int id); private readonly Window _mainWindow; WindowInteropHelper _host; public KeyboardHandler(Window mainWindow) { _mainWindow = mainWindow; _host = new WindowInteropHelper(_mainWindow); SetupHotKey(_host.Handle); ComponentDispatcher.ThreadPreprocessMessage += ComponentDispatcher_ThreadPreprocessMessage; } void ComponentDispatcher_ThreadPreprocessMessage(ref MSG msg, ref bool handled) { if (msg.message == WM_HOTKEY) { //Handle hot key kere } } private void SetupHotKey(IntPtr handle) { RegisterHotKey(handle, GetType().GetHashCode(), 0, VIRTUALKEYCODE_FOR_CAPS_LOCK); } public void Dispose() { UnregisterHotKey(_host.Handle, GetType().GetHashCode()); } } }

Puede obtener el código de clave virtual para la tecla rápida que desea registrar aquí: http://msdn.microsoft.com/en-us/library/ms927178.aspx

Puede haber una mejor manera, pero esto es lo que tengo hasta ahora.

¡Aclamaciones!



RegisterHotKey() sugerido por John podría funcionar: la única pega es que requiere un HWND (usando PresentationSource.FromVisual() y lanzando el resultado a un HwndSource).

Sin embargo, también deberá responder al mensaje WM_HOTKEY ; no estoy seguro de si hay una manera de obtener acceso al WndProc de una ventana WPF o no (lo que se puede hacer para las ventanas de Windows Forms).