Mouse wheel event to work with hovered control - c#

In my C# 3.5 Windows Forms application, I have a few SplitContainers. There is a list control inside each (dock fill). When the focus is on one of these controls and I move mouse wheel, the list, which is now focused, is scrolled.
My task is to scroll the list, which is currently hovered by mouse, not the one which is selected. Is it possible in Windows Forms? If not, is it possible with PInvoke?

It looks like you can use the IMessageFilter and PInvoke to handle this. An example in VB can be found at Redirect Mouse Wheel Events to Unfocused Windows Forms Controls. You should be able to easily convert this to C#.
Points of Interest
This class uses the following techniques for the given task:
Listen to the control's MouseEnter and MouseLeave events to determine when the mouse pointer is over the control.
Implement IMessageFilter to catch WM_MOUSEWHEEL messages in the application.
PInvoke the Windows API call SendMessage redirecting the WM_MOUSEWHEEL message to the control's handle.
The IMessageFilter object is implemented as a singleton of the MouseWheelRedirector class and accessed by the shared members Attach, Detach, and Active.
Using a VB.NET to C# converter, this is what you end up with:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public class MouseWheelRedirector : IMessageFilter
{
private static MouseWheelRedirector instance = null;
private static bool _active = false;
public static bool Active
{
get { return _active; }
set
{
if (_active != value)
{
_active = value;
if (_active)
{
if (instance == null)
{
instance = new MouseWheelRedirector();
}
Application.AddMessageFilter(instance);
}
else
{
if (instance != null)
{
Application.RemoveMessageFilter(instance);
}
}
}
}
}
public static void Attach(Control control)
{
if (!_active)
Active = true;
control.MouseEnter += instance.ControlMouseEnter;
control.MouseLeave += instance.ControlMouseLeaveOrDisposed;
control.Disposed += instance.ControlMouseLeaveOrDisposed;
}
public static void Detach(Control control)
{
if (instance == null)
return;
control.MouseEnter -= instance.ControlMouseEnter;
control.MouseLeave -= instance.ControlMouseLeaveOrDisposed;
control.Disposed -= instance.ControlMouseLeaveOrDisposed;
if (object.ReferenceEquals(instance.currentControl, control))
instance.currentControl = null;
}
private MouseWheelRedirector()
{
}
private Control currentControl;
private void ControlMouseEnter(object sender, System.EventArgs e)
{
var control = (Control)sender;
if (!control.Focused)
{
currentControl = control;
}
else
{
currentControl = null;
}
}
private void ControlMouseLeaveOrDisposed(object sender, System.EventArgs e)
{
if (object.ReferenceEquals(currentControl, sender))
{
currentControl = null;
}
}
private const int WM_MOUSEWHEEL = 0x20a;
public bool PreFilterMessage(ref System.Windows.Forms.Message m)
{
if (currentControl != null && m.Msg == WM_MOUSEWHEEL)
{
SendMessage(currentControl.Handle, m.Msg, m.WParam, m.LParam);
return true;
}
else
{
return false;
}
}
[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr SendMessage(
IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
}

I had similar question and found this thread... so posting my belated answer for others who might find this thread. In my case, I just want the mouse wheel events to go to whatever control is under the cursor... just like right-click does (it would be bewildering if right-click went to the focus control rather than the control under the cursor... I argue the same is true for the mouse wheel, except we've gotten used to it).
Anyway, the answer is super easy. Just add a PreFilterMessage to your application and have it redirect mouse wheel events to the control under the mouse:
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEWHEEL: // 0x020A
case WM_MOUSEHWHEEL: // 0x020E
IntPtr hControlUnderMouse = WindowFromPoint(new Point((int)m.LParam));
if (hControlUnderMouse == m.HWnd)
return false; // already headed for the right control
else
{
// redirect the message to the control under the mouse
SendMessage(hControlUnderMouse, m.Msg, m.WParam, m.LParam);
return true;
}
default:
return false;
}
}

This is Brian Kennedy's answer completed with Hank Schultz comment:
First you should make a class implements IMessageFilter:
public class MessageFilter : IMessageFilter
{
private const int WM_MOUSEWHEEL = 0x020A;
private const int WM_MOUSEHWHEEL = 0x020E;
[DllImport("user32.dll")]
static extern IntPtr WindowFromPoint(Point p);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
IntPtr hControlUnderMouse = WindowFromPoint(new Point((int)m.LParam));
if (hControlUnderMouse == m.HWnd)
{
//Do nothing because it's already headed for the right control
return false;
}
else
{
//Send the scroll message to the control under the mouse
uint u = Convert.ToUInt32(m.Msg);
SendMessage(hControlUnderMouse, u, m.WParam, m.LParam);
return true;
}
default:
return false;
}
}
}
Example usage:
public partial class MyForm : Form
{
MessageFilter mf = null;
public MyForm
{
Load += MyForm_Load;
FormClosing += MyForm_FormClosing;
}
private void MyForm_Load(object sender, EventArgs e)
{
mf= new MessageFilter();
Application.AddMessageFilter(mf);
}
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
Application.RemoveMessageFilter(mf);
}
}

Use Control.MouseEnter Event to set focus to to the control. E.g. using ActiveControl Property

Related

Cross-platform textbox input in MonoGame (Windows/Ubuntu)

I'm working on a game for desktop platforms that uses MonoGame. Previously, it was targeted only at Windows and used pure XNA. I'm exploring options to make it support other platforms using MonoGame and my current alternate test platform is Ubuntu 15.04.
One of the features of the game is support for text boxes that behave similarly to the standard windows UI textbox controls. In order to accomplish this, I used code from this StackOverflow answer.
My question is: How can I write a portable version of the functionality from the linked answer in such a way that it will work on alternate desktop platforms (in this case, Ubuntu)? I'm looking for something that will capture keypress events from the game window on both Windows and Ubuntu.
Additional info:
The linked answer overrides the WndProc of the game's window and passes input into the current textbox by firing events (CharEntered, KeyDown, KeyUp). I've refactored the answer's code to a point where I can encapsulate all the Windows functionality in a single "events" class:
internal class Win32KeyboardEvents : IKeyboardEvents
{
public event CharEnteredHandler CharEntered;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
private readonly IntPtr _prevWndProc;
private readonly NativeMethods.WndProc _hookProcDelegate;
public Win32KeyboardEvents(GameWindow window)
{
_hookProcDelegate = HookProc;
_prevWndProc = (IntPtr)NativeMethods.SetWindowLong(window.Handle, NativeMethods.GWL_WNDPROC,
(int)Marshal.GetFunctionPointerForDelegate(_hookProcDelegate));
}
private IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
IntPtr returnCode = NativeMethods.CallWindowProc(_prevWndProc, hWnd, msg, wParam, lParam);
switch (msg)
{
case NativeMethods.WM_GETDLGCODE:
returnCode = (IntPtr)(returnCode.ToInt32() | NativeMethods.DLGC_WANTALLKEYS);
break;
case NativeMethods.WM_KEYDOWN:
if (KeyDown != null) //fire events that are subscribed to by textbox object based on which windows message is received
KeyDown(null, new XNAKeyEventArgs((Keys)wParam));
break;
case NativeMethods.WM_KEYUP:
if (KeyUp != null)
KeyUp(null, new XNAKeyEventArgs((Keys)wParam));
break;
case NativeMethods.WM_CHAR:
if (CharEntered != null)
CharEntered(null, new Win32CharEnteredEventArgs((char)wParam, lParam.ToInt32()));
break;
}
return returnCode;
}
}
My first attempt for something that would work on Ubuntu was creating a GameComponent-derived object that listened for keyboard input in the component's Update() and fired events appropriately, but this quickly got too complicated.
I've investigated using GTK#, but there are quite a few dependencies that I don't want to install unless GTK# is the best way to go. Here is my GTK# attempt, which is currently untested due to a missing dependency:
internal class CrossPlatformKeyboardEvents : DrawingArea, IKeyboardEvents
{
public event CharEnteredHandler CharEntered = delegate { };
public event KeyEventHandler KeyDown = delegate { };
public event KeyEventHandler KeyUp = delegate { };
public CrossPlatformKeyboardEvents()
{
Application.Init();
Application.Run();
AddEvents((int)EventMask.KeyPressMask);
AddEvents((int)EventMask.KeyReleaseMask);
}
[ConnectBefore]
protected override bool OnKeyPressEvent(EventKey evnt)
{
if (IsPastEvent(evnt))
{
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_PASTE_CODE));
return base.OnKeyPressEvent(evnt);
}
switch (evnt.Key)
{
case Key.Return:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_RETURNKEY_CODE));
break;
case Key.BackSpace:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_BACKSPACE_CODE));
break;
case Key.Tab:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_TAB_CODE));
break;
}
var keyCode = GetXNAKey(evnt.Key);
if (keyCode != Keys.None)
KeyDown(null, new XNAKeyEventArgs(keyCode));
return base.OnKeyPressEvent(evnt);
}
[ConnectBefore]
protected override bool OnKeyReleaseEvent(EventKey evnt)
{
var keyCode = GetXNAKey(evnt.Key);
if (keyCode != Keys.None)
KeyUp(null, new XNAKeyEventArgs(keyCode));
return base.OnKeyReleaseEvent(evnt);
}
private bool IsPastEvent(EventKey evnt)
{
return (evnt.State & ModifierType.ControlMask) > 0 && (evnt.Key == Key.V || evnt.Key == Key.v);
}
private Keys GetXNAKey(Key key)
{
if ((key >= Key.Key_0 && key <= Key.Key_9) ||
(key >= Key.A && key <= Key.Z))
{
return (Keys) key;
}
if (key >= Key.a && key <= Key.z)
{
return (Keys) (key - 32);
}
return Keys.None;
//switch (key)
//{
//}
}
I got around this by using the TextInput event on GameWindow in MonoGame.
This is an extension that is part of MonoGame but not part of Xna. Currently, I have 2 projects - one for standard Xna and one for Monogame. The Monogame project has links to all the files in my Xna project so they share the same code.
Because of the way my projects are set up, I also had to add a compiler flag (MONO) that excludes this code from being compiled for standard XNA.
Full class (replaces CrossPlatformKeyboardEvents posted in the question):
internal sealed class MonoGameKeyboardEvents : IKeyboardEvents
{
public event CharEnteredHandler CharEntered;
private readonly GameWindow _window;
public MonoGameKeyboardEvents(GameWindow window)
{
_window = window;
#if !MONO
if (CharEntered != null) //hide warning for "member is not used"
CharEntered(null, null);
#else
_window.TextInput += GameWindow_TextInput;
#endif
}
#if MONO
private void GameWindow_TextInput(object sender, TextInputEventArgs e)
{
if (CharEntered != null)
{
CharEntered(null, new CharEnteredEventArgs(e.Character));
}
}
~MonoGameKeyboardEvents()
{
Dispose(false);
}
#endif
public void Dispose()
{
#if !MONO
}
#else
Dispose(true);
}
private void Dispose(bool disposing)
{
if (!disposing) return;
_window.TextInput -= GameWindow_TextInput;
}
#endif
}

