2008-09-08 16 views
44

Tôi đang xây dựng một ứng dụng trong C# bằng cách sử dụng WPF. Làm thế nào tôi có thể liên kết với một số phím?Làm thế nào tôi có thể đăng ký một phím nóng toàn cầu để nói CTRL + SHIFT + (LETTER) sử dụng WPF và .NET 3.5?

Ngoài ra, làm cách nào để liên kết với Windows key?

+0

Từ chức năng Win32 ['RegisterHotKey'] (http://msdn.microsoft.com/en-us/library/ms646309.aspx):" Phím tắt liên quan đến phím WINDOWS được dành riêng để sử dụng bởi thao tác hệ thống. " –

+0

@IanKemp: Cho đến khi chúng được hệ điều hành sử dụng, bạn có thể sử dụng chúng trong ứng dụng của mình. Vâng, đó là lý do tại sao MS đã cho Win + N và Win + Shift + N để OneNote ;-) –

Trả lời

27

Tôi không chắc chắn ý nghĩa của bạn là "toàn cầu" ở đây, nhưng ở đây nó đi (tôi giả định bạn có nghĩa là một lệnh ở cấp ứng dụng, ví dụ: Lưu tất cả có thể được kích hoạt từ mọi nơi bởi Ctrl +phím Shift +S.)

bạn tìm ra toàn cầu UIElement của sự lựa chọn của bạn, ví dụ, cửa sổ cấp cao nhất đó là mẹ của tất cả các điều khiển mà bạn cần điều này ràng buộc. Do "bong bóng" của các sự kiện WPF, các sự kiện ở các phần tử con sẽ bong bóng tất cả các con đường lên đến gốc của cây điều khiển.

Bây giờ, trước tiên bạn cần

  1. để ràng buộc Key-Combo với một lệnh sử dụng một InputBinding như thế này
  2. sau đó bạn có thể hookup lệnh để xử lý của bạn (ví dụ mã mà được gọi bằng SaveAll) qua số CommandBinding.

Đối với của Windows Key, bạn sử dụng quyền Key thành viên liệt kê, Key.LWin hoặc 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. 
    } 
+0

Xem ở đây để biết thêm cách MVVM theo cách http://stackoverflow.com/questions/19697106/create-key-binding-in-wpf – anhoppe

+4

nó không thực sự toàn cầu (cấp ứng dụng). Nếu bạn mở cửa sổ mới từ MainWindow và nhấn phím nóng thì nó sẽ không hoạt động. – Dork

+5

Đây cũng không phải là "toàn cầu" từ quan điểm của Windows. Nếu ứng dụng của bạn không tập trung, điều này không hoạt động. – user99999991

1

Tôi không chắc chắn về WPF, nhưng điều này có thể hữu ích. Tôi đã sử dụng giải pháp được mô tả trong RegisterHotKey (user32) (được sửa đổi theo nhu cầu của tôi) cho ứng dụng C# Windows Forms để gán tổ hợp CTRL-KEY trong Windows để hiển thị biểu mẫu C# và nó hoạt động tốt (ngay cả trên Windows Vista). Tôi hy vọng nó sẽ giúp và may mắn!

+2

Điều này không làm việc với WPF. –

+0

Dường như không hoạt động với khóa Windows. Vâng, nó hỗ trợ phím Windows, nhưng bất kỳ phím nóng nào được dành riêng cho Windows. – erodewald

0

RegisterHotKey() đề xuất bởi John có thể hoạt động - điều duy nhất là yêu cầu HWND (sử dụng PresentationSource.FromVisual() và truyền kết quả đến HwndSource).

Tuy nhiên, bạn cũng sẽ cần trả lời thư WM_HOTKEY - Tôi không chắc chắn có cách nào để truy cập vào WndProc của cửa sổ WPF hay không (có thể được thực hiện cho Windows Forms windows) .

0

Một đồng nghiệp đã viết một mẫu về cách tạo ra một mức độ thấp bàn phím móc được sử dụng với WPF.

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

+4

Liên kết bị hỏng. Bạn có biết nó đã được di chuyển? –

+2

Google là bạn của bạn, bạn đã có các cụm từ tìm kiếm như là một phần của liên kết, do đó, bây giờ hãy nhấp vào đây ... http://blogs.vertigo.com/personal/ralph/Blog/Lists/Posts/Post.aspx ? ID = 8 – Gorgsenegger

+0

@Gorgsenegger Liên kết bị hỏng. –

17

Nếu bạn đang đi để trộn Win32 và WPF, dưới đây là cách tôi đã làm nó:

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

