2009-11-10 189 views
3

WinForms GroupBox控件不支持MouseMove(或者至少不是一致的),我不明白爲什麼。爲什麼GroupBox沒有MouseMove事件?

由於它是從Control下降的,它確實有一個MouseMove事件,但GroupBox明確地重新將其與Browsable(false)重新引入,所以它不會顯示在屬性網格中。您可以在運行時鉤住MouseMove事件,有時它可以工作 - 只要FlatStyle保留在Standard中。如果GroupBox的FlatStyle設置爲System,則根本不會觸發MouseMove事件。

反射器沒有給我任何線索。 GroupBox構造函數似乎沒有設置任何奇怪的控件樣式,並且GroupBox不會像重寫MouseMove那樣做任何愚蠢的操作,也無法調用base。

這也似乎是一個WinForms特定的限制,因爲Delphi組框支持OnMouseMove就好了。糾錯:與Delphi的比較無效。 Delphi組框實際上並不是標準的BM_GROUPBOX控件;他們只是被繪到看起來像組框,沒有實際繼承像這樣的奇怪的組框行爲。所以這可能是Windows羣組控件的限制,雖然我沒有看到它在任何地方都有記錄。

爲什麼WinForms GroupBox不支持MouseMove?

回答

4

根據this thread,標準的Windows羣組框(即具有BS_GROUPBOX樣式的BUTTON控件)似乎返回HTTRANSPARENT以響應WM_NCHITTEST。由於控件要求透明,因此Windows會將鼠標移動事件發送到其父窗口。

線程確認,如果你自己處理WM_NCHITTEST並返回HTCLIENT,那麼groupbox將獲得鼠標移動事件。他們使用MFC,但它也可能適用於WinForms。

什麼是不明確的是爲什麼 Windows默認返回HTTRANSPARENT,但至少問題已被獨立確認。

2

你可以看到這與反射器,關鍵屬性是CreateParams和內部OwnerDraw屬性。 GroupBox通常使用OwnerDraw = true,除非設置FlatStyle = System。然後你會得到一個老式的Windows組合框,這個窗口的類名是BUTTON,BS_GROUPBOX風格位開啓。

如果你看看Spy ++,你會發現控件根本沒有得到任何鼠標消息。不知道爲什麼這樣,SDK文檔沒有提到它。我想這可以追溯到Windows 2.x,每個週期都計算在內。但它確實解釋了爲什麼MouseMove事件是隱藏的,當系統風格被選中時它不會觸發。點擊和向上/向下相同。 FlatStyle屬性設置器通過關閉Control.UserMouse控件樣式來真正指出它。

Anyhoo,如果你想要鼠標消息,你需要避免系統風格。

0

組合框是一個靜態控件,它內部包含其他控件。它的設計純粹是爲了將用戶界面「分組」在一起,以使用戶界面直觀,如果佈局正確。因此,您可以代表GroupBox使用很少的事件。

您可能可以創建一個從GroupBox繼承的新類並將其繼承以攔截鼠標移動事件。有一個我以前使用過的非常有用的類,它很容易執行子類併爲MouseMove觸發事件。

看看這個here看看子類是如何工作的......好吧,它是用VB.NET編寫的,但是如果你願意,它可以很容易地轉換成C#,我想象的代碼看起來是這樣的: 注:我已經包含的這段代碼是我的頭頂,所以可能會有錯誤在這...但這是它的要點。

編輯:針對喬·懷特的評論,我已經包含了修改後的代碼,它併發送WM_MOUSEMOVE ......看看下面的臺階上我怎麼轉載此2008專業VS之下。

 
    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!「。

希望這給你提示, 最好的問候, 湯姆。

+0

我很懷疑,因爲其他的反應證實了'WM_MOUSEMOVE'甚至從未被髮送到組框。你有沒有測試過,看看這段代碼是否可以用GroupBox工作? –

+0

@Joe White:請參閱我修改後的代碼以及我用於使其工作的步驟。 HTH。最好的問候,湯姆。 – t0mm13b

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 
}; 
+0

我已經在問題中說過:「你可以在運行時掛鉤MouseMove事件,有時它可以工作 - 只要FlatStyle保留在Standard。如果GroupBox的FlatStyle設置爲System,則不會觸發MouseMove事件所有。」 –

相關問題