为什么GroupBox没有MouseMove事件?

4
WinForms中的GroupBox控件不支持MouseMove事件(或者说,至少不是一致的),我不明白为什么。
由于它是从Control继承而来的,因此它具有MouseMove事件,但GroupBox使用显式重新引入了它,因此在属性网格中不显示。您可以在运行时挂钩MouseMove事件,并且有时它可以工作 - 只要FlatStyle保持为Standard。如果将GroupBox的FlatStyle设置为System,则根本不会触发MouseMove事件。
Reflector没有给我任何线索。 GroupBox构造函数似乎没有设置任何奇怪的控件样式,而GroupBox也没有做任何傻事,比如重写MouseMove并未调用base。 这似乎也是WinForms特定的限制,因为Delphi组框完全支持OnMouseMove。 更正:与Delphi的比较无效。Delphi组框实际上不是标准的BM_GROUPBOX控件;它们只是被绘制成看起来像组框,而没有继承此类奇怪的组框行为。因此,尽管我没有在任何地方看到过文档,但这很可能是Windows组框控件的限制。 为什么WinForms中的GroupBox不支持MouseMove事件呢?
4个回答

5
根据此线程,标准的Windows groupbox(即具有BS_GROUPBOX样式的BUTTON控件)似乎会在响应WM_NCHITTEST时返回HTTRANSPARENT。由于该控件声称是透明的,因此Windows会将鼠标移动事件发送到其父窗口。

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

不清楚的是,默认情况下Windows为什么返回HTTRANSPARENT,但至少已经独立确认了这个问题。


2
您可以使用Reflector来查看,关键属性是CreateParams和内部的OwnerDraw属性。GroupBox通常使用OwnerDraw = true操作,除非您将FlatStyle = System设置。然后,您会得到一个老式的Windows group box,其类名为BUTTON,并打开了BS_GROUPBOX样式位。
如果您使用Spy++查看,您会发现该控件根本没有接收到任何鼠标消息。不确定为什么会这样,SDK文档没有提到。我猜这可以追溯到Windows 2.x时期,当时每个周期都很重要。但这解释了为什么MouseMove事件被隐藏,当选择System样式时无法触发它。单击和上/下也是如此。通过设置FlatStyle属性,它还关闭了Control.UserMouse控件样式。
总之,如果您想要鼠标消息,则需要避免使用System样式。

1
我注意到在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
};

我已经在问题中说过了:“您可以在运行时挂钩MouseMove事件,并且有时它会起作用--只要FlatStyle保持标准。如果GroupBox的FlatStyle设置为系统,则根本不会触发MouseMove事件。” - Joe White

0

GroupBox是一个静态控件,用于容纳其他控件。它的设计纯粹是为了将事物“分组”,如果正确布局,则可以使用户界面直观易懂。因此,你可以使用的事件非常少。

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

请查看这里,了解子类化如何工作...好吧,它是用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. 您的事件处理程序应该像这样:
        private void groupBox1_MyMouseMove(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("MyMouseMove!");
        }

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

希望这给你提示, 最好的问候, 汤姆。


我持怀疑态度,因为其他的回答确认 WM_MOUSEMOVE 甚至从未被发送到 groupbox。你测试过这段代码是否真的适用于 GroupBox 吗? - Joe White
@Joe White:请查看我的修改后的代码以及我用来使其工作的步骤。希望有帮助。最好的问候,Tom。 - t0mm13b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接