How to hide scroll bars without disabling them

Is there a way to scroll on a RichTextBox without the scroll bar visible? I searched Google, but only came up with the ScrollBars Property.
I am using Microsoft Visual C# Express, winforms.
EDIT:
Something that would fix my problem would be a void with a few parameters such as a RTB, which direction to scroll, and how far to go.
Some steps to do:
Put your RTB in a Panel (Dock: none)
Call panel.width -= 20; within your code.
Now we need a mouse wheel scrolling without focus, use my code below:
Here is a little workaround:
public Main()
{
InitializeComponent();
//Works for panels, richtextboxes, 3rd party etc..
Application.AddMessageFilter(new ScrollableControls(richTextBox1));
}
ScrollableControls.cs:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
//Let controls scroll without Focus();
namespace YOURNAMESPACE
{
internal struct ScrollableControls : IMessageFilter
{
private const int WmMousewheel = 0x020A;
private readonly Control[] _controls;
public ScrollableControls(params Control[] controls)
{
_controls = controls;
}
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if (m.Msg != WmMousewheel) return false;
foreach (var item in _controls)
{
ScrollControl(item, ref m);
}
return false;
}
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
private static void ScrollControl(Control control, ref Message m)
{
if (control.RectangleToScreen(control.ClientRectangle).Contains(Cursor.Position) && control.Visible)
{
SendMessage(control.Handle, m.Msg, m.WParam.ToInt32(), m.LParam.ToInt32());
}
}
}
}
Keep in mind that 3rd party controls often use nested container or similiar like radScrollablePanel1.PanelContainer, so you have to call them instead.

Implementing a drag over window option

Note:
Using Windows Forms
preferably C# .NET
Question:
Best method for implementing a drag-over-window tool, similar (or identical) to that featured in process explorer, to obtain the process ID corresponding to the selected Window
I think the easiest way is to put a control on your form that acts as a starting point; you press a mouse button there, and then you move it over the screen while the button is pressed, and pick up the process ID of whatever you are pointing at. I my example I have used a panel (called _aim).
First we set up the mouse events:
private void Panel_MouseDown(object sender, MouseEventArgs e)
{
// make all mouse events being raised in the _aim panel
// regardless of whether the mouse is within the control's
// bounds or not
_aim.Capture = true;
}
private void Panel_MouseMove(object sender, MouseEventArgs e)
{
if (_aim.Capture)
{
// get the process id only if we have mouse capture
uint processId = GetProcessIdFromPoint(
_aim.PointToScreen(e.Location)).ToString();
// do something with processId (store it for remembering the
// last processId seen, to be used as MouseUp for instance)
}
}
private void Panel_MouseUp(object sender, MouseEventArgs e)
{
if (_aim.Capture)
{
// release capture if we have it
_aim.Capture = false;
// perhaps do something more (fetch info about last seen
// process id, if we stored it during MouseMove, for instance)
}
}
The GetProcessIdFromPoint method looks like this:
private uint GetProcessIdFromPoint(Point point)
{
uint procId;
WinApi.GetWindowThreadProcessId(WinApi.WindowFromPoint(point), out procId);
return procId;
}
And finally the windows API things (from pinvoke.net):
public static class WinApi
{
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
public static implicit operator System.Drawing.Point(POINT p)
{
return new System.Drawing.Point(p.X, p.Y);
}
public static implicit operator POINT(System.Drawing.Point p)
{
return new POINT(p.X, p.Y);
}
}
[DllImport("user32.dll")]
public static extern IntPtr WindowFromPoint(POINT Point);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
}

