为什么GroupBox没有MouseMove事件?

时间:2009-11-10 20:03:27

标签: winforms mouseevent groupbox

WinForms GroupBox控件不支持MouseMove(或者至少不一致),我不明白为什么。

由于它来自Control,它确实有一个MouseMove事件,但是GroupBox明确地用Browsable(false)重新引入它,所以它没有显示在Property Grid中。您可以在运行时挂钩MouseMove事件,有时它可以工作 - 只要FlatStyle保留在Standard。如果GroupBox的FlatStyle设置为System,则根本不会触发任何MouseMove事件。

Reflector没有给我任何线索。 GroupBox构造函数似乎没有设置任何奇怪的控件样式,GroupBox没有做任何愚蠢的事情,如覆盖MouseMove并且无法调用base。

这似乎也是WinForms特有的限制,因为Delphi组框支持OnMouseMove就好了。 更正:与Delphi的比较无效。 Delphi组框实际上并不是标准的BM_GROUPBOX控件;它们只是像组框一样被绘制成外观,而没有真正继承像这样的奇怪的groupbox行为。所以这可能是Windows组框控件的限制,但我没有在任何地方看到它。

为什么WinForms GroupBox不支持MouseMove?

4 个答案:

答案 0 :(得分:4)

根据this thread,标准Windows组框(即具有BS_GROUPBOX样式的BUTTON控件)似乎返回HTTRANSPARENT以响应WM_NCHITTEST。由于控件声称是透明的,因此Windows将鼠标移动事件发送到其父窗口。

线程确认,如果你自己处理WM_NCHITTEST并返回HTCLIENT,那么groupbox将获得鼠标移动事件。他们使用的是MFC,但它也可能适用于WinForms。

为什么 Windows默认情况下会返回HTTRANSPARENT,但至少该问题已经过独立确认。

答案 1 :(得分:2)

您可以使用Reflector看到这一点,关键属性是CreateParams和内部OwnerDraw属性。 GroupBox通常以OwnerDraw = true运行,除非您设置FlatStyle = System。然后你得到一个老式的Windows组合框,一个类名为BUTTON且BS_GROUPBOX风格位打开的窗口。

如果你看看Spy ++,你会发现控件根本没有得到任何鼠标消息。不知道为什么会这样,SDK文档没有提到它。我猜这可以追溯到Windows 2.x,每个周期都计算在内。但它确实解释了为什么MouseMove事件被隐藏,它在选择系统样式时无法触发。 Click和Up / Down相同。 FlatStyle属性setter通过关闭Control.UserMouse控件样式来确定它。

Anyhoo,如果你想要鼠标消息,你需要避免系统风格。

答案 2 :(得分:0)

组框是一个静态控件,其中包含其他控件。它的设计纯粹是为了将事物“组合在一起”,以便在正确布局时使用户界面更直观。因此,您可以代表GroupBox使用很少的事件。

您可以创建一个继承自GroupBox的新类,并将其子类化以拦截鼠标移动事件。我之前使用过一个非常有用的类,很容易执行子类化并为MouseMove触发事件。

看一下这个here,看看子类化是如何工作的......好吧,它是用VB.NET编写的,但如果你愿意的话很容易将它翻译成C#,代码我想象看起来像这样: <击> 注意:我所包含的这段代码是我的头脑,因此可能会有错误......但这就是它的主旨。

编辑:为了回应Joe White的评论,我已经包含了修改后的代码,它确实发送了WM_MOUSEMOVE ...请看下面的步骤,了解我如何在VS 2008 Pro下重现这一点。

    public class MyGroupBox : System.Windows.Forms.GroupBox
    {
        private SubClass sc;
        private const int WM_MOUSEMOVE = 0x200;
        public delegate void MyMouseMoveEventHandler(object sender, System.EventArgs e);
        public event MyMouseMoveEventHandler MyMouseMove;
        public MyGroupBox()
            : base()
        {
            sc = new SubClass(this.Handle, true);
            sc.SubClassedWndProc += new SubClass.SubClassWndProcEventHandler(sc_SubClassedWndProc);
        }

        protected override void Dispose(bool disposing)
        {
            if (sc.SubClassed)
            {
                sc.SubClassedWndProc -= new SubClass.SubClassWndProcEventHandler(sc_SubClassedWndProc);
                sc.SubClassed = false;
            }
            base.Dispose(disposing);
        }
        private void OnMyMouseMove()
        {
            if (this.MyMouseMove != null) this.MyMouseMove(this, System.EventArgs.Empty);
        }
        void sc_SubClassedWndProc(ref Message m)
        {
            if (m.Msg == WM_MOUSEMOVE) this.OnMyMouseMove();
        }

    }

    #region SubClass Classing Handler Class
    public class SubClass : System.Windows.Forms.NativeWindow
    {
        public delegate void
          SubClassWndProcEventHandler(ref System.Windows.Forms.Message m);
        public event SubClassWndProcEventHandler SubClassedWndProc;
        private bool IsSubClassed = false;

        public SubClass(IntPtr Handle, bool _SubClass)
        {
            base.AssignHandle(Handle);
            this.IsSubClassed = _SubClass;
        }

        public bool SubClassed
        {
            get { return this.IsSubClassed; }
            set { this.IsSubClassed = value; }
        }

        protected override void WndProc(ref Message m)
        {
            if (this.IsSubClassed)
            {
                OnSubClassedWndProc(ref m);
            }
            base.WndProc(ref m);
        }

        #region HiWord Message Cracker
        public int HiWord(int Number)
        {
            return ((Number >> 16) & 0xffff);
        }
        #endregion

        #region LoWord Message Cracker
        public int LoWord(int Number)
        {
            return (Number & 0xffff);
        }
        #endregion

        #region MakeLong Message Cracker
        public int MakeLong(int LoWord, int HiWord)
        {
            return (HiWord << 16) | (LoWord & 0xffff);
        }
        #endregion

        #region MakeLParam Message Cracker
        public IntPtr MakeLParam(int LoWord, int HiWord)
        {
            return (IntPtr)((HiWord << 16) | (LoWord & 0xffff));
        }
        #endregion

        private void OnSubClassedWndProc(ref Message m)
        {
            if (SubClassedWndProc != null)
            {
                this.SubClassedWndProc(ref m);
            }
        }
    }
    #endregion

  1. 创建一个简单的空白表单。
  2. 从工具调色板中拖出一个组框并将其放入表单中,默认名称为groupBox1
  3. 在表单的设计器代码中,通过执行以下操作更改代码引用:
    System.Windows.Forms.GroupBox groupBox1;
    WindowsApplication.MyGroupBox groupBox1;
  4. InitializeComponent()方法中,将GroupBox的实例化更改为:
    this.groupBox1 = new WindowsApplication.MyGroupBox();
  5. 保存并编译它。
  6. 返回设计器窗口并单击组框,在属性工具箱中查找MyMouseMove事件,然后将其连接起来。
  7. 您的事件处理程序应如下所示:
  8.         private void groupBox1_MyMouseMove(object sender, EventArgs e)
            {
                System.Diagnostics.Debug.WriteLine("MyMouseMove!");
            }
    

    运行应用程序,每次在组框内移动鼠标时,您都会看到输出'MyMouseMove!'。

    希望这会给你提示, 最好的祝福, 汤姆。

答案 3 :(得分:0)

我注意到某些控件的很多事件无法通过VS中的事件选项卡(在“属性”下)访问。您可以在InitializeComponents()下的父窗体设计器中手动分配事件处理程序:

this.groupBox1.MouseMove += new MouseEventHandler(this.groupBox1_MouseMove);

这将触发以下方法:

private void groupBox1_MouseMove(object sender, MouseEventArgs e)
{
    //doodle the stuff you want to happen after the trigger here
};