繁体中文
设为首页
加入收藏
当前位置:.Net技术首页 >> Asp.Net开发 >> 為 Microsoft Visual Studio .NET 設計工具建立可設計式元件(转贴)下

為 Microsoft Visual Studio .NET 設計工具建立可設計式元件(转贴)下

2007-08-15 08:00:00  作者:  来源:互联网  浏览次数:0  文字大小:【】【】【
简介:以相同方式出入:透過程式碼保存元件   不同於以往的設計工具,.Net Framework 元件的 Win Forms 與其它 VS .NET 設計工具,僅依賴表單狀態的程式碼保存性。沒有神奇的格式,也沒有隱藏資料,只是運作平穩的普...

以相同方式出入:透過程式碼保存元件

  不同於以往的設計工具,.Net Framework 元件的 Win Forms 與其它 VS .NET 設計工具,僅依賴表單狀態的程式碼保存性。沒有神奇的格式,也沒有隱藏資料,只是運作平穩的普通程式碼。當然,像點陣圖和本土化字串等,可以視為二進元資料與程式碼一起封裝,但元件狀態和元件所含內容,則必須透過程式碼來保存。在您設計工具的同時,也會產生程式碼。倘若您針對該程式碼進行處理,則會重新剖析,並且將變更內容反應在設計工具中。

.Net Framework 的設計工具可提供所有配備以利用此功能。所有設計工具對於任何類型最希望得知的內容包括:

有哪些關於物件狀態的資訊有助於保存性?

如何將該類資訊以現行物件傳回?

在先前的章節中,已經簡單討論過這個問題。再次聲明,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 物件的好處,在於這些物件若擁有符合傳入 CreationArgument 中各種類型的建構函式,就能夠得知用來儲存資訊的物件建立方式。在呼叫 TypeConverter::CreateInstance,並以此方式嘗試建立和初始設定物件時,TypeConverter 的預設實作會呼叫 CreationBundle::Invoke。倘若無可用的建構函式,則 CreateInstance 呼叫會採用 IDictionary,以允許物件製作更多自訂項目。傳入的 IDictionary 包含每個屬性名稱中不保存的值。

元件的屬性值通常由多個物件所組成,其它架構通常就是為此目的而使用屬性陣列。然而,陣列卻有些缺點,例如,陣列必須在傳出時先行複製,於傳回時再次複製,結果當然大幅影響效能;在新增、修改或刪除值的時候,陣列也無法提供智慧型通知。事實上,如果屬性傳回陣列,是否新增或刪除項目就相當耗費工夫。陣列也是快照值,若基本物件不變更,就無法進行更新。

反之,.Net Framework 在這種情況下,則使用實行 ICollection 物件的集合。物件可建立出集合並傳送給其它物件,同時參照項目可視基本物件的變更而保持最新狀態。若另一物件也針對集合進行變更,則亦將同時通知該物件。對於使用集合的 .Net Framework 設計工具,還需要支援含有 Get 和 Set 的 All 屬性,且其類型必須為集合可保留的物件陣列。例如:

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 的保存性機制,可保存或不保存本集合。若該集合由較先進的類型組成,如上述之 BoolIntString 範例類型,則需要利用與類型相關聯的 TypeConverter,為集合中每個項目建立有效的 PersistInfo (特別是 VS .NET 設計工具的 CreationBundle)。

元件設計工具

  如之前所述,.Net Framework 中的內建設計工具,足以滿足元件的多數要求。不過,.Net Framework 還包括元件設計工具的完整擴充性架構。所有設計工具皆以 System.ComponentModel.Design.IDesigner 介面為基礎,列示如下:

public interface IDesigner {

// 與本設計工具相關的元件

IComponent Component {get;}

// 與元件相關的設計階段動作,

// 如 TabControl 的「Add Tab」

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 可能位於元件基礎類別中,同時可供使用。有些設計工具會顯示出 UI,有些則不顯示。在 ComponentDesigner 中,由於元件通常沒有 UI,因此可以看到代表物件的圖示。另一方面,Win 表單控制項則有設計工具可於設計階段顯示出實際控制項。

圖 3:設計階段的 Win 表單控制項

請注意:位於設計工具下的圖示是未顯示 UI 的控制項;而顯示 UI 的 Win 表單控制項,則於執行階段顯示在表單設計工具中。然而不論顯示與否,這些控制項全數皆以 IDesigner 為基礎建立而成。控制項的設計工具通常會攔截設計中的控制項 WindowProc (來自System.WinForms.Design.ControlDesigner 的設計工具可以簡單地覆寫 WndProc 方法來完成),以執行諸如點擊測試等複雜的工作。然而,對於大部份元件而言,內建的預設設計工具應該已經足夠使用。

使用設計工具服務與基礎架構

  在 VS .NET 中的 .Net Framework 設計工具,顯露出多樣化服務和基礎架構元件,可簡化複雜的作業或允許設計工具了解有關其它部份的狀態。這些服務永遠使用 GetServiceObject 方法透過 IServiceObjectProvider 進行存取。以下是部分特殊設計工具服務的清單:

類型

 

描述

IDesignerHost

與任何最上層設計工具相關的主要類別。可提供方法以增加服務、建立與放置元件、移除元件並進行批次作業。

IComponentChangeService

新增、移除、重新命名或修改元件時提供通知。

ISelectionService

設定或取得目前設計工具中選取的項目。

IToolboxService

允許在工具箱內查驗和修改項目及選項狀態等等。

IUndoService

提供配備以建立動作的復原/重做單位,並管理復原/重做堆疊。

IHelpService

允許設定說明主題或者叫用說明項目。

IMenuCommandService

允許處理設計工具功能表指令和動作。

IReferenceService

在設計工具中將參照內容對應到物件。例如,名稱 button1 對應到元件 button1 。

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();

}

}

此範例可以使用內建授權支援來啟動授權工作。LicFileLicenseProvider 只要在類別的 Assembly 目錄中,尋找名為 .lic 的檔案,其中 classname 必須是完整的類型名稱。例如,對於 String 類型而言,名稱則為 System.String.lic。本檔案可包含字串「System.String 是已授權的元件」。若發現本檔案,LicenseManager.Validate 會傳回授權物件,並隨著類別實例來進行處置。

實行您個人的授權機制也一樣輕鬆簡單。只要建立取自 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

="true"

結論

  在管理程式碼中撰寫控制項,可為傳統的 C++/COM 方法帶來更多的優勢。Microsoft 從最基本的通用語言執行階段開始策劃,遍及至 C# 甚至重組的 Visual Basic 語言,以提供研發人員效率最高的通用方法,將建構軟體時的阻礙降到最低。Microsoft .Net Framework 是第一個以這些技術和原理為基礎,所研發成功的大型程式碼庫範例,而支援的整合式設計工具,則是本方法成功的關鍵。

责任编辑:admin
相关文章