How do i capture the Print Screen key?

I my program I need to capture when the Print Screen key is pressed down but it is not working (however it works with other keys).
I guess this has something to do with windows hijacking my authority and since im still new at this I'd love to know how I can get around this issue.
Here's my current code:
namespace Boom_Screenshot_
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
//SETTINGS
Key TRIGGER_KEY = Key.PrintScreen;
public Window1()
{
InitializeComponent();
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == TRIGGER_KEY)
{
MessageBox.Show("'PrintScreen' was pressed.");
}
}
}
}
I have an answer for you that I found here (I don't speak Chinese so don't ask me what it says :). You have to set a hook. He provides a wrapper class. I repeat some code here without the Chinese characters. RegisterHotKey.cs ...
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestKeydown
{
public class RegisterHotKeyClass
{
private IntPtr m_WindowHandle = IntPtr.Zero;
private MODKEY m_ModKey = MODKEY.MOD_CONTROL;
private Keys m_Keys = Keys.A;
private int m_WParam = 10000;
private bool Star = false;
private HotKeyWndProc m_HotKeyWnd = new HotKeyWndProc();
public IntPtr WindowHandle
{
get { return m_WindowHandle; }
set { if (Star)return; m_WindowHandle = value; }
}
public MODKEY ModKey
{
get { return m_ModKey; }
set { if (Star)return; m_ModKey = value; }
}
public Keys Keys
{
get { return m_Keys; }
set { if (Star)return; m_Keys = value; }
}
public int WParam
{
get { return m_WParam; }
set { if (Star)return; m_WParam = value; }
}
public void StarHotKey()
{
if (m_WindowHandle != IntPtr.Zero)
{
if (!RegisterHotKey(m_WindowHandle, m_WParam, m_ModKey, m_Keys))
{
throw new Exception("");
}
try
{
m_HotKeyWnd.m_HotKeyPass = new HotKeyPass(KeyPass);
m_HotKeyWnd.m_WParam = m_WParam;
m_HotKeyWnd.AssignHandle(m_WindowHandle);
Star = true;
}
catch
{
StopHotKey();
}
}
}
private void KeyPass()
{
if (HotKey != null) HotKey();
}
public void StopHotKey()
{
if (Star)
{
if (!UnregisterHotKey(m_WindowHandle, m_WParam))
{
throw new Exception("");
}
Star = false;
m_HotKeyWnd.ReleaseHandle();
}
}
public delegate void HotKeyPass();
public event HotKeyPass HotKey;
private class HotKeyWndProc : NativeWindow
{
public int m_WParam = 10000;
public HotKeyPass m_HotKeyPass;
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0312 && m.WParam.ToInt32() == m_WParam)
{
if (m_HotKeyPass != null) m_HotKeyPass.Invoke();
}
base.WndProc(ref m);
}
}
public enum MODKEY
{
MOD_ALT = 0x0001,
MOD_CONTROL = 0x0002,
MOD_SHIFT = 0x0004,
MOD_WIN = 0x0008,
}
[DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr wnd, int id, MODKEY mode, Keys vk);
[DllImport("user32.dll")]
public static extern bool UnregisterHotKey(IntPtr wnd, int id);
}
}
Calling code in a Form ...
private RegisterHotKeyClass _RegisKey = new RegisterHotKeyClass();
void _Regis_HotKey()
{
MessageBox.Show("ok");
}
private void Form1_Load(object sender, EventArgs e)
{
_RegisKey.Keys = Keys.PrintScreen;
_RegisKey.ModKey = 0;
_RegisKey.WindowHandle = this.Handle;
_RegisKey.HotKey += new RegisterHotKeyClass.HotKeyPass(_Regis_HotKey);
_RegisKey.StarHotKey();
}
Below is my pure WPF solution.
We can achieve this in xaml by using NavigationCommands (Namespace: System.Window.Input) class which provides a standard set of navigation commands (e.g. NextPage, PreviousPage, Refresh, Search etc.)
Implementation Approach:
So we can call any custom code to execute on application refresh using NavigationCommands.Refresh as
<UserControl.CommandBindings>
<CommandBinding Command='NavigationCommands.Refresh'
Executed="ApplicationRefresh_Executed">
</CommandBinding>
</UserControl.CommandBindings>
Now in code behind class of UserControl we can define method as
private void ApplicationRefresh_Executed(object sender, ExecutedRoutedEventArgs e)
{
// Implementation goes here.
}

How can I register a global hot key to say CTRL+SHIFT+(LETTER) using WPF and .NET 3.5?

I'm building an application in C# using WPF. How can I bind to some keys?
Also, how can I bind to the Windows key?
This is a full working solution, hope it helps...
Usage:
_hotKey = new HotKey(Key.F9, KeyModifier.Shift | KeyModifier.Win, OnHotKeyHandler);
...
private void OnHotKeyHandler(HotKey hotKey)
{
SystemHelper.SetScreenSaverRunning();
}
Class:
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
}
// ******************************************************************
}
I'm not sure of what you mean by "global" here, but here it goes (I'm assuming you mean a command at the application level, for example, Save All that can be triggered from anywhere by Ctrl + Shift + S.)
You find the global UIElement of your choice, for example, the top level window which is the parent of all the controls where you need this binding. Due to "bubbling" of WPF events, events at child elements will bubble all the way up to the root of the control tree.
Now, first you need
to bind the Key-Combo with a Command using an InputBinding like this
you can then hookup the command to your handler (e.g. code that gets called by SaveAll) via a CommandBinding.
For the Windows Key, you use the right Key enumerated member, Key.LWin or Key.RWin
public WindowMain()
{
InitializeComponent();
// Bind Key
var ib = new InputBinding(
MyAppCommands.SaveAll,
new KeyGesture(Key.S, ModifierKeys.Shift | ModifierKeys.Control));
this.InputBindings.Add(ib);
// Bind handler
var 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.
}
Registering OS level shortcuts is hardly ever a good thing: users don't want you to mess with their OS.
That said, there is a much simpler and user friendly way of doing this in WPF, if you're ok with the hotkey working within the application only (i.e as long as your WPF app has the focus):
In 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
}
It's that simple
If you're going to mix Win32 and WPF, here's how I did it:
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());
}
}
}
You can get the virtual-key code for the hotkey you want to register here: http://msdn.microsoft.com/en-us/library/ms927178.aspx
There may be a better way, but this is what I've got so far.
Cheers!
This is similar to the answers already given, but I find it a bit cleaner:
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.
}
}
}
I've found it on fluxbytes.com.
With the NHotKey package, you can make your hotkey global:
https://github.com/thomaslevesque/NHotkey
https://thomaslevesque.com/2014/02/05/wpf-declare-global-hotkeys-in-xaml-with-nhotkey/ (use web.archive.org if the link is broken)
In short, for XAML, all you need to do is to replace
<KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}" />
by
<KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}"
HotkeyManager.RegisterGlobalHotkey="True" />
I'm not sure about WPF, but this may help. I used the solution described in RegisterHotKey (user32) (modified to my needs of course) for a C# Windows Forms application to assign a CTRL-KEY combination within Windows to bring up a C# form, and it worked beautifully (even on Windows Vista). I hope it helps and good luck!
I've found the Global Hotkeys in WPF project on codeproject.com which does the job for me. It's relatively recent, does not need a reference to System.Windows.Forms and works "globally" in terms of reacting to the hotkey being pressed even if "your" application is not the active window.
Baboon's solution works best because you may have multiple windows. I did tweak it so it uses the PreviewKeyDownEvent instead of the PreviewKeyUpEvent in order to handle repetition in keystrokes.
I would advise against OS-level registration unless you are writing something like a snipping tool or an audio recording app as it will let you access functionality when the window is not focused.
Although RegisterHotKey is sometimes precisely what you want, in most cases you probably do not want to use system-wide hotkeys. I ended up using code like the following:
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");
}
}
}
}
RegisterHotKey() suggested by John could work - the only catch is that it requires an HWND (using PresentationSource.FromVisual(), and casting the result to an HwndSource).
However, you'll also need to respond to the WM_HOTKEY message - I'm not sure if there is a way to get access to the WndProc of a WPF window or not (which can be done for Windows Forms windows).

Categories