Como posso registrar uma tecla de atalho global para dizer CTRL + SHIFT + (LETTER) usando o WPF e o .NET 3.5?

Eu estou construindo uma aplicação em C # usando o WPF. Como posso ligar algumas teclas?

Além disso, como posso ligar a chave do Windows ?

Não tenho certeza do que você quer dizer com “global” aqui, mas aqui está (supondo que você queira dizer um comando no nível do aplicativo, por exemplo, Salvar tudo que pode ser acionado de qualquer lugar por Ctrl + Shift + S .)

Você encontra o UIElement global de sua escolha, por exemplo, a janela de nível superior que é o pai de todos os controles nos quais você precisa dessa binding. Devido a “bubbling” de events do WPF, os events nos elementos filhos serão preenchidos até a raiz da tree de controle.

Agora, primeiro você precisa

  1. ligar a combinação de teclas com um comando usando um InputBinding como este
  2. você pode então ligar o comando ao seu manipulador (por exemplo, código que é chamado por SaveAll ) por meio de um CommandBinding .

Para a chave do Windows , você usa o membro enumerado de chave direita, Key.LWin ou 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. } 

Esta é uma solução completa de trabalho, espero que ajude …

Uso:

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

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

Classe:

 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 _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 Action { get; private set; } public int Id { get; set; } // ****************************************************************** public HotKey(Key k, KeyModifier keyModifiers, Action 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(); 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 } // ****************************************************************** } 

Se você vai misturar Win32 e WPF, aqui está como eu fiz:

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

Você pode obter o código de tecla virtual da tecla de atalho que deseja registrar aqui: http://msdn.microsoft.com/pt-br/library/ms927178.aspx

Pode haver uma maneira melhor, mas é isso que eu tenho até agora.

Felicidades!

Registrar os atalhos do nível do sistema operacional dificilmente é uma coisa boa: os usuários não querem que você mexa com o sistema operacional.

Dito isso, há uma maneira muito mais simples e amigável de fazer isso no WPF, se você estiver bem com a tecla de atalho que funciona apenas no aplicativo (ou seja, contanto que seu aplicativo WPF tenha o foco):

Em 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 } 

É simples assim

Embora RegisterHotKey seja às vezes exatamente o que você quer, na maioria dos casos você provavelmente não quer usar teclas de atalho em todo o sistema. Acabei usando código como o seguinte:

 using System.Windows;
 using System.Windows.Interop;

 namespace WpfApp
 {
     class parcial pública MainWindow: Janela
     {
         const int WM_KEYUP = 0x0101;

         const int VK_RETURN = 0x0D;
         const int VK_LEFT = 0x25;  

         MainWindow público ()
         {
             this.InitializeComponent ();

             ComponentDispatcher.ThreadPreprocessMessage + = 
                 ComponentDispatcher_ThreadPreprocessMessage;
         }

         void ComponentDispatcher_ThreadPreprocessMessage (
             ref MSG msg, ref bool manipulado)
         {
             if (msg.message == WM_KEYUP)
             {
                 if ((int) msg.wParam == VK_RETURN)
                     MessageBox.Show ("RETURN foi pressionado");

                 if ((int) msg.wParam == VK_LEFT)
                     MessageBox.Show ("LEFT foi pressionado");
             }
         }
     }
 }

Não tenho certeza sobre o WPF, mas isso pode ajudar. Eu usei a solução descrita no RegisterHotKey (user32) (modificado para minhas necessidades, é claro) para um aplicativo Windows Forms C # atribuir uma combinação CTRL-KEY no Windows para abrir um formulário C # e funcionou perfeitamente (mesmo no Windows Vista) . Espero que ajude e boa sorte!

Eu encontrei o Global Hotkeys no projeto WPF no codeproject.com que faz o trabalho para mim. É relativamente recente, não precisa de uma referência a System.Windows.Forms e funciona “globalmente” em termos de reagir à tecla de atalho que está sendo pressionada, mesmo que “sua” aplicação não seja a janela ativa.

Isso é semelhante às respostas já dadas, mas acho um pouco mais claro:

 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. } } } 

Eu encontrei no fluxbytes.com .

RegisterHotKey() sugerido por John poderia funcionar – o único problema é que ele requer um HWND (usando PresentationSource.FromVisual() e convertendo o resultado em um HwndSource).

No entanto, você também precisará responder à mensagem WM_HOTKEY – não tenho certeza se existe uma maneira de obter access ao WndProc de uma janela do WPF ou não (o que pode ser feito para janelas do Windows Forms).

A solução do babuíno funciona melhor porque você pode ter várias janelas. Eu fiz tweak para que ele use o PreviewKeyDownEvent em vez do PreviewKeyUpEvent, a fim de lidar com a repetição nos pressionamentos de teclas.

Aconselho contra o registro no nível do sistema operacional, a menos que você esteja escrevendo algo como uma ferramenta de recorte ou um aplicativo de gravação de áudio, pois permitirá que você acesse a funcionalidade quando a janela não estiver focada.

Um colega de trabalho escreveu uma amostra sobre como criar um gancho de teclado de baixo nível para ser usado com o WPF.

http://blogs.vertigo.com/personal/ralph/Blog/Lists/Posts/Post.aspx?ID=8