<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    superwei

    導航

    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    統計

    常用鏈接

    留言簿(4)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    為 Microsoft Visual Studio .NET 設計器創建可設計的組件

    轉自:http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic8

    Shawn Burke
    Microsoft Corporation
    2000年7月

     

    摘要:Microsoft .NET 組件是以管理代碼編寫并在通用語言運行時上構建的。本文論述了它是如何向開發人員提供了一個全新的混合開發環境,即象 Microsoft Visual Basic 一樣容易,而同時又提供了強大的低級編程能力,與 ATL 或 MFC 更加相關。

    目錄

    簡介

    微軟即將發布的 Visual Studio .NET 將使程序開發人員獲得一個集成開發環境,它不但為開發傳統的 C/C++ 應用程序,而且也為令人振奮的 Microsoft .NET 組件提供了豐富的工具。這些以管理代碼編寫、在通用語言運行時構建的組件向開發人員提供了一個全新的混合開發環境,即象 Microsoft Visual Basic 一樣容易,而同時又提供了強大的低級編程能力,與 ATL 或 MFC 更加相關。隨著以生產效率為中心的管理環境的到來,它可與傳統 COM 組件很好地協同工作。開發人員可以將更多時間花在構建大型組件上,而不用再為內存泄漏、安全和頭文件擔心。

    除了提供 Microsoft .NET Framework 組件的開發外,Visual Studio .NET (VS .NET) 還擁有很多工具,可以讓組件利用 VS .NET 中設計器架構的優勢來設計出在外觀和性能上與 VS .NET 所附帶組件相近的產品。在開發管理組件時,在 VS.NET 設計器中獲得的所有特性都使用組件本身的 .NET Framework,從而獲得設計時與運行時組件之間的緊密集成。

    組件是什么

    很顯然,Microsoft .NET Framework 組件很容易編寫。讓它們與 Visual Studio .NET 設計器一同工作的唯一要求是它們實現 System.ComponentModel.IComponent,即通常表明繼承于 IComponent 的默認應用。IComponent 使組件可跟蹤設計時的信息(如它的容器組件或名稱)或訪問設計器提供的服務。

    讓我們編寫一個簡單的 .NET 組件,它的形式如下:

    using System;
    using System.ComponentModel;
    public class BoolTracker : Component {
    private bool state;
    private EventHandler handler;
    private static object EventValueChanged = new object();
    public BoolTracker() {
    }
    public bool Value {
    get {
    return state;
    }
    set {
    if (this.state != value) {
    this.state = value;
    OnValueChanged(new EventArgs());
    }
    }
    }
    public void AddOnValueChanged(EventHandler h) {
    handler  = (EventHandler)Delegate.Combine(handler, h);
    }
    protected virtual void OnValueChanged(EventArgs e) {
    if (handler != null) {
    handler(this, e);
    }
    }
    public void RemoveOnValueChanged(EventHandler h) {
    handler = (EventHandler)Delegate.Remove(handler, h);
    }
    }
    

    顯然,這個組件不完成什么功能, 但會將它置入 Visual Studio .NET Win 窗體設計器或組件設計器中,即可從屬性瀏覽器中看見它有名稱,也有一個稱為 “Value” 的屬性,使用下拉箭頭可以將值設置為 True 或 False,當值在 True 和 False 之間切換時,可以觸發事件 OnValueChanged

    對于設計器來說,組件只是我們要說明的一半,最重要的部分是屬性,它組成了元數據,元數據是關于類、屬性、事件等的信息。讓我們以 Value 屬性為例。僅作為屬性,就已經有相關的元數據了,例如類型(布爾)、行為(讀/寫)或名稱(“Value”)。使用“反射”對基本元數據進行檢索,即通用語言運行時允許用戶在運行時檢查對象的類型、基本類型、屬性、方法、構造器、字段和訪問級別。所有這些信息都被認為是元數據。

    定制元數據

    定制元數據包括可添加到類或類成員的任意信息段(字段、屬性或方法),實際上是類型本身被特定客戶所識別。對于Visual Studio .NET 設計器來說,定制元數據構成了所有可擴展性的基礎。VS .NET 設計器理解的所有元數據屬性都基于一個名為 System.ComponentModel.MemberAttribute 的類。它提供一個基本類,因此開發器所關心的屬性可以通過它們的類型快速標識。

    通過一個典型實例可以更容易理解這一概念。比如我們不希望 Value 屬性在屬性瀏覽器中顯示。我們可以添加一個元數據屬性 System.ComponentModel.BrowsableAttribute 來控制一個屬性是否可被瀏覽。

     [Browsable(false)]
    public bool Value {
       get {
          return state;
    }
       set {
       if (this.state != value) {
             this.state = value;
             OnValueChanged(new EventArgs());
          }
       }
    }
    

    在指定屬性時,可以將“BrowsableAttribute”縮略為“Browsable”。由 C# 編譯器為我們添加“Attribute”一詞。唯一的限制是如果指定了屬性值,它必須與構造器的屬性類型相符,且該數值必須是常量。在本例中,BrowsableAttribute 有一個單一的布爾型參數“Browsable”的構造器,編譯器把這個元數據屬性綁定到該構造器并創建一個屬性類的實例。如果屬性類瀏覽器獲得了這個對象,它將枚舉出該對象的屬性并忽略“browsable”屬性,因為它以此屬性為標簽。因此看起來該對象沒有屬性。BrowsableAttribute 也可應用于事件。

    Microsoft .NET Framework 擁有豐富的屬性集來控制設計器如何使用組件。這里是其中一些有用屬性的列表,使您在以后的閱讀中更能理解其含義:

    屬性名 說明
    BrowsableAttribute 控制屬性或事件是否顯示在屬性瀏覽器中。
    BindableAttribute 確定屬性是否適合數據綁定器進行綁定。
    CategoryAttribute 指定屬性在屬性瀏覽器中應分組的類別(“Appearance”, “Layout”, “Behavior”,“ Misc”等等)。
    DefaultEventAttribute/ DefaultPropertyAttribute 指定對象的默認事件或屬性。
    HelpAttribute 指定屬性或事件的幫助文件和主題。
    LicenseProviderAttribute 指向為組件提供許可證信息的 LicenseProvider。
    MergablePropertyAttribute 在屬性瀏覽器中當多個組件被瀏覽和選中時,允許或阻止包含某屬性。
    PersistableAttribute 確定在 Win Forms Designer 或 Component Designer 等可視設計器中生成代碼時,屬性值是否應與代碼保持一致。
    PersistContentsAttribute 確定代碼生成是否應回歸到對象的非數值類型屬性以及是否保持代碼與屬性值一致。ICollection 屬性類型是這一應用的典型示例。
    ShowInToolboxAttribute 確定是否允許在工具框中使用這一組件。
    ToolBoxItemAttriubte 指定從工具框中創建類時應使用的 ToolboxItem 類型。

    屬性瀏覽器接口

    你或許注意到屬性瀏覽器已在上一部分中多次提到。這是因為在設計器中組件參與的大量活動發生在屬性瀏覽器中。設計器組織并顯示組件,但是主要是由對象瀏覽器決定允許可以修改它們。在以前的版本中,瀏覽器顯示自帶的 COM 對象,這些對象擁有要顯示的數據類型的已定義子集:數字、字符串、字體、顏色、圖像、布爾值和枚舉值。除此之外,由屬性頁或對話框來完成其它的屬性操作。但是,VS .NET 設計器允許組件設計器來定義屬性瀏覽器如何處理對象的任何屬性類型,以及用戶如何編輯這些類型。

    .NET Framework 使用這些編輯器編輯許多內置類型,例如,System.Drawing.Color、System.Drawing.Font 或 Win Forms 控件的DockAnchor屬性。

    圖 1. 屬性編輯器

    此屬性瀏覽器可擴展架構來提供四種基本功能類型:

    • 值轉換

    • 子屬性

    • 枚舉

    • 下拉/彈出編輯器

    System.ComponentModel.TypeConverter 處理前三個功能類型,源自System.WinForms.Design.UITypeEditor 的編輯器處理最后一個。

    所有在 .NET Framework 中遇到的屬性都有內置的 TypeConverters。讓我們先看一下 TypeConverter 類及其功能,然后再討論特定的示例。下面的代碼是 TypeConverter 的縮減版。它具有許多已列出方法的特色,但是為了簡單起見,將它們忽略,只有那些核心方法被保留下來。所有此處列出的方法都是虛擬的,因此可在需要的時候忽略它們。但是,TypeConverter 是作為一個基類而不是接口來實現的,大多數重要功能已經內置,用戶可以忽略與應用程序相關的部分。

    public class TypeConverter {
       //
       // 值轉換方法。
       //
       // 確定 TypeConverter 可以從特定類型轉換為目標類型。
    public virtual bool CanConvertFrom(
    ITypeDescriptorContext context,
    Type sourceType);
       // 確定 TypeConverter 是否可以從目標類型轉換為特定類型。
    public virtual bool CanConvertTo(
    ITypeDescriptorContext context,
    Type destinationType);
       // 為 TypeConverter 轉換目標類型值中的值。
    public virtual object ConvertFrom(
    ITypeDescriptorContext context,
    object value,
    object[] arguments);
       // 將值從 TypeConverters 目標類型轉換為目的類型。
    public virtual object ConvertTo(
    ITypeDescriptorContext context,
    object value,
    Type destinationType,
    object[] arguments);
       //
       // 實例創建方法。
       //
       
       // 創建目標類型的對象。
    public virtual object CreateInstance(
    ITypeDescriptorContext context,
    PersistInfo persistInfo);
       // 創建目標類型的對象并從給定的 IDictionary 置入它的值。
       
    public virtual object CreateInstance(
    ITypeDescriptorContext context,
    IDictionary propertyValues);
       // 指定 TypeConverter 是否知道如何創建目標類型的對象實例。
       
    public virtual bool GetCreateInstanceSupported(
    ITypeDescriptorContext context);
       // 獲得值的 PersistInfo,
    // PersistInfo 是說明給定值保留狀態的對象。
       
    public virtual PersistInfo GetPersistInfo(
    ITypeDescriptorContext context,
    object value);
       //
       // 子屬性方法。
       //
       // 檢索確實要在屬性瀏覽器中為此類型顯示的任何子屬性。
       
       
    public virtual PropertyDescriptorCollection GetProperties(
    ITypeDescriptorContext context,
    object value,
    MemberAttribute[] attributes);
       // 指定此類型是否應該在屬性瀏覽器中與 '+' 一同顯示,且是否顯示子屬性。
    public virtual bool GetPropertiesSupported(
    ITypeDescriptorContext context);
       //
       // 預定義的/標準值方法。
       
       //
       // 為此類型檢索一個預定義的“標準”值集合。
    public virtual StandardValuesCollection GetStandardValues(
    ITypeDescriptorContext context);
       // 指定 GetStandardValues 的值集合是一個可能或有效值的完整列表。
       
    public virtual bool GetStandardValuesExclusive(
    ITypeDescriptorContext context);
       // 確定 TypeConverter 是否可以返回一個標準值列表。
    public virtual bool GetStandardValuesSupported(
    ITypeDescriptorContext context);
       // 檢查值對于此類型是否是有效值。
    public virtual bool IsValid(
    ITypeDescriptorContext context, object value);
       //
       // 實用方法。
       //
       
       // 按名稱數組指定的屬性順序排列屬性集合。
       
    protected PropertyDescriptorCollection SortProperties(
    PropertyDescriptorCollection props,
    string[] names);
    }
    public interface ITypeDescriptorContext : IServiceObjectProvider {
       // 返回符合此上下文環境的 IContainer。
    IContainer Container { get; }
       // 返回正在檢查的實例。
    // 如果有值傳遞給 TypeConverter,
    // ITypeDescriptorContext::Instance 將返回提供值的對象。
       
    object Instance { get; }
    // 在對從實例返回的對象更改前調用。如果返回 False, 則不能做出更改并應中止。
    bool OnComponentChanging();
       // 在更改后,或已經對從實例返回的對象更改后調用。
    void OnComponentChanged();
    }
    

    正如您看到的那樣,TypeConverter 包含很多內容。還包括 ITypeDescriptorContext 的一個簡要說明。因為它在大多數 TypeConverter 方法中以一個參數形式出現。 ITypeDescriptorContext 允許對服務和組件對象進行訪問,這些組件對象提供傳遞給 TypeConverter 的所有值。

    但是,仔細研究 TypeConverter 會發現它并不象表現的那樣復雜。其功能可分為四組:

    • 值轉換:諸如屬性瀏覽器的客戶機需要經常將一種字符串表示方式方便地轉換為另一種。TypeConverter 為此提供了一種標準方法,以及一種判定給定轉換是否可行的方法。因為字符串之間的轉換最為常見,所以 TypeConvert 為 ConvertFromStringConvertToString(未顯示)提供了更有益的方式。

    • 實例的創建與持續性:TypeConverter 負責創建給定類型的實例。TypeConverter 可使類型的創建者來控制這一過程,而不是強制客戶機來檢查構造器或獲知默認值。按此方式,TypeConverter 也扮演在設計器中通過代碼保持組件的角色。它可以創建與使用 PersistInfo 對象,該對象說明對象狀態的哪一部分與持續性相關(經常取自代碼)以及如何保持它們。

    • 子屬性方法:TypeConverter 允許對象對在屬性瀏覽器中顯示的子屬性進行控制。System.Drawing.Font 就是一例,實例展開時顯示的屬性并不完全匹配 Font 對象本身的實際屬性。它們為明確性和可用性而被更改并重新排序。

    • 標準值:許多類型(如枚舉)都具有定義的值子集。其它類型可以有預定義的全集,但也可以接受其它數值。TypeConverter 允許客戶機檢查這些列表并檢查未在列表中的值是否可以接受。

    TypeConverter 可以通過兩種方式之一與屬性相關聯。通過在類聲明中有 TypeConverterAttribute,類型可以指定它們自己的 TypeConverter。通常這些類讓它們的轉換器成為嵌套類,因此類型和設計時的信息是一個整體。換句話說,屬性也可以通過屬性聲明本身指定 TypeConverter 來忽略與屬性類型相關的 TypeConverter。當然在那里指定的 TypeConverter 必須要知曉特定類型。不能任意指定 TypeConverter,但是這種方法有助于了解給定屬性的顯示方式,添加或刪除子屬性或為不同字符串轉換添加支持。您可以從默認的 TypeConveter 中導出一種類型,并將導出的類型指定為給定屬性的 TypeConverter 來了解這些知識。與屬性瀏覽器接口的另一半涉及屬性編輯器。屬性編輯器的結構遠比 TypeConverter 結構簡單。為了給不依賴于 Win Forms 或不與其連接的編輯器保持開放,并沒有基本編輯器類型。因為編輯器通常采用用戶界面(UI)驅動,.NET Framework 中的所有標準編輯器都源自 System.Drawing.Design.UITypeEditor。

    public class UITypeEditor {
       // 在編輯值時調用。
    public virtual object EditValue(
    ITypeDescriptorContext context, IServiceObjectProvider provider,
    object value);
       // 指定此編輯器是否可以顯示值。
    public virtual bool GetPaintValueSupported(
    ITypeDescriptorContext context);
       // 如果有,則返回此編輯器的用戶界面樣式。
    public virtual UITypeEditorEditStyle GetEditStyle(
    ITypeDescriptorContext context);
       // 當客戶機需要在 UI 上顯示值時調用。
    public virtual void PaintValue(
    ITypeDescriptorContext context,
    object value,
    Graphics canvas,
    Rectangle rectangle);
    }
    public enum UITypeEditorEditStyle {
       
       // 沒有交互的 UI 組件。
    None = 1,
       // 模態 UI 屬性將顯示一個 [...]
    // 來啟動一個對話。
    Modal = 2,
       // 下拉 UI 屬性將顯示向下箭頭的按鈕,并且
       // UI 將處于類似組合框的下拉菜單中。
    DropDown = 3
    }
    // 接口可通過調用 UITypeEditor::EditValue 中的
    // provider.GetServiceObject(typeof(IwinFormsEditorService))
    // 進行檢索。
    public interface IWinFormsEditorService {
       // 關閉當前顯示的下拉菜單。
    void CloseDropDown();
       // 為值的 UI 編輯將給定控件
    // 置于下拉菜單中。
    void DropDownControl(Control control);
       // 啟動模態對話框來編輯屬性值
    DialogResult ShowDialog(Form dialog);
    }
    

    EditorAttribute 與 TypeConverterAttribute 類似, 可在它們應用的類型或某一特定類型的屬性上予以指定,該特定類型將取代在 Type 本身中指定的任何值。

    用戶單擊屬性瀏覽器上的下拉或橢圓按鈕將調用 UITypeEditor.EditValue。這里是在下拉編輯器中 EditValue 的一個典型實現:

    public override object EditValue(
    ITypeDescriptorContext context, IServiceObjectProvider provider,
    object value) {
    object returnValue = value;
    if (provider != null) {
    IWinFormsEditorService edSvc = (IWinFormsEditorService)
    provider.GetServiceObject(
    typeof(IWinFormsEditorService));
    if (edSvc != null) {
             MyUIEditorControl uiEditor =
    new MyUIEditorControl(edSvc);
    edSvc.DropDownControl(uiEditor);
    value = uiEditor.NewValue;
    }
    }
    return value;
    }
    

    PaintValue 允許編輯器顯示可見的特定值的表示。例如,WinForms 對象使用該編輯器編輯圖像、顏色和字體。

    圖 2. 編輯器顯示

    這是一個基于代碼的示例:

    public override void PaintValue(
    ITypeDescriptorContext context,
    object value,
    Graphics canvas,
    Rectangle rectangle) {
    if (value is Color) {
    Color color = (Color)value;
    SolidBrush b = new SolidBrush(color);
    canvas.FillRectangle(b, rectangle);
    b.Dispose();
    }
    }
                   ColorEditor's PaintValue code.
    

    從外部引入:代碼持續性

    與一些過去的設計器不同,.NET Framework 組件的 Win Forms 和其它 VS .NET 設計器只依賴于代碼持續性來形成狀態。沒有不可思議的格式,沒有隱藏的數據,只有簡明的代碼。當然,類似位圖和本地化字符串之類的內容會以二進制的形式與代碼打包,但是組件的狀態及其組成物在代碼中保持連續。在設計器中做改動時會生成代碼。如果您處理代碼,它會被重新分析,更改也會反映到設計器中。

    為了充分利用這一功能,.NET Framework 中的設計器提供了所有的渠道。對于任何類型,所有設計器都要了解以下內容:

    1. 對于持續性,對象狀態的什么信息有意義?

    2. 信息怎樣返回給活動對象?

    這些已經在前面章節中進行了簡要的論述。我們再次強調 TypeConverter 是過程的核心。代碼生成和代碼分析設計器涉及一種稱為 CreationBundle 的特別類型的 PersistInfo。例如:

    [TypeConverter(typeof(IntBoolString.IntBoolStringConverter))]
       public class
    IntBoolString {
          private int intVal;
          private string stringVal;
          private bool boolVal;
          public IntBoolString(string s, int I, bool b) {
             This.intVal = I;
             This.stringVal =s ;
             This.boolVal = b;      
          }
          public bool Bool{
             get {return boolVal;}
             set {boolVal = value;}
          }
          public int Int {
             get {return intVal;}
             set {intVal = value;}
          }
          public string String {
             get {return stringVal;}
             set {stringVal = value;}
          }
          public override string ToString() {
             return intVal + "," + boolVal + "," + stringVal;
          }
          public class IntBoolStringConverter : TypeConverter {
             public override bool CanConvertFrom(
    ITypeDescriptorContext context,
    Type sourceType) {
                return (sourcetType == typeof(string));
             }
    public virtual object ConvertFrom(
    ITypeDescriptorContext context,
    object value,
    object[] arguments) {
                if (value is string) {
                   string stringValue = (string)value;
                   int  intValue;
                   bool boolValue;
    int commaIndex =
    stringValue.IndexOf(',');
                   if (commaIndex != -1) {
                      intValue = Int32.
    Parse(stringValue.
    Substring(0, commaIndex));
                      commaIndex = stringValue.
    IndexOf(',',
    commaIndex + 1);
                      if (commaIndex != -1) {
                         int nextComma = stringValue.IndexOf(',', commaIndex + 1);
                         if (nextComma != -1) {
                            boolValue = Boolean.Parse(stringValue.Substring
    (commaIndex+1,                            nextComma - commaIndex));                         stringValue = stringValue.Substring(nextComma+1);                         return new IntBoolString(intVal, boolVal, stringValue);                      }                   }                }                throw new FormatException("Can't convert"' + stringValue +
    "' to IntBoolString Object");             }          }                       public override PersistInfo GetPersistInfo(ITypeDescriptorContext
    context,
    object value
    ) {             if (value is IntBoolString) {                IntBoolString ibs = (IntBoolString)value;                return new CreationBundle(typeof(IntBoolString), null,                   new CreationArgument[] {                      new CreationArgument(ibs.Int, typeof(Int32)),                         new CreationArgument(ibs.Bool, typeof(bool)),                         new CreationArgument(ibs.String, typeof(string))});             }             return base.GetPersistInfo(context, value);          }       }       public override object CreateInstance(ITypeDescriptorContext                               context, IDictionary propertyValues) {          return new IntBoolString((int)propertyValues["Int"],             (bool)propertyValues["Bool"],             (string)propertyValue["String"]);       }       public override bool GetCreateInstanceSupported(ITypeDescriptorContext
    context) {          return true;       }    }

    使用 CreationBundle 對象的好處是,如果構建器與傳遞來的 CreationArguments 的每一類型都匹配,那么該對象知道如何創建存儲信息的對象。當調用 TypeConverter::CreateInstance 并試圖以這種方式創建和初始化對象時,TypeConverter 的默認實現調用 CreationBundle::Invoke。如果沒有構建器,CreateInstance 調用會使用 IDictionary 定制更為靈活的對象創建。傳入的 IDictionary 對象包含每一個屬性名的非持續性值。

    組件經常包含組成多個對象的屬性值。其它框架經常為此目的使用屬性數組。但使用數組也有一些缺點。例如,調入和調出數組時都需要復制數組,這會帶來性能問題。數組也不能對添加、修改、或刪除值等操作提供智能通知。實際上,如果屬性傳回數組,則還要進行大量的添加和刪除項目的工作。數組還是一個快照值,并且在基本對象改變時也不會更新。

    與此不同,.NET Framework 為此目的使用集合,集合是實現 ICollection 的對象。對象可以創建集合并將其傳給任一對象,基本對象的任何改動都會使引用更新。如果集合被其它對象更改,也會通知該對象。為了使 .NET Framework 設計器使用集合,它們還需要支持 ALL 屬性,該屬性有 “get” 和 “set”,它的類型是一個集合持有的對象數組。例如:

       public class IntCollection : ICollection {
          private int[] values;
          public IntCollection(int[] intValues) {
             this.values = (int[])intValues.Clone();
          }
          public int[] All {
             get {
                return (int[])values.Clone();
             }
             set {
                values  = (int[])value.Clone();
             }   
          }
          public int Count {
             get {
                if (values == null)  {
                   return 0;
                }
                return values.Length;
             }
          }
          [Browsable(false)]
          public object SyncRoot {
             get {
                return this;
             }
          }
          [Browsable(false)]
          public bool IsReadOnly {
             get {
                return false;
             }
          }
          [Browsable(false)]
          public bool IsSynchronized {
             get {
                return true;
             }
          }
       }
    

    .NET Framework 持續性機制可以使集合保持或解除持續性。如果集合由更高級的類型組成,例如上面的 BoolInString 示例類型,那么只需讓與該類型關聯的 TypeConverter 為集合中的每一項創建一個有效的 PersistInfo (特別是為 VS .NET 設計器創建 CreationBundle)。

    組件設計器

    如前所述,.NET Framework 內置的設計器可以滿足大多數主要組件的要求。盡管這樣,.NET Framework 為組件設計者提供了一個可充分擴展的體系結構。所有設計器都基于下面的 System.ComponentModel.Design.IDesigner 接口:

    public interface IDesigner {
    // 與此設計器相關聯的組件。
    IComponent Component {get;}
    // 與此組件相關聯的設計時動詞,
    // 例如用于 TabControl 的“添加標簽”。
    DesignerVerb[] Verbs {get;}
    // 分配設計器使用的資源,
    // 設計器在此調用后不再可用。
    void Dispose();
    // 調用使設計器執行“默認操作”,
    // 通常為響應運行時對組件的雙擊而調用。
    void DoDefaultAction();
    // 用給定組件初始化設計器。
    void Initialize(IComponent component);
    }
    

    正如您所看到的那樣,IDesigner 是直接的。設計者通過 DesignerAttribute 與組件關聯:

     [Designer("MyNameSpace.Design.MyComponentDesigner, MyNameSpace.DLL")]
    Public class MyComponent : Component {
    }
    

    如果 DesignerAttribute 不出現在類中,則類分層結構將被遍歷直至找到開發環境為止。在前面的示例中,將默認的 ComponentDesigner 定位在 Component 基類上,然后會使用它。一些設計器使用 UI。而另一些則不然。對于 ComponentDesigner,您會看到一個表示對象的圖標,因為組件通常沒有 UI。另一方面,Win Forms 控件具有在設計時顯示實際控件的設計器。

    圖 3. 設計時的 Win Form 控件

    請注意不顯示 UI 的控件就是設計器下面的圖標,擁有 UI 的 Win Forms 控件顯示在窗體設計器中,這與運行時一樣。所有這些都構建在 IDesigner 基礎上。通常,控件的設計器會控制其正在設計的控件的 WindowProc(取自 System.WinForms.Design.ControlDesigner 的設計器可以通過簡單地覆蓋 WindowProc 方法來完成此項任務)來執行諸如命中測試這類的復雜任務。然而,對于大多數組件來說,內置的默認設計器應該就足夠了。

    訪問設計器服務和基礎結構

    VS .NET 中的 .NET Framework 設計器開發環境的許多服務和基礎結構組件簡化了復雜的操作,使設計器各部分之間可以互通狀態信息。這些服務總是通過使用 GetServiceObject 方法的 IServiceObjectProvider 來進行訪問。這是一些有趣的設計器服務列表:

    類型

    說明
    IDesignerHost 與任何頂級設計器相關聯的主類。提供方法以添加服務,創建和安放組件,刪除組件,以及將操作批處理化。
    IComponentChangeService 在添加、刪除、重命名、或修改組件時提供通知。
    ISelectionService 設置或獲取當前在設計器中選擇的項目。
    IToolboxService 允許檢查或修改工具框上的項目以及它們的選擇狀態等等。
    IUndoService 提供創建操作的撤銷/重做單元的工具,并管理撤銷/重做棧。
    IHelpService 允許設置幫助主題或調用幫助項目
    IMenuCommandService 允許處理設計器菜單命令和動詞。
    IReferenceService 將設計器引用映射到對象。例如,命名組件“button 1”。

    IDesignerHost 是 VS .NET 中所有設計器的基礎。IDesignerHost 也是一個 IServiceObjectProvider,它可以動態地添加或刪除服務。它還提供方法來創建組件以確保正確安放組件。這里是一個使用 IDesignerHost 創建組件的示例:

    Public class MyDesigner : IDesigner {
       
       //  .
    Private void OnSurfaceDoubleClick(object s, EventArgs e) {
       IDesignerHost host =
          (IDesignerHost)this.Component.Site.GetServiceObject(typeof(IDesignerHost));
    If (host != null) {
       Object newComponent = host.CreateComponent(typeof(MyComponent));
       DoSomethingInterestingWithComponent(newComponent);
    }      
    }
       // ...
    

    為組件授予許可證

    在 .NET Framework 的可擴展模型中,授予許可證的體系結構也可以擴展。為了簡便使用,框架定義了一個內置的、標準許可證授予方法,以控制組件是否許可在設計時使用,但開發人員可以用任何他們認為恰當的方式替換此方案。

    // 向控件添加 LicenseProviderAttribute。
    LicenseProvider(typeof(LicFileLicenseProvider))]
    public class MyControl : RichControl {
    // 創建新的空許可證。
    private License license = null;
    public MyControl () {
    // 向控件的構建器添加驗證。
    license = LicenseManager.Validate(typeof(MyControl), this);
    // 執行其它實例化任務...  }
    public override void Dispose() {
    if (license != null) {
    license.Dispose();
    license = null;
    }
    }
    protected override void Finalize() {
    Dispose();
    base.Finalize();
    }
    }
    

    此示例使用內置的許可支持來授予許可證。LicFieLicenseProvider 只是在與類組合體相同的目錄中,尋找一個名為 <classname>.lic 的文件,其中 classname 是類型全名。String 類型的名稱是 System.String.lic。該文件包含字符串“System.String 是一個許可的組件”。如果找到該文件,LicenseManager.Validate 會返回一個 License 對象,它將與類實例一同分配。

    實現您自己的許可證方案也很容易。只需創建您自己的源于 LicenseProvider 的類并實現自己的 GetLicense 方法。您可能要實現一個基于注冊表的許可方案,其中許可的設計時組件都在注冊表中有一個條目:

    public class RegistryLicenseProvider: LicenseProvider {
    public override License GetLicense(
    LicenseContext context,
    Type type,
    object instance,
    bool allowExceptions) {
          RegistryKey licenseKey = Registry.LocalMachine.
    OpenSubKey("Software\\MyCompany\\ComponentLicenses");
    if (context.UsageMode == LicenseUsageMode.Designtime) {
    if (licenseKey != null && licenseKey.GetValue(type.FullName) != null) {
                   return new RegLicense(this, type);
                }
                if (allowExceptions) {
                   throw new LicenseException(type, instance,
                      "Couldn''t get design-time license for ''"" +
                                              type.FullName + "''");
                }
                return null;
             }
       else {
          return new RuntimeRegLicense(this, type);
       }
          }
          private class RuntimeRegLicense : License {
             public string LicenseKey {
                get {
                   return type.FullName;
                }
             }
             public override void Dispose() {
             }
          }
          private class RegLicense : License {
             private RegistryLicenseProvider owner;
             private Type type;
             public RegLicense(RegistryLicenseProvider owner, Type type) {
                this.owner = owner;
                this.type = type;
             }
             public string LicenseKey {
                get {
                   return type.FullName;
                }
             }
             public override void Dispose() {
             }
          }
          [LicenseProvider(typeof(RegistryLicenseProvider))]
          public class MyControl : Control {
          }
    

    使用該許可證的組件將在注冊表的以下位置中有一個條目:

    HKEY_LOCAL_MACHINE\Software\MyCompany\ComponentLicenses
                      <Type full name>="true"
             
    

    結論

    以管理代碼編寫控件遠遠優于傳統的 C++/COM 方法。Mircosoft 對從瑣碎的編程到 C,從通用語言運行時到重組的 Visual Basic 語言進行根本性的工程改進,使可能遇到的故障大大減少。Mircosoft .NET Framework 是用這些技術與原理來開發的第一個代碼集,對集成設計器的支持是這一方法的關鍵。

    posted on 2007-06-16 20:32 小辭猬 閱讀(844) 評論(0)  編輯  收藏 所屬分類: DoNet

    主站蜘蛛池模板: 日韩精品免费在线视频| 午夜爽爽爽男女免费观看影院| 国产亚洲美女精品久久久2020| 青草草在线视频永久免费| 午夜亚洲www湿好大| 亚洲 综合 国产 欧洲 丝袜| 成人免费在线看片| 99re6在线精品免费观看| 美女被免费网站在线视频免费| 亚洲欧洲国产日韩精品| 亚洲一区二区三区影院 | 免费不卡中文字幕在线| 色偷偷尼玛图亚洲综合| 亚洲小说区图片区| 亚洲精品人成在线观看| 亚洲国产精品无码专区在线观看| 亚洲AV无码一区二三区 | 在线精品亚洲一区二区| 亚洲视频免费观看| 久久精品国产亚洲AV香蕉| 亚洲精品午夜国产va久久| 亚洲bt加勒比一区二区| 国产亚洲一区二区精品| 国产亚洲欧洲Aⅴ综合一区| 国产精品美女自在线观看免费| 毛片a级三毛片免费播放| 国国内清清草原免费视频99| 亚洲免费中文字幕| 免费精品国偷自产在线在线 | 久久久亚洲AV波多野结衣| 亚洲AV无码AV男人的天堂| 亚洲日本va中文字幕久久| 亚洲精品无码不卡在线播HE| 亚洲欧洲成人精品香蕉网| 国产AV无码专区亚洲AV男同| 亚洲AV无码一区二区三区DV| 水蜜桃亚洲一二三四在线 | 无码成A毛片免费| 日日狠狠久久偷偷色综合免费| 久久青草亚洲AV无码麻豆| 国产A在亚洲线播放|