【问题标题】:Mouse Wheel Event (C#)鼠标滚轮事件 (C#)
【发布时间】:2010-10-03 12:25:56
【问题描述】:

我无法在主窗体中获取鼠标滚轮事件。

作为演示,我想出了一个简单的例子:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        this.panel1.MouseWheel += new MouseEventHandler(panel1_MouseWheel);
        this.panel1.MouseMove += new MouseEventHandler(panel1_MouseWheel);

        Form2 f2 = new Form2();
        f2.Show(this);
    }

    private void panel1_MouseWheel(object sender, MouseEventArgs e)
    {
        if(e.Delta != 0)
        Console.Out.WriteLine(e.Delta);
    }
}

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();

        this.MouseMove += new MouseEventHandler(Form2_MouseMove);
        this.MouseWheel += new MouseEventHandler(Form2_MouseMove);
    }

    private void Form2_MouseMove(object sender, MouseEventArgs e)
    {
        if(e.Delta != 0)
            Console.Out.WriteLine(e.Delta);
    }
}

我在 Form2 中获得了鼠标滚轮事件,但没有在 Form1 中获得任何想法?

干杯,

詹姆斯

【问题讨论】:

  • 鼠标滚轮永恒的挫败感是微软决定将其视为键盘事件而不是鼠标事件,因此鼠标滚轮消息以键盘焦点传递到控件,迫使几乎每个应用程序使用鼠标滚轮做某种变通方法。

标签: c# winforms mousewheel


【解决方案1】:

我怀疑 OP 想要在鼠标悬停在面板上时获取滚动事件,即使面板没有焦点。

这里解释了一种完成此行为的方法:

http://social.msdn.microsoft.com/forums/en-US/winforms/thread/eb922ed2-1036-41ca-bd15-49daed7b637c/

这里:

http://social.msdn.microsoft.com/forums/en-US/winforms/thread/6bfb9287-986d-4c60-bbcc-23486e239384/

取自链接论坛的一段代码sn-ps:

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

这段代码基本上会拦截所有 wm_mousewheel 事件并将它们重定向到鼠标当前悬停的控件。面板不再需要获得焦点来接收滚轮事件。

【讨论】:

  • 这应该被标记为答案。它适用于所有场景,并且可以轻松调整(就像我为个人需求所做的那样。)
  • "m.LParam.ToInt32() & 0xffff" 应该是 "(short)(ushort)(uint)(int)(long)m.LParam" 和 "m.LParam.ToInt32() >> 16" 应该是 "(short)(ushort)(((uint)(int)(long)m.LParam) >> 16)"
  • 也许其中一些强制转换不是必需的 :-) 但这应该可以防止任何强制转换异常。 (int)(long) 将从 64 位 IntPtr 向下转换为 int, (uint) 将使其执行无符号移位(而不是有符号移位), (ushort) 以提取低 16 位,以及 (short) 将其转换为一个有符号的值。
  • @glancep 抱歉,也许我不清楚异常发生在哪里。它只发生在 64 位模式下。如果你有一个 64 位 IntPtr 的值在 int.MinValue 到 int.MaxValue 之外,IntPtr.ToInt32() 将抛出一个溢出异常。
  • 但是,这是一个更好的构造函数。 new Point((int)(long)m.LParam) 绝对比我上面贴的那些乱七八糟的要干净得多。
【解决方案2】:

您的问题源于 form1 有焦点,而不是 panel1。 ...这当然意味着将触发 form1 的事件,而不是 panel1 的事件。

我重新创建了您的场景,并对 Form1 中的构造函数进行了以下更改,并验证它触发了滚轮事件。

public Form1()
{
        InitializeComponent(); 

        /*  --- Old code that don't work ---
            this.panel1.MouseWheel += new MouseEventHandler(panel1_MouseWheel);
            this.panel1.MouseMove += new MouseEventHandler(panel1_MouseWheel);
        */

        this.MouseWheel += new MouseEventHandler(panel1_MouseWheel);
        this.MouseMove += new MouseEventHandler(panel1_MouseWheel);

        Form2 f2 = new Form2();
        f2.Show(this);
    }
}

