個人檔案

ID:tsdn
暱稱天空部落測試員
生日1973/01/4
地區臺中縣

自由欄位
BlogAD

博客秀

部落格
AD

台灣檜木精油
Blog Look Score and Rank
人氣指數
當日人次:
累積人次:
搜尋此頻道內容
搜尋:
我推薦誰
誰推薦我
誰來我家
RSS 訂閱
RSS2
ATOM
贊助商
其它資訊
本部落所刊登之內容,皆由作者個人所提供,不代表 yam天空部落 本身立場。
POWERED BY
POWERED BY
會員登入免費註冊
January 3, 2006
 在本文中我們將導覽從專案啟始的所有的方法到控制項包含到 Visual Studio 工具箱之中,來介紹建立一個自製的Windows 架構的控制項的過程。
DividerPanel是一個自 Panel 控制項繼承,加入了可選擇的邊框呈現及框線樣式功能的簡易控制項。
在完成這 tutorial 之後,應該對於類別繼承方面有基本的認識,包括建立自訂屬性(custom properties),覆寫基底方法(methods),使用屬性註解(property comments),建立一個工具箱圖示,建立一個簡易的designer類別及整合一個自製控制項到 Visual Studio 工具箱之內。
沿著本文我們將討論一些好的練習和在Visual Studio中幫助簡化控制項開發的一些捷徑細節。

環境 : Visual Studio.NET 2005
語言 : C#
需要 :  NET Framework 2.0 SDK


 

下載 source files - 40.7 Kb

Divider Panel Tuorial Part I- Creating a custom Windows Forms control from Start to Toolbox

內容目錄

建立一個新方案

Creating a new Solution

當為了開發一個控制項在 Visual Studio 2005 中建立一個新專案時,通常是以一個新空白的方案啟始,
不直接跳到專案精靈與啟始一個新的控制項庫專案。
這麼做的原因是,我們能在方案裡建立多個專案-這可以讓我們將測試應用程式與控制項程式庫保持成
個別專案,以及增加容易分享的能力連結類別及包含到全域方案項目中。

  1. 建立一個新專案,主選單中選擇 File > New > Project...
  2. 在 [New Project 對話盒] 的 Project types 中選擇
      Other Project Types -> Visual Studio Solutions
  3. Templates 中選擇 Blank Solution
  4. 輸入Windows Forms Divider Panel 作為方案名稱。
  5. 點擊 [OK] 鍵 關閉 New Project 對話盒

建立 c# 空白方案

 

在新方案建立之後, 我們可從 Solution Explorer (方案總管)視窗內的方案標題欄位上按下滑鼠右鍵然後從選單中
選擇 Add > New Project ,建立一個新專案。

加入新專案

Add New Project 對話盒開啟時, 選擇 Windows Control Library 項目,
然後輸入 DividerPanel 作為專案名稱。

新增c#之Windows架構的控制項程式庫專案

專案精靈將會建立控制項程式庫連同兩個預設的檔案: UserControl1.cs 及 AssemblyInfo.cs。
為了這 tutorial 因此請刪除 UserControl1.cs 然後新增一個空的類別來建立一個控制項。

Solution Explorer (解決方案資源管理器)視窗內選取 UserControl1.cs 項目然後按下滑鼠右鍵
從選單中選擇 [Delete] 將它刪除並且從此專案中移除。

Adding a new class

接下來,在Solution Explorer (解決方案資源管理器)視窗內的 DividerPanel project 項目上按下右鍵,
從選單中選擇 Add > Class...  ,然後在 Add New Item 對話盒中的Name欄位中輸入 DividerPanel.cs
並且按下 ADD 鍵,新增一個空類別到專案裡。


Add New Item 對話盒

 

從一個已存在的控制項繼承

Inheritance是使程式設計能物件導向的主要因素之一。
當我們從一個已存在的類別繼承時,我們自動的獲得所有base classes' 的功能並且可以依據原有的功能
得再延伸創造出另一個特殊的類別。
所有的 Windows Forms controls都是從 System.Windows.Forms.Control 繼承,它包含了 一個 Form
上的控制項所需的所有基本的屬性(properties)及方法(methods)。

從一個已存在的控制項繼承是很快速的 - 我們只要在類別宣告列上加上基底類別:

public class DividerPanel : System.Windows.Forms.Panel
{

 }

