I have some menus that contain many menuitems. Mouse wheel doesn't scroll them. I have to use the keyboard arrows or click the arrows at top and bottom. Is it possible to use the mouse wheel to scroll toolstrip menu items? Thanks
-
manually its not possible .......... – andy Oct 30 '12 at 12:41
4 Answers
You can enable it application wide with this class:
public class DropDownMenuScrollWheelHandler : System.Windows.Forms.IMessageFilter
{
private static DropDownMenuScrollWheelHandler Instance;
public static void Enable(bool enabled)
{
if (enabled)
{
if (Instance == null)
{
Instance = new DropDownMenuScrollWheelHandler();
Application.AddMessageFilter(Instance);
}
}
else
{
if (Instance != null)
{
Application.RemoveMessageFilter(Instance);
Instance = null;
}
}
}
private IntPtr activeHwnd;
private ToolStripDropDown activeMenu;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 0x200 && activeHwnd != m.HWnd) // WM_MOUSEMOVE
{
activeHwnd = m.HWnd;
this.activeMenu = Control.FromHandle(m.HWnd) as ToolStripDropDown;
}
else if (m.Msg == 0x20A && this.activeMenu != null) // WM_MOUSEWHEEL
{
int delta = (short)(ushort)(((uint)(ulong)m.WParam) >> 16);
handleDelta(this.activeMenu, delta);
return true;
}
return false;
}
private static readonly Action<ToolStrip, int> ScrollInternal
= (Action<ToolStrip, int>)Delegate.CreateDelegate(typeof(Action<ToolStrip, int>),
typeof(ToolStrip).GetMethod("ScrollInternal",
System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance));
private void handleDelta(ToolStripDropDown ts, int delta)
{
if (ts.Items.Count == 0)
return;
var firstItem = ts.Items[0];
var lastItem = ts.Items[ts.Items.Count - 1];
if (lastItem.Bounds.Bottom < ts.Height && firstItem.Bounds.Top > 0)
return;
delta = delta / -4;
if (delta < 0 && firstItem.Bounds.Top - delta > 9)
{
delta = firstItem.Bounds.Top - 9;
}
else if (delta > 0 && delta > lastItem.Bounds.Bottom - ts.Height + 9)
{
delta = lastItem.Bounds.Bottom - owner.Height + 9;
}
if (delta != 0)
ScrollInternal(ts, delta);
}
}

- 1,151
- 7
- 18
-
1Should be `ts.Height` not `owner.Height` ? Also, can listen for the `MouseWheel` event rather than using `AddMessageFilter`. Other than that, good stuff! – Loathing Jun 21 '15 at 03:02
-
Whose MouseWheel event would you hook into? The AddMessageFilter allows you to handle all mouse wheel scrolling for all menus in the application without worrying about hooking up events to each one. – Bryce Wagner Jun 23 '15 at 14:39
-
I have an extension method that increases the size of the scroll buttons, so it was a natural place to attach the mouse wheel event. Initially `AddMessageFilter` seemed unnecessary, but on second thought it is a good solution. – Loathing Jun 23 '15 at 21:29
-
works perfectly, except in addition to what Loathing mentioned above i had to change "msg == 0x20A" to "m.Msg ..." and the line "int delta = (short)..." to "int delta = m.WParam.ToInt32() >> 16;" – joreg Oct 30 '15 at 17:43
-
Noticed that the top/bottom buttons do not update when wheeled to either end. – sjlewis Aug 05 '16 at 08:56
A working solution:
Register for
MouseWheel
event of your form andDropDownClosed
event of your rootMenuStripItem
(here, rootItem) in theLoad
event of the formthis.MouseWheel += Form3_MouseWheel; rootItem.DropDownOpened += rootItem_DropDownOpened; rootItem.DropDownClosed += rootItem_DropDownClosed;
Add the code for
Keyboard
class which simulate key pressespublic static class Keyboard { [DllImport("user32.dll")] static extern uint keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); const byte VK_UP = 0x26; // Arrow Up key const byte VK_DOWN = 0x28; // Arrow Down key const int KEYEVENTF_EXTENDEDKEY = 0x0001; //Key down flag, the key is going to be pressed const int KEYEVENTF_KEYUP = 0x0002; //Key up flag, the key is going to be released public static void KeyDown() { keybd_event(VK_DOWN, 0, KEYEVENTF_EXTENDEDKEY, 0); keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0); } public static void KeyUp() { keybd_event(VK_UP, 0, KEYEVENTF_EXTENDEDKEY, 0); keybd_event(VK_UP, 0, KEYEVENTF_KEYUP, 0); } }
Add the code for
DropDownOpened
,DropDownClosed
,MouseWheel
events:bool IsMenuStripOpen = false; void rootItem_DropDownOpened(object sender, EventArgs e) { IsMenuStripOpen = true; } void rootItem_DropDownClosed(object sender, EventArgs e) { IsMenuStripOpen = false; } void Form3_MouseWheel(object sender, MouseEventArgs e) { if (IsMenuStripOpen) { if (e.Delta > 0) { Keyboard.KeyUp(); } else { Keyboard.KeyDown(); } } }

- 13,273
- 10
- 65
- 90
-
Thanks! Is it possible to scroll the view without simulating up and down arrows, as in Visual Studio 2010 ? – Jerry Oct 30 '12 at 22:14
-
You must implement your own MenuStrip, otherwise the only working solution is this – Mohsen Afshin Oct 31 '12 at 06:21
-
This solution works, but is rather clunky as it moves the selection up and down the list rather than scrolling it per se. If the mouse moves over the list while this happens, the selection jumps back under the mouse which gets very messy and disorienting for the user. Note also that you can use SendKeys to make the last event handler much more tidy: `SendKeys.Send((e.Delta > 0) ? "{UP}" : "{DOWN}");`. – Jason Williams Jun 05 '14 at 22:14
-
The MouseWheel event seems to stop firing when either the up or down button is clicked – sjlewis Aug 05 '16 at 07:43
This is very simply using a submenu (ToolStripMenuItem
) of the context menu :
Assuming using a form1
(or UserControl
) and a contextMenuStrip1
:
private void form1_Load( object sender , EventArgs e )
{
//this.MouseWheel -= When_MouseWheel;
this.MouseWheel += When_MouseWheel;
}
void When_MouseWheel( object sender , MouseEventArgs e )
{
if ( this.contextMenuStrip1.IsDropDown ) {
//this.Focus();
if ( e.Delta > 0 ) SendKeys.SendWait( "{UP}" );
else SendKeys.SendWait( "{DOWN}" );
}
}

- 349
- 3
- 6
I modified Mohsen Afshin's answer to click the up/down arrows instead of sending up/down key presses. My application had a ContextMenuStrip
called menu
. Here's the code.
In the initialization:
menu.VisibleChanged += (s, e) =>
{
if (menu.Visible)
{
MouseWheel += ScrollMenu;
menu.MouseWheel += ScrollMenu;
}
else
{
MouseWheel -= ScrollMenu;
menu.MouseWheel -= ScrollMenu;
}
};
The ScrollMenu function:
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
private void ScrollMenu(object sender, MouseEventArgs e)
{
Point origin = Cursor.Position;
int clicks;
if (e.Delta < 0)
{
Cursor.Position = menu.PointToScreen(new Point(menu.DisplayRectangle.Left + 5, menu.DisplayRectangle.Bottom + 5));
clicks = e.Delta / -40;
}
else
{
Cursor.Position = menu.PointToScreen(new Point(menu.DisplayRectangle.Left + 5, menu.DisplayRectangle.Top - 5));
clicks = e.Delta / 40;
}
for (int i = 0; i < clicks; i++)
mouse_event(0x0006, 0, 0, 0, 0);//Left mouse button up and down on cursor position
Cursor.Position = origin;
}
I was having trouble getting the mouse_event
function to click a specific location, so I moved the cursor, clicked, and then moved the cursor back. It doesn't seem the cleanest, but it works.

- 1,120
- 1
- 6
- 15