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

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

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

    superwei

    導(dǎo)航

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

    統(tǒng)計

    常用鏈接

    留言簿(4)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    為 Microsoft Visual Studio .NET 設(shè)計器創(chuàng)建可設(shè)計的組件

    轉(zhuǎn)自:http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic8

    Shawn Burke
    Microsoft Corporation
    2000年7月

     

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

    目錄

    簡介

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

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

    組件是什么

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

    讓我們編寫一個簡單的 .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 窗體設(shè)計器或組件設(shè)計器中,即可從屬性瀏覽器中看見它有名稱,也有一個稱為 “Value” 的屬性,使用下拉箭頭可以將值設(shè)置為 True 或 False,當(dāng)值在 True 和 False 之間切換時,可以觸發(fā)事件 OnValueChanged

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

    定制元數(shù)據(jù)

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

    通過一個典型實例可以更容易理解這一概念。比如我們不希望 Value 屬性在屬性瀏覽器中顯示。我們可以添加一個元數(shù)據(jù)屬性 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”一詞。唯一的限制是如果指定了屬性值,它必須與構(gòu)造器的屬性類型相符,且該數(shù)值必須是常量。在本例中,BrowsableAttribute 有一個單一的布爾型參數(shù)“Browsable”的構(gòu)造器,編譯器把這個元數(shù)據(jù)屬性綁定到該構(gòu)造器并創(chuàng)建一個屬性類的實例。如果屬性類瀏覽器獲得了這個對象,它將枚舉出該對象的屬性并忽略“browsable”屬性,因為它以此屬性為標(biāo)簽。因此看起來該對象沒有屬性。BrowsableAttribute 也可應(yīng)用于事件。

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

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

    屬性瀏覽器接口

    你或許注意到屬性瀏覽器已在上一部分中多次提到。這是因為在設(shè)計器中組件參與的大量活動發(fā)生在屬性瀏覽器中。設(shè)計器組織并顯示組件,但是主要是由對象瀏覽器決定允許可以修改它們。在以前的版本中,瀏覽器顯示自帶的 COM 對象,這些對象擁有要顯示的數(shù)據(jù)類型的已定義子集:數(shù)字、字符串、字體、顏色、圖像、布爾值和枚舉值。除此之外,由屬性頁或?qū)υ捒騺硗瓿善渌膶傩圆僮鳌5牵琕S .NET 設(shè)計器允許組件設(shè)計器來定義屬性瀏覽器如何處理對象的任何屬性類型,以及用戶如何編輯這些類型。

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

    圖 1. 屬性編輯器

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

    • 值轉(zhuǎn)換

    • 子屬性

    • 枚舉

    • 下拉/彈出編輯器

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

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

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

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

    但是,仔細研究 TypeConverter 會發(fā)現(xiàn)它并不象表現(xiàn)的那樣復(fù)雜。其功能可分為四組:

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

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

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

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

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

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

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

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

    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.
    

    從外部引入:代碼持續(xù)性

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

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

    1. 對于持續(xù)性,對象狀態(tài)的什么信息有意義?

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

    這些已經(jīng)在前面章節(jié)中進行了簡要的論述。我們再次強調(diào) TypeConverter 是過程的核心。代碼生成和代碼分析設(shè)計器涉及一種稱為 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 對象的好處是,如果構(gòu)建器與傳遞來的 CreationArguments 的每一類型都匹配,那么該對象知道如何創(chuàng)建存儲信息的對象。當(dāng)調(diào)用 TypeConverter::CreateInstance 并試圖以這種方式創(chuàng)建和初始化對象時,TypeConverter 的默認(rèn)實現(xiàn)調(diào)用 CreationBundle::Invoke。如果沒有構(gòu)建器,CreateInstance 調(diào)用會使用 IDictionary 定制更為靈活的對象創(chuàng)建。傳入的 IDictionary 對象包含每一個屬性名的非持續(xù)性值。

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

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

       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 持續(xù)性機制可以使集合保持或解除持續(xù)性。如果集合由更高級的類型組成,例如上面的 BoolInString 示例類型,那么只需讓與該類型關(guān)聯(lián)的 TypeConverter 為集合中的每一項創(chuàng)建一個有效的 PersistInfo (特別是為 VS .NET 設(shè)計器創(chuàng)建 CreationBundle)。

    組件設(shè)計器

    如前所述,.NET Framework 內(nèi)置的設(shè)計器可以滿足大多數(shù)主要組件的要求。盡管這樣,.NET Framework 為組件設(shè)計者提供了一個可充分?jǐn)U展的體系結(jié)構(gòu)。所有設(shè)計器都基于下面的 System.ComponentModel.Design.IDesigner 接口:

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

    正如您所看到的那樣,IDesigner 是直接的。設(shè)計者通過 DesignerAttribute 與組件關(guān)聯(lián):

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

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

    圖 3. 設(shè)計時的 Win Form 控件

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

    訪問設(shè)計器服務(wù)和基礎(chǔ)結(jié)構(gòu)

    VS .NET 中的 .NET Framework 設(shè)計器開發(fā)環(huán)境的許多服務(wù)和基礎(chǔ)結(jié)構(gòu)組件簡化了復(fù)雜的操作,使設(shè)計器各部分之間可以互通狀態(tài)信息。這些服務(wù)總是通過使用 GetServiceObject 方法的 IServiceObjectProvider 來進行訪問。這是一些有趣的設(shè)計器服務(wù)列表:

    類型

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

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

    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 的可擴展模型中,授予許可證的體系結(jié)構(gòu)也可以擴展。為了簡便使用,框架定義了一個內(nèi)置的、標(biāo)準(zhǔn)許可證授予方法,以控制組件是否許可在設(shè)計時使用,但開發(fā)人員可以用任何他們認(rèn)為恰當(dāng)?shù)姆绞教鎿Q此方案。

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

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

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

    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"
             
    

    結(jié)論

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

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

    主站蜘蛛池模板: www.亚洲色图.com| 午夜无遮挡羞羞漫画免费| 亚洲av中文无码| 丰满亚洲大尺度无码无码专线 | 亚洲高清资源在线观看| 十八禁无码免费网站| 亚洲今日精彩视频| 最刺激黄a大片免费网站| 亚洲综合区图片小说区| 久热中文字幕在线精品免费| 亚洲an日韩专区在线| 在线免费观看一区二区三区| 亚洲GV天堂无码男同在线观看| 国产一区二区视频免费| 一本久久免费视频| 国产亚洲综合网曝门系列| 亚洲视频免费在线观看| 亚洲av专区无码观看精品天堂| 成人毛片免费观看视频| 免费精品国自产拍在线播放| 久久久久无码专区亚洲av| 久久永久免费人妻精品下载| 亚洲日本国产精华液| 国产精品冒白浆免费视频| 国产国产人免费人成成免视频| 亚洲精品私拍国产福利在线| 女人被男人躁的女爽免费视频| 色视频在线观看免费| 亚洲AV成人片色在线观看| 性xxxxx免费视频播放| 羞羞漫画在线成人漫画阅读免费| 伊人亚洲综合青草青草久热| 51精品视频免费国产专区| 亚洲国产精品美女久久久久| 亚洲综合亚洲综合网成人| 91福利视频免费| 精品成人一区二区三区免费视频| 亚洲av最新在线网址| 在线观看免费人成视频色9| v片免费在线观看| 亚洲午夜精品国产电影在线观看|