【讨论】:

    【解决方案3】:

    添加另一个面板事件MouseEnter,并在其回调函数中获取输入焦点:

    void MouseEnterEvent()
    {
       this.Panel.Focus();
    }
    

    【讨论】:

      【解决方案4】:

      感谢@nitrogenycs 的回答,我编写了一个简单的泛型类来轻松解决这个问题:

      using System;
      using System.Windows.Forms;
      using System.Runtime.InteropServices;
      using System.Drawing;
      
      namespace MyNamespace
      {
        public class MouseWheelManagedForm : Form, IMessageFilter
        {
          private bool managed;
      
          public MouseWheelManagedForm () : this (true) {
         }
      
          public MouseWheelManagedForm (bool start) {
            managed = false;
            if (start)
              ManagedMouseWheelStart();
          }
      
          protected override void Dispose (bool disposing) {
            if (disposing)
              ManagedMouseWheelStop();
            base.Dispose(disposing);
          }
      
          /************************************
           * IMessageFilter implementation
           * *********************************/
          private const int WM_MOUSEWHEEL = 0x20a;
          // 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);
      
          private bool IsChild (Control ctrl) {
            Control loopCtrl = ctrl;
      
            while (loopCtrl != null && loopCtrl != this)
              loopCtrl = loopCtrl.Parent;
      
            return (loopCtrl == this);
          }
      
          public bool PreFilterMessage (ref Message m) {
            if (m.Msg == WM_MOUSEWHEEL) {
              //Ensure the message was sent to a child of the current form
              if (IsChild(Control.FromHandle(m.HWnd))) {
                // Find the control at screen position m.LParam
                Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
      
                //Ensure control under the mouse is valid and is not the target control
                //otherwise we'd be trap in a loop.
                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;
          }
      
          /****************************************
           * MouseWheelManagedForm specific methods
           * **************************************/
          public void ManagedMouseWheelStart () {
            if (!managed) {
              managed = true;
              Application.AddMessageFilter(this);
            }
          }
      
          public void ManagedMouseWheelStop () {
            if (managed) {
              managed = false;
              Application.RemoveMessageFilter(this);
            }
          }
      
        }
      }
      

      从那里,你只需要从这个类继承你的表单,而不是你需要“管理”鼠标轮的每个表单:

      using System;
      using System.Collections.Generic;
      using System.Data;
      using System.Linq;
      using System.Windows.Forms;
      
      namespace MyApp
      {
        public partial class MyForm : MyNamespace.MouseWheelManagedForm
        {
          public MyForm ()
          {
            InitializeComponent();
          }
      
        }
      }
      

      希望这会帮助其他人(比我)。

      【讨论】:

      • 仅供参考,您可以使用 this.Contains 而不是 IsChild。
      【解决方案5】:

      面板本身不能获得焦点,只有放置在面板内的项目才能获得焦点。只有在其中放置了某些东西并且该东西具有焦点时,面板才会收到 MouseWheel 事件。只需将鼠标悬停在面板上并移动鼠标滚轮即可将事件发送到表单,而不是面板。

      这是你的两个例子之间的区别。

      【讨论】:

      • 不正确。在我正在编写的应用程序中,我有一个面板,它是我的图形显示空间。虽然您无法点击面板,但您可以说(在代码中)myPanel.Focus() 以便面板将焦点从其他所有内容中移开,以便我的显示代码正常工作.
      • @Jerry - 我已经对此进行了测试,我的测试表明我是对的。如果面板上没有任何内容,即使调用 myPanel.Focus() 也不会改变 MouseWheel 事件的去向。只有当面板上的某些内容具有焦点时,我才能获得 MouseWheel 事件。
      【解决方案6】:

      也许这对你有用?

      public partial class Form1 : Form {
          public Form1() {
              InitializeComponent();
              Form2 f2 = new Form2();
              f2.MouseWheel += new MouseEventHandler(panel1_MouseWheel);
              f2.MouseMove += new MouseEventHandler(panel1_MouseWheel);
              f2.Show(this);
          }
      
          private void panel1_MouseWheel(object sender, MouseEventArgs e)
          {
              if(e.Delta != 0) Console.Out.WriteLine(e.Delta);
          }
      }
      

      【讨论】:

        【解决方案7】:
        this.MouseWheel += pictureBox1_MouseWheel; //tanımlama
        void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
                    {
                        if (Convert.ToString(e.Delta) == "120")
                        {
                            //yukarı
                        }
                        else if (Convert.ToString(e.Delta) == "-120")
                        {
                            //aşağı
                        }
                    }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-08-22
          • 1970-01-01
          • 2014-12-30
          • 2011-07-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多