您好,登錄后才能下訂單哦!
本篇內容介紹了“WinForm怎么構建通用的速選組件”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
對于WinForm組件,大家并不陌生。在這里我們將為大家講解WinForm通用速選組件的構建,這也是通往WinForm開發的必經之路。
用戶界面中,需要用戶進行多項選擇時,我們通常會提供一組快速選擇(以下簡稱速選)按鈕:全選、反選、清空,以方便用戶操作。本文章將會構建一個通用速選組件來簡化操作,使用之后,您不需要編寫任何代碼,只需設置兩個屬性便可讓一個控件擁有速選的功能。
WinForm中常見的幾種多選形式如下圖:
圖1
我們暫且將用于顯示選項的控件叫做“選項控件”,全選、反選、清空叫做“速選按鈕”。
四種形式采用了不同的控件用作選項控件、速選按鈕:
選項控件 | 速選按鈕 | |
形式一 | CheckedListBox | Label |
形式二 | CheckBox | LinkLabel |
形式三 | TreeView | Button |
形式四 | DataGridView | PictureBox |
實際使用中還可能會有其它形式的選項控件和速選按鈕,根據使用場合不同,選項控件和速選按鈕可以任意組合。這給我們編程帶來了麻煩,選項控件沒有統一的訪問接口,換一個選項控件就要編寫不同的選擇代碼。
想要將全選、反選、清空的選擇邏輯提取出來還真不容易。于是很多程序員就選擇了針對實際應用的組合(如形式一,CheckedListBox + Label)進行直接編碼。這樣導致大量相似的代碼充斥在項目之中,會帶來以下問題:
重復的編碼、調試、測試(主要是界面)工作;
一旦選項控件發生改變,就必須修改代碼,修改后還要測試;(可能性比較大,可能客戶不喜歡形式一,非要你修改成形式二)
這樣的直接編碼實際上已經違反了DRY原則,我們應該糾正。
面向對象要求我們封裝變化,我們這里不變的是速選的邏輯,變化的是選項控件和速選按鈕,變化是兩個方向的,有點接近橋模式的應用場景了。不過我對設計模式了解不深,不敢冒用,而且感覺這里速選按鈕的變化是比較簡單的,至少它們都有一個Click事件,也可以認為是不變的。
我們采用另外一種途徑,.Net其實已經給我們提供了一種擴展控件功能的方式(也正體現了面向對象的OCP原則),可以讓我們給控件賦予額外的功能。我們來看一個重要的接口:IExtenderProvider 接口,位于System.ComponentModel命名空間下。實現了這個接口的組件有ToolTip、ErrorProvider等,ToolTip、ErrorProvider這兩個組件可以向其它組件(控件是組件的一種)提供額外的功能,ToolStip能讓其它控件在用戶鼠標懸停時彈出一個小框顯示一些提示信息,ErrorProvider則能讓控件顯式錯誤信息。
我們要做的就是創建一個新的組件,實現IExtenderProvider接口,向控件(Button、Label等)提供速選的功能。這個組件我已經完成了,名字叫FastSelect,先不考慮實現原理、如何實現,我們先看下如何使用吧:
FastSelect是一個組件,會自動顯示在工具箱中,將其拖入窗體,將會顯示在設計器下方。
圖2
選中全選按鈕(label1),屬性顯示窗口如下圖:
圖3
分組中出現了一個新的分組:速選,其中有如上圖兩個屬性,***個屬性用于選擇選項控件,第二個屬性用于確定選擇方式(全選還是清空)。設置兩個屬性的值如上圖,完成后label1就可以全選分類中的所有選項了。
簡單說來,只需要向窗體置入一個控件(Button、Label、LinkLabel、Picture),簡單設置兩個屬性,這個控件就自動具有了速選功能,不需要任何代碼。當然得借助FastSelect組件。
這么神奇,是如何實現的呢?要從IExtenderProvider接口說起,這個接口可以向其他組件提供屬性,如上圖中的兩個屬性。有這里有兩點要說明一下:
向其它組件提供屬性,這個可以限定,比如僅只向Button提供。
提供屬性并不是真正給其他組件加上新的屬性,只是在WinForm設計時,在PropertyGrid中顯示額外屬性(后面會詳說)。
我們來看下FastSelect是如何實現的:
public enum SelectionType { 清空, 反選, 全選, } [ProvideProperty("SelectionSource", typeof(Control))] [ProvideProperty("SelectionType", typeof(Control))] public partial class FastSelect : Component, IExtenderProvider { public bool CanExtend(object extendee) { if (extendee == null) return false; if (extendee is Button || extendee is Label || extendee is PictureBox) return true; return false; } [Category("速選"), Description("速選源控件"), Localizable(true)] public Control GetSelectionSource(Control control) { ... } public void SetSelectionSource(Control control, Control selectionSource) { ... } [Category("速選"), Description("速選方式"), DefaultValue(SelectionType.清空), Localizable(true)] public SelectionType GetSelectionType(Control control) { } public void SetSelectionType(Control control, SelectionType selectionType) { ... } ... }
SelectionType枚舉不必多說,我們來看FastSelect,它繼承至Component,實現了IExtenderProvider接口。
CanExtend是IExtenderProvider接口的***成員,它用來標識可以給那些組件進行擴展,上面的代碼中我們限定了只可以給Button、Label、PictureBox進行擴展,也就是說其它類型的控件在屬性窗口中是看不到速選屬性的。CanExtend中沒有LinkLabel,是因為LinkLabel是Label的子類,Label有的它也會自動擁有。
Get(Set) SelectionSource、Get(Set)SelectionType有點類似屬性的get/set吧,只不過多了一個參數(***個參數control),傳入這個參數的是要進行屬性擴展的控件,還是讓我們看一下WinForm生成的代碼吧(在Form1.generated.cs中)
this.fastSelect.SetSelectionSource(this.label1, this.categoriesListBox); this.fastSelect.SetSelectionType(this.label1, SelectionType.全選);
對照圖三,應該明白“提供屬性”的真正含義了吧。
我們再來看是如何實現選擇的,先看代碼片段:
public partial class FastSelect : Component, IExtenderProvider { private Dictionary sourceControlsDict; private Dictionary typeDict; public void SetSelectionSource(Control control, Control selectionSource) { sourceControlsDict.Add(control, selectionSource); control.Click += new EventHandler(control_Click); } public void SetSelectionType(Control control, SelectionType selectionType) { typeDict.Add(control, selectionType); } void control_Click(object sender, EventArgs e) { Control control = sender as Control; Control selectionSource = sourceControlsDict[control]; SelectionType selectType = typeDict[control]; if (selectionSource is DataGridView) { DataGridView dataGridView = selectionSource as DataGridView; foreach (DataGridViewRow row in dataGridView.Rows) row.Selected = ChangeSelected(row.Selected, selectType); } } private bool ChangeSelected(bool isSelected, SelectionType type) { if (type == SelectionType.清空) return false; else if (type == SelectionType.全選) return true; else if (type == SelectionType.反選) return !isSelected; else throw new NotImplementedException(); } }
一個窗體上只需要一個FastSelect組件,但它要為其它多個組件提供屬性,我們這里使用Dictionary保存這些屬性。
設置控件SelectionSource屬性時,其實是調用了SetSelectionSource方法,其中我們為控件注冊了Click事件,control_Click中根據不同的選項控件的類型(DataGridView、CheckedListBox等)和選擇類型進行相應的處理。這樣應該明白了吧。
“WinForm怎么構建通用的速選組件”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。