[C#.NET][Winform][User Control] 使用 IExtenderProvider 擴充控制項屬性 並 增加選擇物件方法
這次我想要為控制項新增一個選擇功能,比如一個Group 控制項裡有很多個CheckBox,一個TreeView裡有很多的Node,要執行"全選、反選、全不選"等動作,每次都將這些動作寫在不同的專案裡時,的確有點耗時且每次寫完都還要測試,這有點壞了味道,為此我們可以利用IExtenderProvider 來增加控制項的屬性,並觸發相關事件,進而達到上述動作。
需要用Enum來表示動作
public enum SelectType
{
None,
Clear,
DeSelect,
All,
}
擴充屬性的內容,到時候Get/Set要用的類別
[TypeConverter(typeof(ExpandableObjectConverter))]
public class SelectControl
{
public Control Control { get; set; }
public SelectType? SelectType { get; set; }
public override string ToString()
{
if (this.Control == null)
return "No Select Control";
else
return string.Format("{0},{1}", this.Control.Name, this.SelectType.ToString());
}
}
Attributr的定義效果如下
擴充元件的外殼
[ProvideProperty("SelectControl", typeof(Control))]
public class SelectContaioner : Component, IExtenderProvider
{
//TODO:
}
TODO裡有兩件事要做,一個是建構式初始化,另一個是CanExtend方法,在這裡我只為Button控制項增加擴充。
#region 實作 IExtenderProvider 成员
public bool CanExtend(object target)
{
//擴充這兩種控制項
if (target is Button || target is Label)
return true;
else
return false;
}
#endregion
#region construct
public SelectContaioner(IContainer Container)
: this()
{
if (Container == null)
{
throw new ArgumentNullException("cont");
}
Container.Add(this);
}
public SelectContaioner()
{
}
#endregion
接下來在擴充SelectContaioner 類別裡,我們已經定義了[ProvideProperty("SelectControl", typeof(Control))],就必須要寫相對應的SetSelectControl/GetSelectControl,這樣才能在VS裡看到這個屬性
public void SetSelectControl(Control Ctrl, SelectControl SelectControl)
{
//TODO:
}
public SelectControl GetSelectControl(Control Ctrl)
{
//TODO:
return new SelectControl();
}
到這裡,編譯後在Winform專案裡加入SelectContaioner 元件後應該就看見SelectControl屬性了
SetSelectControl方法裡,我們必須把有設定擴充屬性的Button控制項,加入Dictionary<Control, SelectControl>集合中,並且為這些控制項註冊Click事件
Dictionary<Control, SelectControl> _SelectControls = new Dictionary<Control, SelectControl>();
public void SetSelectControl(Control Ctrl, SelectControl SelectControl)
{
if (SelectControl.Control is Form || SelectControl.Control is SelectControl)
return;
if (this._SelectControls.ContainsKey(Ctrl))
{
this._SelectControls[Ctrl] = SelectControl;
}
else
{
this._SelectControls.Add(Ctrl, SelectControl);
}
if ((SelectControl.SelectType == SelectType.None) || SelectControl.Control == null)
{
Ctrl.Click -= new EventHandler(Ctrl_Click);
}
else
{
Ctrl.Click += new EventHandler(Ctrl_Click);
}
}
在Ctrl_Click就是按時按下去要幹的事,不同的控制項有不同的選擇方式
void Ctrl_Click(object sender, EventArgs e)
{
if (sender == null)
return;
Control ctrl = sender as Control;
SelectControl selectControl = this._SelectControls[ctrl];
Control currentControl = selectControl.Control;
SelectType? currentSelectType = selectControl.SelectType;
if (currentControl == null)
return;
if (currentControl is TreeView)
{
TreeView treeView = currentControl as TreeView;
treeViewControl(treeView, currentSelectType);
}
else if (currentControl is CheckedListBox)
{
CheckedListBox checkedListBox = currentControl as CheckedListBox;
checkedListBoxControl(checkedListBox, currentSelectType);
}
else
{
//容器控制項
foreach (Control control in currentControl.Controls)
{
if (control is TreeView)
{
TreeView treeView = control as TreeView;
treeViewControl(treeView, currentSelectType);
}
else if (control is CheckBox)
{
CheckBox checkBox = control as CheckBox;
checkBox.Checked = changeSelectModel(checkBox.Checked, currentSelectType);
}
}
}
}
下面這三段表示TreeView控制項裡的選擇,這也利用 yield 把iteration跟process分開,總算逮到應用了http://www.dotblogs.com.tw/yc421206/archive/2010/03/03/13863.aspx
Process~
void treeViewControl(TreeView treeView, SelectType? selectType)
{
foreach (TreeNode treeNode in getTreeNodes(treeView))
{
treeNode.Checked = changeSelectModel(treeNode.Checked, selectType);//process
}
}
Iteration~
IEnumerable<TreeNode> getTreeNodes(TreeView treeView)
{
foreach (TreeNode node in treeView.Nodes)
{
yield return node;
foreach (TreeNode subNode in getTreeNodes(node))
yield return subNode;
}
}
IEnumerable<TreeNode> getTreeNodes(TreeNode treeNode)
{
foreach (TreeNode node in treeNode.Nodes)
{
yield return node;
foreach (TreeNode subNode in getTreeNodes(node))
yield return subNode;
}
}
處理CheckedListBox 的動作
void checkedListBoxControl(CheckedListBox checkedListBox, SelectType? selectType)
{
for (int i = 0; i < checkedListBox.Items.Count; i++)
{
bool selected = changeSelectModel(checkedListBox.GetItemChecked(i), selectType);
checkedListBox.SetItemChecked(i, selected);
checkedListBox.SetSelected(i, selected);
}
}
改便選擇模式
bool changeSelectModel(bool isSelected, SelectType? selectType)
{
switch (selectType)
{
case SelectType.None:
return isSelected;
case SelectType.Clear:
return false;
case SelectType.DeSelect:
return !isSelected;
case SelectType.All:
return true;
default:
throw new NotImplementedException();
}
}
編譯完成之後,再針對每一個Button定義擴充屬性
定義好屬性之後,VS會自動幫我們加入程式碼,我們自己一行都不用寫,爽啦。^______^
範例下載:
System.Windows.Forms.Extend.zip
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET