How to use GetAsyncKeyState in C#? - c#

I want to know how to use it....I want to say that if I Click Up and Right Arrow (Form1_KeyDown Event) then timer1.Start();
and When I release the Up and Right Arrow (Form1_KeyUp Event) then timer1.Stop();
I have already Imported the "User32.dll"
using System.Runtime.InteropServices;
[DllImport("User32.dll")]
public static extern short GetAsyncKeyState(Keys ArrowKeys);
so How to use it...I see a lot of website but can't get it

Implement IMessageFilter for you Form and track when the Up/Right Arrows are toggled:
public partial class form1 : Form, IMessageFilter
{
public form1()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
private bool UpDepressed = false;
private bool RightDepressed = false;
private const int WM_KEYDOWN = 0x100;
private const int WM_KEYUP = 0x101;
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_KEYDOWN:
if ((Keys)m.WParam == Keys.Up)
{
UpDepressed = true;
}
else if ((Keys)m.WParam == Keys.Right)
{
RightDepressed = true;
}
break;
case WM_KEYUP:
if ((Keys)m.WParam == Keys.Up)
{
UpDepressed = false;
}
else if ((Keys)m.WParam == Keys.Right)
{
RightDepressed = false;
}
break;
}
timer1.Enabled = (UpDepressed && RightDepressed);
label1.Text = timer1.Enabled.ToString();
return false;
}
private void timer1_Tick(object sender, EventArgs e)
{
label2.Text = DateTime.Now.ToString("ffff");
}
}

Here is how you can use it
int keystroke;
byte[] result = BitConverter.GetBytes(GetAsyncKeyState(keystroke));
if (result[0] == 1)
Console.Writeline("the key was pressed after the previous call to GetAsyncKeyState.")
if (result[1] == 0x80)
Console.Writeline("The key is down");

change
[DllImport("User32.dll")]
public static extern short GetAsyncKeyState(Keys ArrowKeys);
to
[DllImport("User32.dll")]
public static extern bool GetAsyncKeyState(Keys ArrowKeys);
async key state should be a bool not a short

Related

Console check for pressed keys like in monogame

In my console application, I would like to be able to detect for key presses somewhat like in mono, where you can get a list of the currently pressed keys and check if a key is on the list, or detect if a key is being pressed. My problem with using
if( Console.KeyAvailable ) k = Console.ReadKey( true ).Key;
is the fact that there will be a delay after I press the first key. You can see an example of this if you open notepad and hold down "A". A will be pressed, then a delay, and then A will be spammed.
How can I get keyboard input without the delay in between presses? I'm not afraid to use low level functions such as hooking into kernel32.dll
So I wrote some code, based off what I've read so far.
Step 1: Copy the following code into your console application. It must be STAThread or it will throw an error. Put in the commands you want to use in the switch statement. All other keys will be blocked by ReadKey(true).
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
using System;
using System.Windows.Forms;
using System.Windows.Input;
namespace ConsoleApplication10
{
class Program
{
[STAThread]
static void Main(string[] args)
{
KeyListener.RegisterHotKey(Keys.A);
KeyListener.HotKeyPressed += new EventHandler<HotKeyEventArgs>(KeyListener_HotKeyPressed);
while (true)
{
Console.ReadKey(true);
}
}
static void KeyListener_HotKeyPressed(object sender, HotKeyEventArgs e)
{
switch (e.Key)
{
case Keys.A:
{
Console.WriteLine("Do stuff");
return;
}
default:
return;
}
}
}
}
Step 2: Add a reference to System.Windows.Forms. You need this to have a hidden Form that is necessary for the message loop for the keyboard hook.
Step 3: Add the following static class. It does all the heavy lifting of the keyboard hooks for you, so you don't have to do it.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleApplication10
{
public static class KeyListener
{
public static event EventHandler<HotKeyEventArgs> HotKeyPressed;
public static int RegisterHotKey(Keys key, KeyModifiers modifiers)
{
_windowReadyEvent.WaitOne();
int id = System.Threading.Interlocked.Increment(ref _id);
_wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, id, (uint)modifiers, (uint)key);
return id;
}
public static int RegisterHotKey(Keys key)
{
_windowReadyEvent.WaitOne();
int id = System.Threading.Interlocked.Increment(ref _id);
_wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, id, (uint)KeyModifiers.None, (uint)key);
return id;
}
public static void UnregisterHotKey(int id)
{
_wnd.Invoke(new UnRegisterHotKeyDelegate(UnRegisterHotKeyInternal), _hwnd, id);
}
delegate void RegisterHotKeyDelegate(IntPtr hwnd, int id, uint modifiers, uint key);
delegate void UnRegisterHotKeyDelegate(IntPtr hwnd, int id);
private static void RegisterHotKeyInternal(IntPtr hwnd, int id, uint modifiers, uint key)
{
RegisterHotKey(hwnd, id, modifiers, key);
}
private static void UnRegisterHotKeyInternal(IntPtr hwnd, int id)
{
UnregisterHotKey(_hwnd, id);
}
private static void OnHotKeyPressed(HotKeyEventArgs e)
{
if (KeyListener.HotKeyPressed != null)
{
KeyListener.HotKeyPressed(null, e);
}
}
private static volatile MessageWindow _wnd;
private static volatile IntPtr _hwnd;
private static ManualResetEvent _windowReadyEvent = new ManualResetEvent(false);
static KeyListener()
{
Thread messageLoop = new Thread(delegate ()
{
Application.Run(new MessageWindow());
});
messageLoop.Name = "MessageLoopThread";
messageLoop.IsBackground = true;
messageLoop.Start();
}
private class MessageWindow : Form
{
public MessageWindow()
{
_wnd = this;
_hwnd = this.Handle;
_windowReadyEvent.Set();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_HOTKEY)
{
HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
KeyListener.OnHotKeyPressed(e);
}
base.WndProc(ref m);
}
protected override void SetVisibleCore(bool value)
{
// Ensure the window never becomes visible
base.SetVisibleCore(false);
}
private const int WM_HOTKEY = 0x312;
}
[DllImport("user32", SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private static int _id = 0;
}
public class HotKeyEventArgs : EventArgs
{
public readonly Keys Key;
public readonly KeyModifiers Modifiers;
public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
{
this.Key = key;
this.Modifiers = modifiers;
}
public HotKeyEventArgs(IntPtr hotKeyParam)
{
uint param = (uint)hotKeyParam.ToInt64();
Key = (Keys)((param & 0xffff0000) >> 16);
Modifiers = (KeyModifiers)(param & 0x0000ffff);
}
}
[Flags]
public enum KeyModifiers
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
NoRepeat = 0x4000
}
}
Step 4:
Now, there's still a delay. It's way more elegant, but you still have the operating system fighting you. So what to do?
You have two options.
a) You implement a timing option and you simply repeat the action for as long as a key is down on the timer tick event. You can either copy the code or merge it with the hotkey approach that I've given you.
See here for details: Removing the delay after KeyDown event?
private bool _moveUp;
private bool _moveDown;
private bool _moveLeft;
private bool _moveRight;
// You can add the Timer in the Winforms Designer instead if you like;
// The Interval property can be configured there at the same time, along
// with the Tick event handler, simplifying the non-Designer code here.
private System.Windows.Forms.Timer _movementTimer = new Timer { Interval = 100 };
public MainForm()
{
InitializeComponent();
_movementTimer.Tick += movementTimer_Tick;
}
private void movementTimer_Tick(object sender, EventArgs e)
{
_DoMovement();
}
private void _DoMovement()
{
if (_moveLeft) Player.MoveLeft();
if (_moveRight) Player.MoveRight();
if (_moveUp) Player.MoveUp();
if (_moveDown) Player.MoveDown();
}
// You could of course override the OnKeyDown() method instead,
// assuming the handler is in the Form subclass generating the
// the event.
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.IsRepeat)
{
// Ignore key repeats...let the timer handle that
return;
}
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = true;
break;
case Keys.Down:
_moveDown = true;
break;
case Keys.Left:
_moveLeft = true;
break;
case Keys.Right:
_moveRight = true;
break;
}
_DoMovement();
_movementTimer.Start();
}
public void MainForm_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = false;
break;
case Keys.Down:
_moveDown = false;
break;
case Keys.Left:
_moveLeft = false;
break;
case Keys.Right:
_moveRight = false;
break;
}
if (!(_moveUp || _moveDown || _moveLeft || _moveRight))
{
_movementTimer.Stop();
}
}
b) In your Main method, you get the delay setting, set the delay programmatically to the lowest setting, and on Application Exit, you set it back to its original setting.
See here for where to find it in the registry: https://superuser.com/questions/388160/keyboard-repeat-rate-repeat-delay-values-in-win7
And how to read/write to the registry: https://msdn.microsoft.com/en-us/library/microsoft.win32.registry_methods(v=vs.110).aspx
Note: With this approach, there's still a small delay. It's small, but it's there. Good luck.

C# Setting up a single key hotkey

I'm currently trying to set a single key as global hook. Howerver, I was perhaps wondering if it's possible to manipulate this code, so that a single button is set as a global hot key?
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
const int B = 0x42;
const int MOD_SHIFT = 0x0004;
const int WM_HOTKEY = 0x0312;
private void Form1_Load(object sender, EventArgs e)
{
RegisterHotKey(this.Handle, 1, B + MOD_SHIFT, (int)Keys.X);
RegisterHotKey(this.Handle, 2, B + MOD_SHIFT, (int)Keys.Y);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
UnregisterHotKey(this.Handle, 1);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_HOTKEY && (int)m.WParam == 1)
MessageBox.Show("Hotkey X pressed.");
if (m.Msg == WM_HOTKEY && (int)m.WParam == 2)
MessageBox.Show("Hotkey Y pressed.");
base.WndProc(ref m);
}
}
}

I can't access datagrid from a custom event

I've a simple script which looks at the clipboard. Once it's changed it throws an event that looks at the text contained and uses that text to populate a datagrid.
The event is working perfectly fine and retrieve the text as expected, but when I try to add a row to the datagrid nothing happens. Any suggestion?
public void ClipBoardHasChanged(ClipboardMonitor m, ClipboardChangedEventArgs e)
{
string clipBoardValue = (string)e.ClipboardChangedEventArgsT.GetData(DataFormats.StringFormat, true);
this.dataGridView1.Rows.Add();
this.dataGridView1.Rows[0].Cells[0].Value = clipBoardValue;
}
The Class of Clipboard
namespace MarketMatrixCorrelation
{
[DefaultEvent("ClipboardChanged")]
public class ClipboardMonitor : Control
{
IntPtr nextClipboardViewer;
[DllImport("User32.dll")]
protected static extern int SetClipboardViewer(int hWndNewViewer);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
public ClipboardMonitor()
{
this.BackColor = System.Drawing.Color.Red;
this.Visible = false;
nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
}
/// <summary>
/// Clipboard contents changed.
/// </summary>
public delegate void ClipboardChanged(ClipboardMonitor s, ClipboardChangedEventArgs e);
public event ClipboardChanged ClipboardChangedT = delegate { };
protected override void Dispose(bool disposing)
{
ChangeClipboardChain(this.Handle, nextClipboardViewer);
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
// defined in winuser.h
const int WM_DRAWCLIPBOARD = 0x308;
const int WM_CHANGECBCHAIN = 0x030D;
switch (m.Msg)
{
case WM_DRAWCLIPBOARD:
SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
OnClipboardChanged();
break;
case WM_CHANGECBCHAIN:
if (m.WParam == nextClipboardViewer)
nextClipboardViewer = m.LParam;
else
SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
break;
default:
base.WndProc(ref m);
break;
}
}
//Catch when something is posted on the clipboard
private void OnClipboardChanged()
{
try
{
ClipboardChangedEventArgs iData = new ClipboardChangedEventArgs();
iData.ClipboardChangedEventArgsT = Clipboard.GetDataObject();
if (iData.ClipboardChangedEventArgsT.GetDataPresent(DataFormats.StringFormat))
{
ClipboardChangedT(this, iData);
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
public class ClipboardChangedEventArgs : EventArgs
{
private IDataObject DataObject;
public IDataObject ClipboardChangedEventArgsT
{
set
{
DataObject = value;
}
get
{
return this.DataObject;
}
}
}
}
Just place the ClipBoardMonitor in your Form and paste this in Form Load Event
private void Form1_Load(object sender, EventArgs e)
{
this.clipboardMonitor1.ClipboardChangedT += (o, ex) =>
{
string clipBoardValue = (string)ex.ClipboardChangedEventArgsT.GetData(DataFormats.StringFormat, true);
object[] row = { clipBoardValue };
this.dataGridView1.Rows.Add(row);
};
}

Check whether user is inactive

How do I check user is inactive?
I have this:
class UserActivity : IMessageFilter
{
private double afk_time = 0.1;//minutes
private DateTime last_activity = DateTime.Now;
public static bool inactive = false;
private int WM_LBUTTONDOWN = 0x0201;
private int WM_MBUTTONDOWN = 0x0207;
private int WM_RBUTTONDOWN = 0x0204;
private int WM_MOUSEWHEEL = 0x020A;
private int WM_MOUSEMOVE = 0x0200;
private int WM_KEYDOWN = 0x0100;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_LBUTTONDOWN || m.Msg == WM_MBUTTONDOWN || m.Msg == WM_RBUTTONDOWN || m.Msg == WM_MOUSEWHEEL || m.Msg == WM_MOUSEMOVE || m.Msg == WM_KEYDOWN)
{
this.last_activity = DateTime.Now;
inactive = false;
}
if (DateTime.Now.AddMinutes(-afk_time) > last_activity)
inactive = true;
return false;
}
}
But I must run it in Program.cs
Application.AddMessageFilter(new UserActivity());
How can I do that I can run the checking of user inactivity by my self. I'll check some checkbox and it will start checking.
And I want check global user activity - in all system not only in app.
I don't want use of cpu unnecessary. Or should I use another solution?
I found this and it works perfect !
So if another one have problem with it here is solution:
[StructLayout(LayoutKind.Sequential)]
public struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}
[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
public static TimeSpan? GetInactiveTime()
{
LASTINPUTINFO info = new LASTINPUTINFO();
info.cbSize = (uint)Marshal.SizeOf(info);
if (GetLastInputInfo(ref info))
return TimeSpan.FromMilliseconds(Environment.TickCount - info.dwTime);
else
return null;
}
I have tried to build a class base on previous answer, I have test it and it work properly.
It is async but i have not use async key. If someone finds some problem or amelioration so welcome.
public class User
{
[StructLayout(LayoutKind.Sequential)]
public struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}
[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
public static TimeSpan? GetInactiveTime()
{
LASTINPUTINFO info = new LASTINPUTINFO();
info.cbSize = (uint)Marshal.SizeOf(info);
if (GetLastInputInfo(ref info))
return TimeSpan.FromMilliseconds(Environment.TickCount - info.dwTime);
else
return null;
}
private TimeSpan? LastTime;
private DispatcherTimer timer = new DispatcherTimer();
public void RequestUserActivity()
{
LastTime = GetInactiveTime();
timer.Interval = TimeSpan.FromMilliseconds(500);
timer.Tick += timer_Tick;
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
TimeSpan? tmpTime = GetInactiveTime();
if (LastTime.HasValue && tmpTime.HasValue && tmpTime < LastTime)
{
timer.Stop();
UserAtivited(this, new EventArgs());
}
else if (!LastTime.HasValue || !tmpTime.HasValue)
{
timer.Stop();
throw new Exception();
}
}
public event EventHandler UserAtivited;
}
Now In your window create a new instance of User, register to UserActivated event and laugth RequestUserActivity(). I expert it will be helpfull.
Please, do not have regards on my poor english. I'm french speaker.

UserControl: How to add MouseWheel Listener?

I'm creating an UserControl that should react if the mouse is over the control and MouseWheel gets rotated.
Currently i'm doing this as shown here:
public MyUserControl()
{
this.MouseWheel += new MouseEventHandler(MouseWheelHandler);
}
private void MouseWheelHandler(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Delta > 0)
incIndex();
if (e.Delta < 0)
decIndex();
}
protected override void OnMouseEnter(EventArgs e)
{
this.Focus();
base.OnMouseEnter(e);
}
In fact this works fine, but the problem is the part with the "this.Focus();" as it destroys my forms / applications behaviour.
Is there a better way to achive this?
having the same problem, I finally implemented a mix of both solution by #Paul_Westcott and #nr1. This is a local solution like solution of #Paul_Westcott (only applies to the winforms control that subscribes to). It is multiple monitor safe and MDI safe (overlapping by other windows within the application)
public static class MouseWheelHandlerForWinformsControl
{
private class MouseWheelMessageFilter : IMessageFilter
{
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(Point pt);
private readonly Control mCtrl;
private readonly Action<MouseEventArgs> mOnMouseWheel;
public MouseWheelMessageFilter(Control ctrl, Action<MouseEventArgs> onMouseWheel)
{
mCtrl = ctrl;
mOnMouseWheel = onMouseWheel;
}
public bool PreFilterMessage(ref Message m)
{
// handle only mouse wheel messages
if (m.Msg != 0x20a)
return false;
Point mouseAbsolutePosition = new Point(m.LParam.ToInt32());
Point mouseRelativePosition = mCtrl.PointToClient(mouseAbsolutePosition);
IntPtr hControlUnderMouse = WindowFromPoint(mouseAbsolutePosition);
Control controlUnderMouse = Control.FromHandle(hControlUnderMouse);
if (controlUnderMouse != mCtrl)
return false;
MouseButtons buttons = GetMouseButtons(m.WParam.ToInt32());
int delta = m.WParam.ToInt32() >> 16;
var e = new MouseEventArgs(buttons, 0, mouseRelativePosition.X, mouseRelativePosition.Y, delta);
mOnMouseWheel(e);
return true;
}
private static MouseButtons GetMouseButtons(int wParam)
{
MouseButtons buttons = MouseButtons.None;
if(HasFlag(wParam, 0x0001)) buttons |= MouseButtons.Left;
if(HasFlag(wParam, 0x0010)) buttons |= MouseButtons.Middle;
if(HasFlag(wParam, 0x0002)) buttons |= MouseButtons.Right;
if(HasFlag(wParam, 0x0020)) buttons |= MouseButtons.XButton1;
if(HasFlag(wParam, 0x0040)) buttons |= MouseButtons.XButton2;
return buttons;
}
private static bool HasFlag(int input, int flag)
{
return (input & flag) == flag;
}
}
public static void MemorySafeAdd(Control ctrl, Action<MouseEventArgs> onMouseWheel)
{
if (ctrl == null || onMouseWheel == null)
throw new ArgumentNullException();
var filter = new MouseWheelMessageFilter(ctrl, onMouseWheel);
Application.AddMessageFilter(filter);
ctrl.Disposed += (s, e) => Application.RemoveMessageFilter(filter);
}
}
Once you have added this helper class into your solution, you subscribe a control myControl to the mouse-wheel, in one line, as following:
public void Init() {
MouseWheelHandlerForWinformsControl.MemorySafeAdd(myControl, OnMouseWheelEvent);
}
void OnMouseWheelEvent(MouseEventArgs args) {
// do what you need here
}
Copy pasted from http://social.msdn.microsoft.com/forums/en-US/winforms/thread/eb922ed2-1036-41ca-bd15-49daed7b637c/
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsApplication1 {
public partial class Form1 : Form, IMessageFilter {
public Form1() {
InitializeComponent();
Application.AddMessageFilter(this);
}
public bool PreFilterMessage(ref Message m) {
if (m.Msg == 0x20a) {
// WM_MOUSEWHEEL, find the control at screen position m.LParam
Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
IntPtr hWnd = WindowFromPoint(pos);
if (hWnd != IntPtr.Zero && hWnd != m.HWnd && Control.FromHandle(hWnd) != null) {
SendMessage(hWnd, m.Msg, m.WParam, m.LParam);
return true;
}
}
return false;
}
// P/Invoke declarations
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(Point pt);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}
}
this works great, thx Hans Passant
Just a slight variation on the solution that has already been published, in order to not have to use P/Invoke.
public static class MouseWheelHandler
{
public static void Add(Control ctrl, Action<MouseEventArgs> onMouseWheel)
{
if (ctrl == null || onMouseWheel == null)
throw new ArgumentNullException();
var filter = new MouseWheelMessageFilter(ctrl, onMouseWheel);
Application.AddMessageFilter(filter);
ctrl.Disposed += (s, e) => Application.RemoveMessageFilter(filter);
}
class MouseWheelMessageFilter
: IMessageFilter
{
private readonly Control _ctrl;
private readonly Action<MouseEventArgs> _onMouseWheel;
public MouseWheelMessageFilter(Control ctrl, Action<MouseEventArgs> onMouseWheel)
{
_ctrl = ctrl;
_onMouseWheel = onMouseWheel;
}
public bool PreFilterMessage(ref Message m)
{
var parent = _ctrl.Parent;
if (parent != null && m.Msg == 0x20a) // WM_MOUSEWHEEL, find the control at screen position m.LParam
{
var pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
var clientPos = _ctrl.PointToClient(pos);
if (_ctrl.ClientRectangle.Contains(clientPos)
&& ReferenceEquals(_ctrl, parent.GetChildAtPoint(parent.PointToClient(pos))))
{
var wParam = m.WParam.ToInt32();
Func<int, MouseButtons, MouseButtons> getButton =
(flag, button) => ((wParam & flag) == flag) ? button : MouseButtons.None;
var buttons = getButton(wParam & 0x0001, MouseButtons.Left)
| getButton(wParam & 0x0010, MouseButtons.Middle)
| getButton(wParam & 0x0002, MouseButtons.Right)
| getButton(wParam & 0x0020, MouseButtons.XButton1)
| getButton(wParam & 0x0040, MouseButtons.XButton2)
; // Not matching for these /*MK_SHIFT=0x0004;MK_CONTROL=0x0008*/
var delta = wParam >> 16;
var e = new MouseEventArgs(buttons, 0, clientPos.X, clientPos.Y, delta);
_onMouseWheel(e);
return true;
}
}
return false;
}
}
}
And then this can used from individual controls like
class MyControl
: Control
{
public MyControl()
{
...
MouseWheelHandler.Add(this, MyOnMouseWheel);
}
void MyOnMouseWheel(MouseEventArgs e)
{
...
}
}
Paul Westcott's answer works great when using a FlowLayoutPanel. My implementation of MyOnMouseWheel event is:
void MyOnMouseWheel(MouseEventArgs e)
{
int ChangeIncrement = (this.panel1.VerticalScroll.SmallChange * 4); //Change the 4 to any positive number to scroll more or less one each scroll event.
if (e.Delta < 0)
{
int NewValue = this.panel1.VerticalScroll.Value + ChangeIncrement;
if (NewValue > this.panel1.VerticalScroll.Maximum)
{
this.panel1.VerticalScroll.Value = this.panel1.VerticalScroll.Maximum;
}
else
{
this.panel1.VerticalScroll.Value = NewValue;
}
}
else if (e.Delta > 0)
{
int NewValue = this.panel1.VerticalScroll.Value - ChangeIncrement;
if (NewValue < this.panel1.VerticalScroll.Minimum)
{
this.panel1.VerticalScroll.Value = this.panel1.VerticalScroll.Minimum;
}
else
{
this.panel1.VerticalScroll.Value = NewValue;
}
}
this.panel1.PerformLayout();
}

Categories

Resources