----------------更新:2014-04-21---------------
蒙doggo兄指教,得知有更好的方法可以代替蹩腳的0尺寸Button法,即調用窗體的驗證方法Form.Validate(),該方會觸發窗體中焦點控件的Validating事件以驗證數據,達到與0尺寸Button法幾乎相同的效果。先看采用新方法的代碼:
public class ToolStripEx : ToolStrip { protected override void OnClick(EventArgs e) { base.OnClick(e); Form fm = FindForm(); if (fm != null) { fm.Validate(); } } }
之所以說幾乎,是因為還是有一點不同,就是Form.Validate()並不會觸發焦點控件的Leave事件,所以需要該事件的猿友恐怕還得繼續沿用0尺寸Button法或另想他法。
另外發現ToolStrip還有個操蛋的問題,就是上述方法都只對ToolStripButton的Click事件有效,但如果按鈕是分離按鈕ToolStripSplitButton,大家知道,按鈕部分的單擊事件就該用ButtonClick而不是Click,單擊按鈕部分雖然也會先觸發ToolStrip.Click事件進行驗證,但不管驗證結果如何,ButtonClick都會被執行,不像ToolStripButton.Click那樣,驗證不過就不會執行。所以對付ButtonClick,在找到更好的辦法前,我還得在事件處理方法中加判斷才行。真他娘的讓人不省心。
----------------原文:2014-03-24---------------
如題,Winform碼農大概都知道這樣一個問題,就是當輸入焦點仍處在TextBox、DataGridViewCell等控件中時,如果單擊普通Button、CheckBox等控件,那么該驗證的會得到驗證,該提交的會提交,該報錯的會報錯,該被阻止的操作會被阻止。但如果單擊的是工具欄上的項目(如ToolStripButton,之所以說項目而不是控件,你懂的),是不會觸發焦點控件的驗證事件的,而是會直接執行按鈕事件,這樣帶來的影響相信大家深有體會。總之不解決ToolStrip的這個問題我不會幸福。先看辦法:
/// <summary>
/// 工具欄(無右側豎線、無手柄、可觸發其它控件驗證) /// </summary>
public class ToolStripEx : ToolStrip { readonly Button btn;//定義一個用來轉移焦點的控件,如Button
public ToolStripEx() { //初始化並指定控件尺寸為0,0。因為你不會希望這個按鈕被看到
btn = new Button { Width = 0, Height = 0 }; //下面為可選項 //讓工具欄在視覺上更地道。如被按下的ToolStripButton更明顯,否則只有一個慘淡的線框
ToolStripManager.VisualStylesEnabled = false; //不顯示拖曳抓柄
GripStyle = ToolStripGripStyle.Hidden; } //在工具欄獲得句柄后將控件添加進窗體,之所以不在構造函數中做這事是因為那個時候窗體也許還是null
protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); Form fm = this.FindForm(); if (fm != null) { fm.Controls.Add(btn); }//這樣添加后,btn.Location會是0,0
} //在工具欄被碰到時(其實選用其它類似事件也行)將焦點轉移到btn上,以此觸發焦點控件的驗證 //注意雖然是工具欄的Click,但經過實踐點擊其中的子項都會優先觸發該事件 //所以當焦點控件驗證通不過時,不會再執行子項的Click事件,這一點我想是由win32消息機制實現的
protected override void OnClick(EventArgs e) { base.OnClick(e); btn.Focus(); } //可選。把工具欄最右邊的1px豎線K掉,這種瑕疵對於我來說簡直不能忍受,草泥馬微軟,有病
protected override void OnPaint(PaintEventArgs e) { e.Graphics.SetClip(new Rectangle(0, 0, Width - 1, Height)); base.OnPaint(e); } }
辦法很簡單,就是在點擊工具欄時先把焦點移到其它能正常獲得焦點的控件上,以此來觸發先前控件的Leave/Validating/DataError等事件。
其實為了解決這個問題我頗費了一番周折,最開始想到的其實就是這招,但覺得猥瑣了點,作為一個有追求的碼農,我認為應該從消息層面去解決,所以一開始就把這個陰招放在一邊,專心搗鼓消息。開始我認為這個問題的本質是因為,工具欄就像Panel之類的控件,是得不到焦點的控件,不像Button之流,能夠讓其他控件的焦點轉移過來,所以才有這個問題。那么我就想通過調用win32 API,讓工具欄能發出與Button一樣的消息,讓焦點控件受騙,以為點到的是Button,從而驗證自己的數據,移交自己的焦點。經過多番實踐,確實讓工具欄獲得了焦點,讓焦點控件失去焦點,用Spy++看焦點控件接收到的消息也與點擊Button接收到的消息看起來一樣了,但仍然不會觸發驗證,這就扯蛋了~我那個沮喪啊。BTW~其實給工具欄設置SetStyle(ControlStyles.Selectable, true)也可以達到同樣目的,但一樣解決不了問題。
也許是還沒摸透問題的本質,也許是win32消息還是玩不轉~總之是經歷過若干次失敗的嘗試,我不得不放棄高大上的解決辦法,這才回頭來重新拾起猥瑣方案,所以文中辦法其實是妥協的結果,難免心有不甘,等他日機緣到了,我定再次嘗試“正統”的解決辦法。如有路過高人點撥,感激不盡!