2008-09-08 5 views
44

저는 WPF를 사용하여 C#에서 응용 프로그램을 작성하고 있습니다. 일부 키에 어떻게 바인딩 할 수 있습니까?WPF 및 .NET 3.5를 사용하여 CTRL + SHIFT + (LETTER)를 말하도록 전역 단축키를 등록하는 방법은 무엇입니까?

또한 어떻게 Windows key에 바인딩 할 수 있습니까?

+0

Win32 ['RegisterHotKey' 기능] (http://msdn.microsoft.com/en-us/library/ms646309.aspx) 문서에서 "WINDOWS 키와 관련된 바로 가기 키는 운영을 위해 사용하기 위해 예약되어 있습니다 체계." –

+0

@IanKemp : OS에서 사용하기 전까지는 응용 프로그램을 사용할 수 있습니다. 그렇습니다. MS가 Win + N 및 Win + Shift + N을 OneNote에 제공 한 이유입니다. –

답변

27

여기서 "전역"으로 무엇을 의미하는지 확신 할 수 없지만 여기서는 응용 프로그램 수준에서 명령을 의미한다고 가정합니다 (예 : 모두 저장). Ctrl 키 + 시프트 + S.)

에 의해 당신은 예를 들어, 당신의 선택의 글로벌 UIElement,이 바인딩을 필요로하는 모든 컨트롤의 부모 최상위 창을 찾을 수 있습니다. WPF 이벤트의 "버블 링 (bubbling)"으로 인해 자식 요소의 이벤트가 컨트롤 트리의 루트까지 끊어지지 않습니다.

지금, 당신이 먼저 (SaveAll에 의해 호출되는 예를 들어 코드) 당신이 당신의 처리기에 명령을 연결 시설 다음 수있는이

  • 같은 InputBinding 사용하여 명령을 사용하여 키 콤보를 결합하는
    1. 필요 CommandBinding을 통해 윈도우 키에 대한

    , 당신은 바로 Key 열거 멤버, Key.LWin 또는 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

    더 많은 MVVM 스타일의 방법을 보려면 http://stackoverflow.com/questions/19697106/create-key-binding-in-wpf – anhoppe

    +4

    을 참조하십시오. 실제로는 글로벌 (응용 프로그램 수준)이 아닙니다. MainWindow에서 새 창을 열고 거기서 단축키를 누르면 작동하지 않습니다. – Dork

    +5

    또한 이것은 Windows 관점에서 "전역"이 아닙니다. 앱에 초점이 맞지 않으면이 기능이 작동하지 않습니다. – user99999991

    1

    WPF에 대해 잘 모르겠지만 도움이 될 수 있습니다. Windows에서 C# 양식을 가져 오려면 CTRL-KEY 조합을 할당하는 C# Windows Forms 응용 프로그램의 경우 RegisterHotKey (user32) (내 필요에 맞게 수정 됨)에 설명 된 솔루션을 사용하고 Windows Vista에서도 아름답게 작동했습니다. 나는 그것이 도움이되고 행운이 있기를 바란다!

    +2

    이것은 WPF에서 작동하지 않습니다. –

    +0

    Windows 키가 전혀 작동하지 않는 것 같습니다. 음, Windows 키를 지원하지만 단축키는 Windows 자체 용으로 예약되어 있습니다. – erodewald

    0

    RegisterHotKey() John이 제안한 유일한 해결책은 HWND (PresentationSource.FromVisual()을 사용하고 결과를 HwndSource로 캐스팅)가 필요하다는 것입니다.

    WM_HOTKEY 메시지에 응답해야합니다. WPF 창의 WndProc에 액세스 할 수있는 방법이 있는지 잘 모르겠습니다 (Windows Forms 창에서 수행 할 수 있음). .

    0

    공동 작업자가 낮은 수준을 생성하는 방법에 대한 샘플을 썼다 사용 WPF와 함께 사용할 키보드 후크 당신은 Win32 및 WPF를 혼합 위하여려고하는 경우에

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

    +4

    링크가 끊어졌습니다. 기사가 옮겨 졌다는 것을 알고 있습니까? –

    +2

    Google은 귀하의 친구입니다. 링크의 일부로 검색어가 있으므로 여기를 클릭하십시오. http://blogs.vertigo.com/personal/ralph/Blog/Lists/Posts/Post.aspx ? ID = 8 – Gorgsenegger

    +0

    @Gorgsenegger 링크가 끊어졌습니다. –

    17

    가 여기 어떻게 내가 해냈어 :

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

    당신은 여기에 등록하고자하는 바로 가기 키의 가상 키 코드를 얻을 수 있습니다 : http://msdn.microsoft.com/en-us/library/ms927178.aspx

    더 좋은 방법이있을 수 있지만, 지금까지 내가 가진 것입니다.

    건배!

    +0

    거기에 메모리 누수가있는 것 같아요. –

    +0

    이 코드에서 어떻게 메모리 누수가 발생합니까? 그것의 IDisposable – user99999991

    +0

    'ComponentDispatcher.ThreadPreprocessMessage + = ComponentDispatcher_ThreadPreprocessMessage;'이 줄은 UI Thread에서 호출되어야합니다. 그렇지 않으면 메시지를받지 못합니다. –

    1

    RegisterHotKey는 때로 정확하게 원하는 것일 수 있지만 대부분의 경우 시스템 전체 단축키를 사용하지 않으려 고합니다. 나는 다음과 같은 코드를 사용하여 종료 ...

     
    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

    이 전체 작업 솔루션입니다, 그것은 도움이되기를 바랍니다

    사용법 :

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

    ...

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

    클래스 :

    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

    인쇄용 화면 버튼을 등록 할 때 실제 "PrintScreen"방법이 작동하지 않습니다. "handles = true"도 작동하지 않았습니다. 어떤 제안? – ErwinOkken

    +0

    @ErwinOkken, 제대로 이해할 수 없네요. 단축키는 당신에게 글로벌 (당신의 컴퓨터에 ~ 전역) 세션입니다. "PrintScreen"을 등록하면이 키를 누를 때 해당 키가 프로그램에서 처리되어야합니다. 프로그램을 종료하면 "PrintScreen"을 ~로 실행해야합니다 (프로그램을 실행하기 전에). 프로그램에서 "PrintScreen"을 처리 (등록) 할 수 없다는 뜻입니까? –

    +0

    내가 불분명하면 죄송합니다. PrintScreen 기능은 이미지를 클립 보드에 저장하는 원래의 기능을 잃어서는 안됩니다. 그런 일이 생기면 그걸 연결해야합니다. – ErwinOkken

    1

    나를 위해 일하는 codeproject.com에서 Global Hotkeys in WPF 프로젝트를 발견했습니다. 상대적으로 최근에 System.Windows.Forms에 대한 참조가 필요하지 않으며 "응용 프로그램"이 활성 창이 아니더라도 단축키에 대한 응답으로 "전역 적"으로 작동합니다.

    14

    OS 레벨 바로 가기를 등록하는 것은 결코 쉬운 일이 아닙니다. 사용자는 자신의 OS를 망치는 것을 원하지 않습니다. 당신이 단축키 만 (즉, 한 당신의 WPF 응용 프로그램에 포커스가로) 응용 프로그램 내에서 작업하는 확인 인 경우
    말했다

    는 WPF에서이 일을 훨씬 더 간단하고 사용자 친화적 인 방법이있다 :

    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 
    } 
    

    그것은 간단

    +0

    수동으로 등록을 취소해야합니까? – Joel

    +0

    앱을 닫으면 자동으로 제거됩니다. 그렇지 않으면 앱 수명 기간 내에 등록을 취소해야합니다. –

    +3

    +1은 간단하고 OS 레벨 단축키가 잘못되었음을 나타냅니다. 동의했다. – Joel

    0

    원숭이의 솔루션이 가장 잘 작동합니다. 나는 키 스트로크에서 반복을 처리하기 위해 PreviewKeyUpEvent 대신 PreviewKeyDownEvent를 사용하기 때문에 그것을 조정했다.

    당신이 snipping tool이나 audio recording app과 같은 것을 쓰지 않는 한 OS 레벨의 등록에 반대 할 것을 권한다. 왜냐하면 창이 집중되어 있지 않을 때 기능에 접근 할 수있게 해주기 때문이다.

    1

    이 이미 주어진 해답과 유사하지만, 나는 그것을 조금 청소기 찾을 : 나는 fluxbytes.com에 그것을 발견했습니다

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

    합니다.