Bạn có thể lấy mã ảo-key cho phím nóng bạn muốn đăng ký ở đây: http://msdn.microsoft.com/en-us/library/ms927178.aspx

Có thể có cách tốt hơn, nhưng đây là những gì tôi đã có cho đến nay.

Chúc mừng!

+0

Tôi đoán bạn tạo ra một rò rỉ bộ nhớ ở đó ... –

+0

Làm thế nào để bạn tạo ra một rò rỉ bộ nhớ từ mã này? IDisposable của nó – user99999991

+0

'ComponentDispatcher.ThreadPreprocessMessage + = ComponentDispatcher_ThreadPreprocessMessage;' Dòng này nên được gọi từ UI Thread, nếu không bạn không nhận được tin nhắn. –

1

Mặc dù RegisterHotKey đôi khi chính xác những gì bạn muốn, trong hầu hết các trường hợp, có thể bạn không muốn sử dụng các phím nóng trên toàn hệ thống. Tôi đã kết thúc sử dụng mã như sau:

 
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"); 
      } 
     } 
    } 
} 
47

Đây là một giải pháp làm việc đầy đủ, hy vọng nó sẽ giúp ...

Cách sử dụng:

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

...

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

Lớp:

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 
    } 

    // ****************************************************************** 
} 
+0

Tôi biết đã lâu rồi, nhưng khi tôi đăng ký nút PrintScreen, phương thức "PrintScreen" thực sự ngừng hoạt động. "handling = true" cũng không hoạt động. Bất kỳ đề xuất? – ErwinOkken

+0

@ErwinOkken, tôi không chắc chắn có hiểu đúng không. HotKey là phiên toàn cầu cho bạn (~ toàn cầu đến máy của bạn). Nếu bạn đăng ký "PrintScreen", sau đó khi bạn nhấn nó, phím đó sẽ được xử lý trong chương trình của bạn. Đóng chương trình của bạn sẽ cho phép "PrintScreen" chạy như bình thường ~ (trước khi bạn chạy chương trình của bạn). Bạn có nghĩa là bạn không thể xử lý (đăng ký) "PrintScreen" trong chương trình của bạn? –

+0

Xin lỗi nếu tôi không rõ. Chức năng PrintScreen không nên mất tính năng gốc của nó: lưu trữ hình ảnh trong Clipboard. Khi hoặc sau đó xảy ra, tôi cần phải móc nó. – ErwinOkken

1

Tôi đã tìm thấy dự án Global Hotkeys in WPF trên codeproject.com thực hiện công việc cho tôi. Nó tương đối gần đây, không cần tham chiếu đến System.Windows.Forms và hoạt động "trên toàn cầu" về phản ứng với phím nóng được nhấn ngay cả khi ứng dụng "của bạn" không phải là cửa sổ hoạt động.

14

Đăng ký các phím tắt ở cấp độ hệ điều hành hầu như không bao giờ là điều tốt: người dùng không muốn bạn gây rối với hệ điều hành của họ.

Điều đó nói rằng, có một cách đơn giản và thân thiện với người dùng hơn trong WPF, nếu bạn chỉ sử dụng phím nóng trong ứng dụng (ví dụ: ứng dụng WPF của bạn có tiêu điểm):

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

Nó mà

+0

Bạn có cần hủy đăng ký theo cách thủ công không? – Joel

+0

Nó sẽ tự động bị xóa khi bạn đóng ứng dụng, nếu không thì bạn cần phải hủy đăng ký ứng dụng trong vòng đời của ứng dụng. –

+3

+1 để đơn giản và chỉ ra rằng các phím tắt cấp hệ điều hành là xấu. Đã đồng ý. – Joel

0

giải pháp đơn giản Baboon của hoạt động tốt nhất vì bạn có thể có nhiều cửa sổ. Tôi đã tinh chỉnh nó để nó sử dụng PreviewKeyDownEvent thay vì PreviewKeyUpEvent để xử lý sự lặp lại trong tổ hợp phím.

Tôi khuyên bạn nên đăng ký cấp hệ điều hành trừ khi bạn đang viết một cái gì đó như công cụ cắt hoặc ứng dụng ghi âm vì nó sẽ cho phép bạn truy cập chức năng khi cửa sổ không được lấy nét.

1

này cũng tương tự như câu trả lời đã được đưa ra, nhưng tôi thấy nó một chút bụi:

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

tôi đã tìm thấy nó trên fluxbytes.com.

Các vấn đề liên quan