對於我們的 DividerPanel 控制項,我們已經指定了從標準的System.Windows.Forms Panel.control
繼承它的基礎功能。
我們的新的控制項現在已有了 Panel 控制項所有的屬性(Properties )和及方法(Methods)-我們現在
可以把自己自訂的屬性加入,並且覆寫(override)一些 Panel 控制項原有的方法(Methods)來實行
我們自訂的功能。
 

加入屬性(Properties)到控制項

為了要實行我們的 DividerPanel,我們將需要增加二個新的屬性:BorderSideBorder3DStyle
對於 BorderSide 屬性我們將使用 System.Windows.Forms.Border3DSide 作為該屬性的資料型別,
Border3DStyle 屬性我們將使用 System.Windows.Forms.Border3DStyle 作為該屬性的資料型別。

// This system of private properties with public accessors is a
            // best practice coding style.
            // Note that our private properties are in camelCasing -
            // the first letter is lower case, and additional word 
            // boundaries are capitalized.
            private System.Windows.Forms.Border3DSide borderSide;
            private System.Windows.Forms.Border3DStyle border3DStyle;
            // Next we have our public accessors, Note that for public accessors
            // we use PascalCasing - the first letter is capitalized and additional
            // word boundaries are also capitalized.
            public System.Windows.Forms.Border3DSide BorderSide
            {
            get { return this.borderSide; }
            set
            {
            if( this.borderSide != value )
            {
            this.borderSide = value;
            this.Invalidate();
            }
            }
            }
            public System.Windows.Forms.Border3DStyle Border3DStyle
            {
            get { return this.border3DStyle; }
            set
            {
            if( this.border3DStyle != value )
            {
            this.border3DStyle = value;
            this.Invalidate();
            }
            }
            }
            

在上述的程式碼中我們首先定義二個私有的屬性:borderSide 和 border3DStyle -這些是在類別裡
使用的變數。
二個公開的屬性,使用get及set 兩個存取子,get 存取子傳回類別中的私有成員變數值。
另外 set 存取子首先檢查新設定值是否與目前的數值相同,若不同則更新私有成員變數則
呼叫 Invalidate() method 重畫控制項。

這裏再給大家介紹一點小知識

1.大家應該養成良好的編碼規範,就拿物件命名來說,很多人習慣了使用其他語言的命名規則
來命名C#的變數,例如他就把我們的borderSide命名為 m_BorderSide,雖然這樣看來也沒什麼
不妥之處,但是當你使用this.***智慧感應機制的時候,問題來了,他就不智慧,大家不信可以
試一試,既然這個IDE為我們提供了如此方便快捷的方法, 能提高很多效率,為何不用呢,我想
沒有人跟自己過不去。

2.還有一點小技巧,當你輸入你的變數的時候,比如我們的borderSide這個變數,你不必全部
輸入,當你輸入bor還沒有輸完的時候,按下Alt鍵加方向右鍵,你就會奇跡般的發現你的變數
borderSide已經出現在螢幕上了,這對初學者來說是個神奇的功能吧……

 

定義的變數要直觀的表示它的含義 通常在建構子中為我們的變數賦初值,還有其他的初始化
工作通常也在建構子中完成,好了繼續我們的程式,現在我們的建構子內容如下:
// This is the Constructor for our class. Any private variables 
            // we have defined should have their initial values set here. It 
            // is good practice to always initialize every variable to a specific 
            // value, rather then leaving it as an inferred value. For example, 
            // all bool's should be set to false rather than leaving them as an 
            // inferred false, and any objects referenced that are initially null 
            // should be explicitly set to null. (eg. myObject = null)
            public DividerPanel()
            {
            // Set default values for our control's properties
            this.borderSide = System.Windows.Forms.Border3DSide.All;
            this.border3DStyle = System.Windows.Forms.Border3DStyle.Etched;
            }
            

 

覆寫繼承方法(Methods)

當我們要覆寫(override)在基底類別內的一個方法(method)時,CLR 將會執行你的程式碼來取代包含
在基底類別相對應方法((method))的程式碼。
為了要把框線加入到 DividerPanel,因此我們必須覆寫基底的 Panel 控制項的 OnPaint 方法。
一旦我們覆寫了一個基底類別方法,我們依然可能藉由使用 base 關鍵字來呼叫基底類別的
原始方法(method)。 

覆寫方法

  1. 使用 View Code 命令開啟 [程式碼編輯器] 中的 DrividerPanel.cs。
  2. DividerPanel 類別的專屬行右括號前輸入 override,然後按空格鍵。
    此時會出現含所有您可以覆寫的有效基底類別成員之清單方塊。
    注意   您可以按 ESC 或繼續輸入,以略過本選項。
  3. 選取 OnPaint 方法,再按 ENTER,會產生下列程式碼:

overriding methods

IntelliSense 的設計目的,是協助您覆寫繼承的成員。當您輸入 override 然後按空格鍵時,IntelliSense 會顯示所有在快顯清單方塊中,您可以覆寫的有效基底類別成員。

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
            {
            // allow normal control painting to occur first
            base.OnPaint(e);
            // add our custom border
            System.Windows.Forms.ControlPaint.DrawBorder3D (
            e.Graphics,
            this.ClientRectangle,
            this.border3DStyle,
            this.borderSide );
            }

解說上面程式碼意義:

base.OnPaint(e)呼叫基底類 別的OnPaint函數,它可以幫我們完成很多繁瑣的控制項繪製工作,
當基底類別的控制項繪製完成之後我們接著
呼叫System.Windows.Forms.ControlPaint.DrawBorder3D 繪圖函數,
在Panel的上面繪製我們自定義的邊框樣式,參數就是我們先前定義的樣式
另外給大家個小提醒吧System.Windows.Forms.ControlPaint.值得你好好研究,
它提供了很多靜態的方法,用來windows樣式的控制項,
比如Button、CheckBoxes, RadioButtons, Grids 到此為止我們的控制項似乎已經完成了,
但是不能高興得太早,距離成功我們還有很多的事情要做呢。

加入屬性描述(Property Descriptions)及說明文件支援

為了使我們的控制項和 ToolBox 裏面提供的沒有多大區別,我們要做得更加完善,就像下圖所示,
對我們的屬性要有描述和支援

Adding property descriptions

我們將使用下面的程式碼來修改 public accessors 。
Visual Studio 藉著 <summary> 列來產生 Xml 文件檔案- 這裏還有一點小的技巧,
當你在一個函數前面輸入“
///”的時候,VS 2005自動為你產生一個描述的格式,
它自動把函數參數什麼的都搞好了,你需要做的只是添加必要的說明。大家試一下便知道。

