C#中的自定義控件中的屬性、事件及一些相關特性的總結

今天學習了下C#用戶控件開發添加自定義屬性的事件,主要參考了MSDN,總結並實驗了一些用於開發自定義屬性和事件的特性(Attribute)。windows

在這裏先說一下個人環境:編輯器

操做系統:Windows7旗艦版(Service Pack 1)函數

VS版本:Microsoft Visual Studio Ultimate 2012,版本 11.0.50727.1 RTMREL工具

.NET Framework版本:4.5.50938學習

C#版本:Visual C# 2012測試

1、準備工做

一、創建一個C#窗體應用程序,主窗體起名爲FormMain,向解決方案中再加入一個用戶控件,起名爲TestUserControlthis

二、在TestUserControl中放一個按鈕,取名爲btnTestspa

三、控件作好後,會出如今【工具箱】內操作系統

四、將控件拖拽到一個窗體(Form)上就可使用了,取名testUserControl1。這個名字是VS默認取的,即首字母小寫,最後補上數字做爲序號。設計

2、添加自定義屬性

在TestUserControl類中,添加下面的代碼:

/// <summary>
/// 按鈕名稱
/// </summary>
public string ButtonName
{
    get
    {
        //TODO
        return btnTest.Text;
    }
    set
    {
        //TODO
        btnTest.Text = value;
    }
}

代碼添加完畢後,在FormMain上加入的testUserControl1的屬性中,就會出現BtnName了

3、添加自定義事件

在TestUserControl類中,添加下面的代碼:

/// <summary>
/// 事件
/// </summary>
public event EventHandler BtnTestClick;
/// <summary>
/// 測試按鈕
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTest_Click(object sender, EventArgs e)
{
    if (BtnTestClick != null)
    {
        //TODO
        BtnTestClick(sender, e);
    }
}

代碼添加完畢後,在FormMain上加入的testUserControl1的事件中,就會出現BtnTestClick了

在FormMain的代碼中實現這個函數

private void testUserControl1_BtnTestClick(object sender, EventArgs e)
{
    MessageBox.Show(sender.ToString() + "\r\n" + e.ToString());
}

這時運行程序,點擊控件testUserControl1內的按鈕btnTest,就會有下面的效果:

4、幾個特性(Attribute)

1)DefaultEvent和DefaultProperty:指定自定義控件的默認事件和默認屬性

DefaultEventAttribute(MSDN)能夠用來指定組件的默認事件,如在TestUserControl類上面加入代碼

[DefaultEvent("BtnTestClick")]

那在Form編輯界面,雙擊控件testUserControl1就會自動進入testUserControl1_BtnTestClick事件。

這裏再說明一下,C#中的System.Windows.Forms.Control類代碼以下:

[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ComVisible(true)]
[DefaultEvent("Click")]
[DefaultProperty("Text")]
[Designer("System.Windows.Forms.Design.ControlDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
[DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.Design.Serialization.CodeDomSerializer, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
[ToolboxItemFilter("System.Windows.Forms")]
public class Control : Component, IDropTarget, ISynchronizeInvoke, IWin32Window, IBindableComponent, IComponent, IDisposable
{ /* ... */ }

這裏能夠看到DefaultEvent的值爲「Click」,這也就是爲何拖入Form的按鈕(Button),在雙擊後會進入它的Click事件:

private void button1_Click(object sender, EventArgs e)

對於不但願以Click事件做爲默認事件的控件來講,要手動指定該控件的DefaultEvent特性,如複選框(CheckBox)的聲明:

[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ComVisible(true)]
[DefaultBindingProperty("CheckState")]
[DefaultEvent("CheckedChanged")]
[DefaultProperty("Checked")]
[ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem,System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public class CheckBox : ButtonBase
{ /* ... */ }

這裏的DefaultEvent被寫上了「CheckedChange」,所以在Form的編輯界面,雙擊複選框時默認進入的編輯事件爲

private void checkBox1_CheckedChanged(object sender, EventArgs e)

自定義的控件(直接繼承自UserControl),若是不添加這個屬性,在編輯界面雙擊後進入的事件是Load事件。

相似的特性還有DefaultProperty,DefaultPropertyAttribute(MSDN)能夠被用來指定組件的默認屬性。指定默認屬性後,當用戶在Form裏單擊這個控件時,將在屬性瀏覽窗口中自動選定該屬性:

[DefaultProperty("BtnName")]

2)Browsable:設置控件某一屬性或事件是否出如今「屬性」窗口中

BrowsableAttribute(MSDN)指定某一屬性或事件是否應在「屬性」窗口中顯示,如在屬性BtnName上添加代碼:

[Browsable(false)]

則控件testUserControl1的屬性界面就不會出現BtnName的設置了,下圖紅線部分爲以前BtnName所在的位置

若是某屬性或事件沒有添加Browsable特性,那麼該屬性或事件也能夠在「屬性」窗口中看到。這裏還要說明如下,Browsable只能決定某屬性或事件在「屬性」窗口內的可見性,Browsable被置爲false的屬性和事件,仍能夠在編輯器中經過代碼中使用。

3)Description:指定控件某一屬性或事件出如今「屬性」窗口中的說明文字

DescriptionAttribute(MSDN)用於指定控件的某一屬性或事件出如今「屬性」窗口中的說明文字

如在BtnName上添加下面代碼:

[Description("設置按鈕上顯示的文字")]

也能夠帶上Browsable特性一塊兒使用:

[Browsable(true)]
[Description("設置按鈕上顯示的文字")]

或寫在一對方括號裏,用逗號隔開:

[Browsable(true), Description("設置按鈕上顯示的文字")]

在「屬性」界面中看到的說明文字,效果以下:

4)EditorBrowsable:指定某一屬性或方法在編輯器中可見

EditorBrowsableAttribute(MSDN)指定某個屬性或方法在編輯器中能夠查看。

EditorBrowsableAttribute的構造函數以下:

public EditorBrowsableAttribute(EditorBrowsableState state);

其中,EditorBrowsableState是一個枚舉(enum),這個枚舉共有三個值,分別是Always、Never和Advanced

Always:該屬性或方法在編輯器中始終是可瀏覽的

Never:該屬性或方法始終不能在編輯器中瀏覽

Advanced:該屬性或方法是隻有高級用戶才能夠看到的功能。 編輯器能夠顯示或隱藏這些屬性

前面兩個都好理解,第三個Advanced着實會讓人一頭霧水(什麼才叫「高級用戶」?)。後來查了一些資料,才知道對於高級成員的可見性,能夠在「工具」菜單下的「選項」中進行配置。

(在這裏感謝大神在social.msdn.microsoft.com上的 解答

若是勾選了「隱藏高級成員」,那麼用代碼「[EditorBrowsable(EditorBrowsableState.Advanced)]」標記的屬性,將不能在IDE中自動顯示。但這也僅僅是不自動顯示而已,若是在代碼中真的調用了不可見的屬性,編譯不會報錯,運行也不會有問題。

以下圖:BtnName被標記爲「EditorBrowsableState.Never」,所以這個屬性不會出如今VS的智能提示(學名叫IntelliSense)中,但若是寫到代碼裏,卻沒有問題。

須要注意的是,這種隱藏只有在該控件代碼爲當前解決方案不可見時有效,也就是說,若是這個控件的實現代碼就在你的解決方案內,EditorBrowsable並不能保證用戶看不見這個屬性。但若是這個控件時被放在一個dll文件中添加引用到當前的解決方案中,EditorBrowsable特性才能按其文字描述中說明的那樣起做用。

5)DesignerSerializationVisibility:代碼生成器生成組件相關代碼的方式

DesignerSerializationVisibilityAttribute(MSDN)用於指定在設計時序列化組件上的屬性時所使用的持久性類型。

參數爲DesignerSerializationVisibility類型的枚舉:

Hidden:代碼生成器不生成對象的代碼

Visible:代碼生成器生成對象的代碼

Content:代碼生成器產生對象內容的代碼,而不是對象自己的代碼

這個說法一眼看上去並不易理解,所以我決定仍是用兩個具體例子說明一下:

一、Hidden與Visible、Content的不一樣

仍是以咱們上面的BtnName屬性爲例,參數爲【DesignerSerializationVisibility.Hidden】的狀況

[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public string BtnName
{
    get
    {
        return btnTest.Text;
    }
    set
    {
        btnTest.Text = value;
    }
}

將控件拖入FormMain的窗體設計器中,可用在文件FormMain.Designer.cs中看到:

/// <summary>
/// 設計器支持所需的方法 - 不要
/// 使用代碼編輯器修改此方法的內容。
/// </summary>
private void InitializeComponent()
{
    this.testUserControl1 = new ControlTest.TestUserControl();
    this.SuspendLayout();
    // 
    // testUserControl1
    // 
    this.testUserControl1.Location = new System.Drawing.Point(33, 46);
    this.testUserControl1.Name = "testUserControl1";
    this.testUserControl1.Size = new System.Drawing.Size(134, 77);
    this.testUserControl1.TabIndex = 0;
    // ...
}

將BtnName上方的特性DesignerSerializationVisibilityAttribute的參數改成【DesignerSerializationVisibility.Visible】或【DesignerSerializationVisibility.Content】後,函數InitializeComponent()中的代碼會有不一樣:

/// <summary>
/// 設計器支持所需的方法 - 不要
/// 使用代碼編輯器修改此方法的內容。
/// </summary>
private void InitializeComponent()
{
    this.testUserControl1 = new ControlTest.TestUserControl();
    this.SuspendLayout();
    // 
    // testUserControl1
    // 
    this.testUserControl1.BtnName = "button1";
    this.testUserControl1.Location = new System.Drawing.Point(36, 32);
    this.testUserControl1.Name = "testUserControl1";
    this.testUserControl1.Size = new System.Drawing.Size(134, 77);
    this.testUserControl1.TabIndex = 0;
    // ...
}

可用看出,區別就在下面這行代碼:

this.testUserControl1.BtnName = "button1";

使用了Hidden就沒有,使用了Visible就會有(使用了Content也會有)

使用了Hidden後,在「屬性」界面中,不管怎麼修改BtnName屬性的值,編譯時編譯器都不會理睬這個值,而是使用默認值(這個例子裏面就是button1)。使用了Hidden後,即便在FormMain.Designer.cs裏手動把上面那行賦值的代碼加上,這行代碼在程序從新編譯後仍是會消失。

二、Visible與Content的不一樣

Content被用在能夠序列化的集合,例如System.Windows.Forms.DataGridView類(數據表格)

//
// 摘要:
//     獲取一個包含控件中全部列的集合。
//
// 返回結果:
//     一個 System.Windows.Forms.DataGridViewColumnCollection,包含 System.Windows.Forms.DataGridView
//     控件中的全部列。
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor("System.Windows.Forms.Design.DataGridViewColumnCollectionEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
[MergableProperty(false)]
public DataGridViewColumnCollection Columns { get; }

IDE只是生成這些屬性中包含組件的代碼,而不會生成屬性自己的代碼。在使用IDE添加各個DataGridViewTextBoxColumn時,各個DataGridViewTextBoxColumn的代碼會被放在FormMain.Designer.cs文件中,而有關Columns屬性自己只會在在函數InitializeComponent()中生成這樣一段代碼:

this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.Column1,
this.Column2,
this.Column3});

6)其餘特性

其餘的特性還有許多(如Localizable被用於指定屬性是否可本地化、DefaultValue用於爲屬性指定另外一個「默認值」等),如只是初步瞭解能夠去查看VS從程序集 System.Windows.Forms.dll中反射出的各控件、控件屬性、控件事件的聲明和摘要(就是聲明上面的綠字),更詳細的描述能夠去參考MSDN。

END

相關文章
相關標籤/搜索