Mouse wheel scrolling Toolstrip menu items

后端 未结 4 1366
梦如初夏
梦如初夏 2021-01-14 13:48

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 u

相关标签:
4条回答
  • 2021-01-14 13:54

    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.

    0 讨论(0)
  • 2021-01-14 14:02

    A working solution:

    1. Register for MouseWheel event of your form and DropDownClosed event of your root MenuStripItem (here, rootItem) in the Load event of the form

          this.MouseWheel += Form3_MouseWheel;
          rootItem.DropDownOpened += rootItem_DropDownOpened;
          rootItem.DropDownClosed += rootItem_DropDownClosed;
      
    2. Add the code for Keyboard class which simulate key presses

      public 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);
          }
      }
      
    3. 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();
              }
          }
      }
      
    0 讨论(0)
  • 2021-01-14 14:04

    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);
        }
    }
    
    0 讨论(0)
  • 2021-01-14 14:11

    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}" );         
        }
    }
    
    0 讨论(0)
提交回复
热议问题