/// <summary>
            /// Specifies the sides of the panel to apply a three-dimensional border to.
            /// </summary>
            [Bindable(true), Category("Border Options"),
            DefaultValue(System.Windows.Forms.Border3DSide.All),
            Description("Specifies the sides of the panel to apply a
            three-dimensional border to.")]
            public System.Windows.Forms.Border3DSide BorderSide
            {
            get { return this.borderSide; }
             set
            {
            if( this.borderSide != value )
            {
            this.borderSide = value;
            this.Invalidate();
            }
            }
            }
            /// <summary>
            /// Specifies the style of the three-dimensional border.
            /// </summary>
            [Bindable(true), Category("Border Options"),
            DefaultValue(System.Windows.Forms.Border3DStyle.Etched),
            Description("Specifies the style of the three-dimensional border.")]
            public System.Windows.Forms.Border3DStyle Border3DStyle
            {
             get { return this.border3DStyle; }
             set
            {
            if( this.border3DStyle != value )
            {
            this.border3DStyle = value;
            this.Invalidate();
            }
            }
            }

Bindable(true) - 當設定為 true 時,屬性有任何改變將會立即被通知,
這就是在設計期你能預覽效果的原因

Category("Border Options") -  Category attribute 指定屬性應該在 Properties 頁的那一個分類群組中。
如果未設定 Category attribute 則新加入的屬性將被放在 "Misc" 分類群組中。

DefaultValue(System.Windows.Forms.Border3dSide.All) - 這 attribute 是設定屬性的預設值,
所指定的預設值將會以高亮度及加粗字體顯示,如上圖中的BorderSide的預設值 "Top"

Description("Specifies the sides of the panel to apply a three-dimensional border to.") -
 Description 值是被顯示在 properties 頁的最下方, 當某一個屬性被選取時,它的Description 值
便會被顯示

加入工具箱(Toolbox)支援

為我們的控制項加入工具箱支援是非常簡易容易做到的事,我們可以建立一個點陣圖(BMP)
來作為我們的控制項在工具箱顯示的圖示,然後設定一些attributes 以便讓 Visual Studio 知道
該如何在工具箱上顯示的我們的控制項。
 

  1. 在方案總管內的 DividerPanel 專案上按下滑鼠右鍵,從選單選擇  Add > New Item
    為我們的控制項建立一個圖示。
    Adding a Toolbox icon
     
  2. 開啟對話盒後選擇 Bitmap File,並且命名為DividerPanel.bmp

     
  3. 在 DividerPanel.bmp  的Bitmap Editor 屬性頁上將 HeightWidth 設為 16, Colors 屬性設為 16。


    Now paint your icon bitmap and save it:

    Painting a Toolbox icon


     

  4. 在方案總管中選取DividerPanel.bmp ,然後切換至 Properties 視窗將 Build Action 屬性
    設定為 Embedded Resource

     
  5. 最後一步驟是把二個新的attributes 加入到我們的 DividerPanel 類別中,讓編譯器知道
    控制項關聯點陣圖並且允許包含到 Visual Studio 工具箱中:
[ToolboxItem(true)]
            [ToolboxBitmap(typeof(DividerPanel))]
            public class DividerPanel : System.Windows.Forms.Panel
            {
            }

在上面的程式碼片斷中的第一行是是設定允許這個類別加入成為工具箱中的一個項目,
第二行是告訴編譯器將 DividerPanel.bmp 檔案與我們的控制項關聯,該  attribute所指定的名稱
只是點陣圖的資源名稱,所以不需加入 .bmp 副檔名。

另外值的注意的是工具箱的圖示必須是點陣圖(.bmp) 以及最大尺寸是 16x16 ,16色。 

加入一個簡易的Designer 類別

現在我們應該可以編譯控制項,將它加入到工具箱之中,並且可以開始任意的拖曳到表單上,
但是還有一個潛在的問題需要提出:
從 Panel 控制項所繼承的BorderStyle 屬性可被設定成 None, FixedSingle 或 Fixed3D -
這將與我們新加入的屬性造成混淆,因此我們必須移除 BorderStyle 屬性。
透過建立一個簡易的 designer 類別來過濾屬性清單,這樣便能將 BorderStyle 從 properties 視窗中移除。

  1. 在方案總管內的 DividerPanel 專案上按下滑鼠右鍵,從選單中選擇 Add > Class
  2. 開啟 Add New Item 對話盒後,輸入 DividerPanelDesigner.cs 作為類別名稱然後按下 OK
  3. 為了實行我們的 designer 類別 , 我們的專案必需從 framework參考 System.Design.dll,
    在方案總管內的 DividerPanel 專案>References 上按下滑鼠右鍵,從選單中選擇 Add Reference
    然後在 Add Reference 對話盒中,選擇 System.Design.dll 後按下OK
    Adding a reference to System.Design.dll
     
  4. 開啟 DividerPanelDesigner.cs,然後在  Class 宣告列後填入
    System.Windows.Forms.Design.ScrollableControlDesigner :
    class DividerPanelDesigner :  System.Windows.Forms.Design.ScrollableControlDesigner
                    {
                    }
  5. 覆寫 PreFilterProperties method,在 method中填入移除 BorderStyle 屬性的程式碼:
    protected override void PreFilterProperties(System.Collections.IDictionary  properties)
                    {
                    properties.Remove("BorderStyle");
                    }
  6. 最後把一個 designer attribute 加入到我們的 DividerPanel 類別,讓它與我的 designer 類別連接:
    [ToolboxItem(true)]
                    [ToolboxBitmap(typeof(DividerPanel))]
                    [DesignerAttribute(typeof(DividerPanelDesigner))]
                    public class DividerPanel : System.Windows.Forms.Panel
                    {
                    }

 

Assembly Attributes & Signing

 

開啟 AssemblyInfo.cs 檔案然後提供一些資料來定義組件屬性 (assembly attributes) :

[assembly: AssemblyTitle("Divider Panel")]
            [assembly: AssemblyDescription("A Panel control with selectable border appearance")]
            [assembly: AssemblyConfiguration("")]
            [assembly: AssemblyCompany("TSDN")]
            [assembly: AssemblyProduct("Divider Panel Tutorial")]
            [assembly: AssemblyCopyright("Copyright (c) 2006-2008 TSDN")]
            [assembly: AssemblyTrademark("")]
            [assembly: AssemblyCulture("")]
// Flagging your assembly with the CLS Compliant attribute
            // lets the Framework know that it will work with all
            // CLS Compliant languages, and theoretically with all 
            // future framework platforms (such as Mono on Linux).
            // If possible you should avoid using any non-CLS compliant code 
            // in your controls such as unsigned integers (uint) and
            // calls to non-framework assemblies.
            [assembly: System.CLSCompliant(true)]
            // The ComVisible attribute should always be explicitly set for controls.
            // Note that in order for your control to be consumed by
            // COM callers, you must use parameter-less constructors
            // and you cannot use static methods.
            [assembly: System.Runtime.InteropServices.ComVisible(true)]

在我們使用這個控制項之前,最好註冊我們的組件,這是一個好的習慣,能防止程式被修改
首先我們必須使用 Visual Studio 所提供的強式名稱(Strong Name) 公用程式來為我們的組件建立一對
新的 公開/私密的金鑰。
 

Using the Strong Name utiltiy to create a new key pair

  1. [開始] 選單選擇
      [程序集 > Microsoft Visual Studio 2005 > Visual Studio Tools > Visual Studio 2005 Command Promp]
    開啟命令列視窗。
  2. 在命令列上輸入:     SN  -k  [輸出檔名].snk
  3. 將產生出來的檔案放在專案目錄下。
  4. 滑鼠雙擊在方案總管(Solution Explorer)內的專案下的Properties
    開啟Project Designer視窗切換至 Signing 頁。
  5. 選取 Sign the assembly 核取方塊。
  6. Choose a strong name key file 下拉式清單中選擇 <Browse...>
    指定 DividerPanel.snk。

    To sign an assembly using an existing key file
     

 

使用自製控制項

當我們完成上述工作之後,現在可以建置我們的控制項啦,並且把它加入到工具箱之中,
這樣我們便能在所有的應用程式中使用它。
 

  1. Visual Studio 2005 IDE的主選單中選擇 Build > Build  DividerPanel
    建置 DividerPanel.dll 。
  2. 接下來我們要建立一個新的Windows 應用程式專案,來說明如何在Windows
    應用程式專案中使用我們所自製的控制項,請在方案總管中新增一個Windows
    應用程式專案並且命名為 DividerPanelTest。
  3. 在工具箱視窗上按下滑鼠右鍵,從選單中選擇 Choose Items...
    開啟 Choose Toolbox Items 對話盒。

    Adding a custom control to the Toolbox

     

  4. 按下 Choose Toolbox Items 對話盒中的Browse鍵,從檔案對話盒中
    選取在 DividerPanel\bin\Release 目錄下的DividerPanel.dll檔案。
    Choose Toolbox Items Dialog Box

    選取DividerPanel.dll檔案
     
  5. 此時我們便會在 .Net Framework Components 標籤頁中看到 DividerPanel 的相關資訊


     
  6. 在按下OK鍵關閉Choose Toolbox Items 對話盒之後,我們便會在工具箱視窗中
    看到 DividerPanel 控制項,此時便可以將它從工具箱拖曳到應用程式的表單中。

    Your new control in the Toolox, and ready to use

     

  7. 接下來就等你親自來試試囉 ^_^,886 下回見囉 !

     

摘要

在本文章中我們已經簡略提及下列各項主題:

  • 為控制項專案建立一個空白的方案
  • 從已存在的控制項繼承功能
  • 為控制項類別增加新的屬性(Properties)及存取子(Accessors) 
  • 覆寫繼承方法 ( inherited methods )
  • 增加屬性描述( property descriptions)及文件支援
  • 增加 Visual Studio Toolbox 支援 
  • 簡易的 designer 類別建立與使用
  • 指派 組件(assembly) attributes 及 使用 strong name 工具(SN.exe) 簽署一個程式庫( library )
  • 加入一個控制項到Visual Studio Toolbox

Release History

  • Version 1.0: 29 August 2003 - Initial posting
  • Version 1.01: 12 September 2004 - Edited some terminology, added additional tips.
  • Version 2.0:(2006/01/01)-改使用 Visual Studio 2005 與 NET Framework 2.0 SDK

誰推薦這篇文章
引用 (你可以針對此文寫一篇屬於自己的blog/想法,並給作者一個通告)
引用
相關閱讀
留言 (2筆)
1.
c sir 這vs 2005的解說 實在是太棒了 酷 頂之
我看也要來玩玩 vs 2005
 
jackktop 2006-01-04 09:19:48 留言 |
2.
好厲害喔!
比我會挖~WA
 
flyup 2006-01-04 21:33:30 留言 |
發表你的留言 (字數限制 最多 2000 個中文字)
私密留言:
Name:






內容: