[C#.NET][Winform][User Control] 使用 IExtenderProvider 擴充控制項屬性 並 增加選擇物件方法

  • 6343
  • 0
  • C#
  • 2013-08-16

[C#.NET][Winform][User Control] 使用 IExtenderProvider 擴充控制項屬性 並 增加選擇物件方法

這次我想要為控制項新增一個選擇功能,比如一個Group 控制項裡有很多個CheckBox,一個TreeView裡有很多的Node,要執行"全選、反選、全不選"等動作,每次都將這些動作寫在不同的專案裡時,的確有點耗時且每次寫完都還要測試,這有點壞了味道,為此我們可以利用IExtenderProvider 來增加控制項的屬性,並觸發相關事件,進而達到上述動作。

image

 

 


需要用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的定義效果如下

image

 

 

擴充元件的外殼


[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屬性了

image


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定義擴充屬性

image

 

定義好屬性之後,VS會自動幫我們加入程式碼,我們自己一行都不用寫,爽啦。^______^

image

 

範例下載:

 

System.Windows.Forms.Extend.zip

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo