用.NET Framework 2.0創建Form設計器

Microsoft .NET Framework 1.0提供了一個非常通用的設計時框架,但是沒有提供任何實現代碼來完成一個設計器,Visual Studio? .NET實現了所有的複雜邏輯,要第三方去重新實現這個複雜的邏輯。.NET Framework 2.0引入了一組類能夠用於設計器的實現。 

用.NET Framework 2.0創建Form設計器(圖二)

  理解.NET Framework如何工作,非常重要的是要了解設計器是如何使用的。設計器是負責治理設計界面上的組件的設計時期行爲和表現的對象。框架關聯設計時對象和運行時對象,爲設計時組件提供了一個管道擴展運行時對象的行爲。運行時,Form上的一個form和button這兩個控件只是通過父子關係相關聯,沒有其他的對象來控制這些控件的生命週期。

用.NET Framework 2.0創建Form設計器(圖三)
Figure 2

  上面的圖片看出設計時比較複雜,Form和button都有一個設計器相關聯,兩個對象都和Host容器相關聯,host容器擁有這兩個對象,host容器也提供服務---例如選取服務處理設計時的組件選取,並跟蹤所選取的組件,UI服務用於顯示對話,調用幫助系統和設計環境相聯繫。

  Host container有許多職責,包括創建組件、綁定組件到設計器和爲組件和設計器提供服務。從持久化介質上加載組件和保存組件狀態到持久化介質。Host container提供撤銷、剪貼板功能和其他的服務等爲實現魯棒的設計器所依靠的功能。

用.NET Framework 2.0創建Form設計器(圖四)
Figure 3 Designer Hosting

  host container使用designer loader持久化設計器狀態,designer loader使用序列化機制序列化組件。

  服務擴展

  The .NET Framework設計時框架是可擴展,提供的服務可用於實現各式各樣的設計器。一個服務是提供對象可通過類型進行查詢,典型的是你定義了服務的抽象類和接口和服務的實現。你可以從service container中添加或者刪除服務。IDesignerHost是設計器主要的host接口,是一個Service Container。服務是一個組件間可以共享的,正因如此,在創建和使用Service的時候必須遵循確定的規則。

  Services不被保證的,無論何時通過GetService方法請求一個服務(Services),你一定要檢查返回的是否是一個有效對象。並不是所有的服務在所有的平臺上都是可用的,而且曾經可用的服務未來不可能是可得。因此你的代碼應當被寫的降低優雅型,通常籍由需要某種服務而丟失某些特性,以防萬一一個服務也得不到。

  假如你添加一個服務,記得在設計器的被disposed的時候移除它。設計器會時不時地創建和消毀,假如你沒有去清除一個服務的話,舊的設計器就會遺留在內存中。DesignSurface 和DesignSurfaceManager

  .NET Framework 2.0引入了兩個類DesignSurface 和DesignSurfaceManager.給設計器提供宿主以及給設計器提供服務。DesignSurface是使用者所感知的設計器,它是UI使用者操縱改變設計時特徵,DesignSurface 可能被當作一個單獨的設計者使用或者和DesignSurfaceManager結合使用爲設計器應用程序提供多個DesignSurface。

  DesignSurface提供好幾個設計時服務,大多數服務都可以在服務容器中被覆蓋,替換不可替換的服務是非法的,因爲他們之間彼此仰賴.注重添加到Service Containe實現了接口IDisposabler的服務當DesignSurface 銷燬的時候都會被銷燬。

  除了提供缺省的服務,DesignSurface也提供了IDictionaryService,此服務提供一個使用關聯鍵設置、檢索和查找對象的簡單接口。不可能替換這些服務因爲在每個站點上無法替換這些服務。

  DesignSurfaceManager是設計器的容器,它提供通用的服務以處理在設計者,屬性窗口和其他的全局對象之間的事件路由. 使用 DesignSurfaceManager 是可選擇的, 但是假如你想需要有一組設計者窗口,推薦使用DesignSurfaceManager。

  DesignSurfaceManager也提供了幾個設計時服務(see Figure 5).。每一個都可以在PRotected屬性ServiceContainer(服務容器)中被覆蓋。和DesignSurface一樣,DesignSurfaceManager所有的 實現了接口IDisposabler的服務 當設計器應用程序銷燬的時候都會被銷燬。

  IDesignerEventService 是一個非凡地有用的服務. 當一個設計器變成活躍的時候 , 它答應一個設計器應用程序被通知到. IDesignerEventService 提供了一組設計器和全局對象的訪問點, 例如屬性窗口能夠偵聽到選擇變化事件.

  宿主form

  爲了示範一下宿主一個設計器是多麼簡單,我寫了下面的簡單代碼來創建一個基本的Windows? Forms designer並顯示它:

// Create the DesignSurface and load it with a form

DesignSurface ds = new DesignSurface();
ds.BeginLoad(typeof(Form));

// Get the View of the DesignSurface, host it in a form, and show it

Control c = ds.View as Control;
Form f = new Form();
c.Parent = f;
c.Dock = DockStyle.Fill;
f.Show();
  在這一個代碼片斷中,我已經用Form方式裝載 DesignSurface. 同樣地,你能用擁有根設計器的任何組件裝載 DesignSurface. 舉例來說,你可以改爲裝載 UserControl 或一個組件.

用.NET Framework 2.0創建Form設計器(圖五)
Figure 6 Hosting Windows Forms Designer

  提供下載的例子代碼中有四種根組件:Form, UserControl, Component, and MyTopLevelComponent (一個圖形設計器). 當你運行例子的時候,一個Shell UI 將會打開. 它包括一個工具箱,一個屬性窗口, 一個tab Control來宿主設計器,一個Output window和一個Solution EXPlorer,如圖6所示..使用菜單的File New Form 用窗口打開一個新的Windows Forms Designer。這本質上就是使用上面所展示的代碼加載一個設計器。與裝載一個Form相比較,例子中還展示瞭如何裝載UserControl或者組件。

  創建一個根組件,也就是創建一個設計器實現IRootDesigner接口,然後指定這個組件的designer相關聯,根組件的視圖屬性將呈現給使用者。

  DesignSurface 提供的主要服務之一是 IDesignerHost,IDesignerHost是用於提供設計器和對類型、服務和事務控制的主要接口。它也用於創建和銷燬組件。添加一個按鈕到Windows Forms designer所要做的工作就是從DesignSurface獲得IDesignerHost接口並創建button,代碼如圖7

// Add a Button to the Form
IDesignerHost idh = (IDesignerHost)ds.GetService(typeof(IDesignerHost)); 
Button b = (Button)idh.CreateComponent(typeof(Button));
// Set the Parent of this Button to the RootComponent (the Form)
b.Parent = (Form)idh.RootComponent;
// Use ComponentChangeService to announce changing of the 
// Form's Controls collection */
IComponentChangeService icc = (IComponentChangeService) idh.GetService(typeof(IComponentChangeService));
icc.OnComponentChanging(idh.RootComponent, TypeDescriptor.GetProperties(idh.RootComponent)["Controls"); 

  ItoolboxUser指定設計器支持從Toolbox中增加控件到設計器,這意味着你確實需要一個實現ToolboxService的Toolbox,你能夠用IToolboxUser接口把控件添加到根組件。例如:

/* Add a Button to the Form using IToolboxUser */

IDesignerHost idh = (IDesignerHost)ds.GetService(typeof(IDesignerHost));
IToolboxUser itu = (IToolboxUser)idh.GetDesigner(idh.RootComponent);
itu.ToolPicked(new ToolboxItem(typeof(Button)));
用.NET Framework 2.0創建Form設計器(圖六)
Figure 8 Custom RootDesigner Updates

  例子程序中雙擊Toolbox中的控件,控件被添加到自定義的根設計器,根設計器的視圖中顯示一個pie chart如圖8所示,點擊GraphStyle鏈接改變視圖到bar graph.

  工具箱

  MyRootDesigner實現IToolboxUser接口,這個接口有兩個方法:GetToolSupported and ToolPicked. 你能使用 GetToolSupported 過濾項目能被填加到設計器上的組件. 進入ToolboxItem 的 CreateComponents 方法 (如名字應用,負責創造組件) 調用的時候調用ToolPicked。

  既然我們已經成功添加控件和組件到設計器,讓我們來看一下如何實現一個Toolbox。首先,你的工具箱需要實現 IToolboxService —這一個服務被增加到服務容器,任何需要使用的任何人都可以被存取。

  答應項目從工具箱通過老鼠或鍵盤的添加到設計器上,示例程序的工具箱處理KeyDown 和 MouseDown 事件。Enter鍵或鼠標雙擊事件, IToolboxUser.ToolPicked 被調用. 示例展示了鼠標單擊拖動控件如何序列化ToolboxItem 到 DataObject和DoDragDrop方法調用,鼠標mouse up事件IToolboxService.SerializeToolboxItem被調用,而且項目將會被增加到設計器.

  當一個控件或者組件被添加到設計器,你能藉由實現 INameCreationService 提供一個定製的名字給組件,示例程序展示了CreateName, ValidateName, and IsValidName的代碼實現。QQRead.com 推出數據恢復指南教程 數據恢復指南教程 數據恢復故障解析 常用數據恢復方案 硬盤數據恢復教程 數據保護方法 數據恢復軟件 專業數據恢復服務指南 Multiple DesignSurfaces

  當治理多個DesignSurfaces,一個好主意是使用DesignSurfaceManager。它使得輕易治理這些DesignSurfaces(注重 DesignSurfaceManager 的服務也是可得的到 DesignSurface.) 

  調用DesignSurfaceManager.CreateDesignSurface將調用CreateDesignSurfaceCore,你能夠重寫這個函數去創建一個自定義的DesignSurface和增加服務。示例程序在類HostSurfaceManager通過重寫這個函數創建了自定義的HostSurface:

protected override DesignSurface CreateDesignSurfaceCore(IServiceProvider parentProvider)
{
 return new HostSurface(parentProvider);
}

  你能通過HostSurfaceManager類的事件ActiveDesignSurfaceChanged更新output窗口,代碼如下:

void HostSurfaceManager_ActiveDesignSurfaceChanged(object sender, ActiveDesignSurfaceChangedEventArgs e)
{
 ToolWindows.OutputWindow o = this.GetService(typeof(ToolWindows.OutputWindow)) as ToolWindows.OutputWindow;
 o.RichTextBox.Text += "New host added.\n";
  DesignerLoaders

  到現在爲止我已經實現了DesignSurfaces、宿主設計器、添加控件、Toolbox和存取服務,像 OutputWindow. 下一個步驟要持久化設計器。設計器載入程序如同你將會期待一樣, 負責從持久化介質加載Designer form. 設計器載入程序只有少許的需求. 事實上,你能創建Windows Forms designer的一個實例。

  除了載入設計器,設計器載入程序對設計結果的保存也是設計器的職責。因爲保存是可選擇的行爲,一個設計者載入程序偵聽改變來自設計器的改變事件,而且自動的保存這些狀態。.

  .NET Framework 2.0引入兩個新的類來自定義加載器:BasicDesignerLoader 和CodeDomDesignerLoader,示例應用舉例說明兩者的載入程序類型的實現。然而,假如你正在使用一個載入程序,它應該用來裝載DesignSurface. 你將使用的 BeginLoad 代碼片斷當使用載入程序的時候應該看起來有點像下面的代碼:

// Load it using a Loader
ds.BeginLoad(new MyLoader());
  DesignerLoader 負責載入 DesignSurface 的根組件而且創建任何組件. 當創造一個新的Form或任何其他的根組件的時候,載入程序只是裝載它. 和從代碼文件或一些其他的存儲介質的載入,載入程序負責解析文件或者存儲而且再創建根組件的任何其他的必需組件.

  .NET Framework定義了一個抽象基類叫做DesignerLoader,用於加載和保存設計器到持久介質。基類是abstract,因此任何持久化模型都可以使用這個類,但是,這也增加了實現類的複雜性。

  BasicDesignerLoader提供了除任何數據的持久格式外設計者載入程序的完全和通常的實現. 像 DesignerLoader ,它是abstract, 不處理關於持久化格式的任何事情. BasicDesignerLoader處理標準的工作:如何時該保存,知道該如何再裝載, 而且追蹤來自設計器的變化通知. 它的特徵包括對多依靠加載,保存變化, 而且延期加載支持。

  服務被 BasicDesignerLoader 添加到設計器的服務容器(service container)中。像其他的服務一樣,你能夠修改被保護的 LoaderHost 屬性來修改可替換的服務。示例應用程序實現持久化xml格式的類是BasicDesignerLoader. 爲了瞭解它如何工作,選擇菜單 File Type BasicDesignerLoader.. 然後選擇菜單File New Form創建一個新的Form,查看它所生成的XML文件,選擇菜單View Code XML. 所看到的XML文件的內容類似於下面的內容:

<Object type="System.Windows.Forms.Form, System.Windows.Forms, 
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
name="Form1" children="Controls">

<Property name="Name">Form1</Property>
<Property name="DataBindings">
<Property name="DefaultDataSourceUpdateMode">OnValidation</Property>
</Property>
<Property name="ClientSize">292, 273</Property>
</Object>
  BasicDesignerLoader的PerformFlush 和 PerformLoad 是二個abstract方法是你爲實現序列化和反序列化的功能必須實現的方法. 

  CodeDomDesignerLoader

  設計時序列化是通過產生代碼來實現,代碼生成Schema的一個挑戰是如何處理多語言。.NET Framework被設計爲多語言協同工作,因此我也希望設計器能夠生成多語言。有二個方法來達到解決這個問題. 第一要需要每個語言廠商爲他們的語言寫代碼生成引擎. 不幸的是,沒有語言廠商能夠預期第三方組件廠商代碼生成的多樣性需求. 第二種方式要需要每個組件廠商提供代碼生成器給他們支持的每種語言.因爲被支持的語言的數量是未知的,所以這相當糟糕。

  爲了解決這個問題,.Net Framework定義了一個對象模型叫做代碼文檔對象模型(CodeDOM),所有的原始代碼能本質上分解爲原始的元素的組合,而且 CodeDOM 是那些元素的對象模型.當代碼依附在CodeDOM, 生成的對象模型能夠給不同語言的代碼生成器生成適當的代碼。

  .NET Framework 2.0引入了CodeDomDesignerLoader類,繼續自BasicDesignerLoader。CodeDomDesignerLoader是一個通過CodeDom進行讀寫支持的全功能的加載器。它是設計者加載器, 因而你所需要做全部的是CodeDomProvider.

  示例應用中你可以選擇菜單File Type CodeDomDesigner-Loader來看CodeDom的實做例子。創建新的Form通過菜單File New Form---這創建一個DesignSurface和用CodeDomDesignerLoader加載它。查看代碼,通過選擇菜單View Code C#查看Form生成的C#代碼,或者選擇菜單View Code VB查看Visual Basic代碼。

CompilerParameters cp = new CompilerParameters();

AssemblyName[] assemblyNames = Assembly.GetExecutingAssembly().GetReferencedAssemblies(); 
foreach (AssemblyName an in assemblyNames)
{
 Assembly assembly = Assembly.Load(an);
 cp.ReferencedAssemblies.Add(assembly.Location);
} cp.GenerateExecutable = true;
cp.OutputAssembly = executable; cp.MainClass = "DesignerHostSample." + 
this.LoaderHost.RootComponent.Site.Name; // Compile CodeCompileUnit using CodeProvider
CSharpCodeProvider cc = new CSharpCodeProvider();
CompilerResults cr = cc.CompileAssemblyFromDom(cp, codeCompileUnit); if (cr.Errors.HasErrors)
{
string errors = string.Empty;
foreach (CompilerError error in cr.Errors)
{
errors += error.ErrorText + "\n";
}
MessageBox.Show(errors, "Errors during compile.");
}

  示例程序使用CSharpCodeProvider 和VBCodeProvider生成代碼,它也使用代碼提供程序編譯代碼和運行可執行程序。

  ITypeResolutionService是一個使用CodeDomDesignerLoader的時候的必須服務,負責類型解析。例如當從Toolbox添加一個控件到設計器的時候,這個服務被調用解析控件的類型。示例程序解析程序集System.Windows.Forms的所有類型,所以你能夠將Toolbox的Windows Forms下的控件添加到設計器

  結論

  你所看到的是,.NET Framework提供了一個強大的和靈活的設計器宿主基礎結構。比上一個版本的Visual Studio設計器的可擴展性有助於設計着解決非凡的需求或更多高級的場合. 當然, 設計者也能輕易地宿主與Visual Studio外面 。同樣地. 下載樣例程序代碼,你就能夠參照例子設計你自己的設計器。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章