.Net开发笔记(十九) 创建一个可以可视化设计的对象
閱讀本篇博客之前需要了解VS窗體設(shè)計器的工作原理,詳細(xì)可參見本系列博客(十)、(十一)、(十二)。必須需要知道的一條結(jié)論就是:處于窗體設(shè)計器(Form Designer)中的任何組件(包含控件,下同),都是實際存在的一個實例。也就是說,拖進(jìn)去的button1,其實就是實例化一個Button控件。
通常編碼中,我們在使用一個類型對象時,通過以下方式:
?View Code如上所示,我們在使用Car類時,是通過new的方式來創(chuàng)建一個實例,然后給它初始化一些信息,這些所有操作都是通過我們手動來編寫代碼實現(xiàn)的。
我們注意到,在設(shè)計UI界面的時候,窗體中的所有控件、組件都是可以通過“屬性窗體”來編輯的,也就是說,界面上這些元素的初始化不需要我們手動編寫代碼,完全可以通過點點鼠標(biāo),按按鍵盤就可以做到。我們可以總結(jié)出來設(shè)計器可以幫我們做以下工作:
- 實例化對象
? ? 沒錯,不用你手動new對象了,設(shè)計器幫你來完成。
- 編輯屬性
? ? 選定一個組件,在屬性窗體中編輯它的屬性,跟你通過編寫“實例.屬性=屬性值”是一樣的效果。
- 注冊事件
? ? 選定一個組件,在屬性窗體的事件選項卡,雙擊事件空白處,自動注冊事件。
窗體設(shè)計器不需要你手動編寫一行代碼,對象的實例化、屬性編輯、事件注冊全部搞定,也就是說,窗體設(shè)計器能夠可視化設(shè)計一些對象。至于哪些類型的對象可以通過窗體設(shè)計器來進(jìn)行可視化設(shè)計,請參見本系列(十、十一、十二),我在這里直接給出結(jié)果:窗體設(shè)計器能夠可視化設(shè)計實現(xiàn)了IComponent接口類型的對象。也就是說,如果你定義了一個類型A,恰好它實現(xiàn)了IComponent接口(直接或者間接),那么你就完全可以通過窗體設(shè)計器來可視化設(shè)計A類型的對象。
由此可以看出,創(chuàng)建一個可以可視化設(shè)計的對象并不難,只要我們的類型實現(xiàn)了IComponent接口就行(官方稱這種類型為組件)。我們再來看一下,窗體設(shè)計器初始化出來的對象,跟我們自己手動編寫代碼初始化的對象有哪些相同點和不同點:
不同點:
- 前者更直觀簡單,隱藏的東西太多,后者復(fù)雜,但是清楚內(nèi)部過程。
- 前者對象的初始化,在程序一啟動就開始,不能人工控制其時機,具體是在Form1的構(gòu)造方法中的InitializeComponent()中進(jìn)行,后者就更靈活,需要的時候編寫代碼就可以。
- 前者初始化出來的對象幾乎都跟UI界面有關(guān)(這個很容易就能想到,窗體設(shè)計器肯定設(shè)計跟窗體界面有關(guān)的東西),而后者沒有這個原則,不管是什么對象,都是可以的。
相同點:
- 都是初始化一個對象。
- 都有代碼產(chǎn)生,前者產(chǎn)生的代碼在InitializeComponent()中,后者為人工編寫。
我們應(yīng)該清楚,程序最終都是要經(jīng)過將源代碼編譯成可執(zhí)行文件之后才能運行的,所以源代碼是一切根本,沒有源代碼,其他的都是白扯。
綜上所有之述,我們可以手動編寫代碼來初始化任何對象,我們可以通過窗體設(shè)計器來初始化實現(xiàn)了IComponent接口的類型對象。
好了知道怎樣才能創(chuàng)建一個可以可視化設(shè)計的對象之后,我們來創(chuàng)建一個試一下,定義一個類型MyComponent,使其繼承自Component:
?View Code如上代碼所示,該類型只包含了幾個公共屬性,沒有其他內(nèi)容。此類型對象就可以通過窗體設(shè)計器來設(shè)計了,也就是說,從工具箱中向設(shè)計器中拖放MyComponent類型之后,窗體設(shè)計器自動會實例化一個MyComponent對象,并且你可以通過屬性窗體來編輯該對象的屬性:
1)StringProperty
? ? ?String類型屬性,直接可以在屬性窗體中輸入。
2)ColorProperty
? ? ?Color類型屬性,屬于.NET自帶類型,所以有默認(rèn)的屬性編輯器,如下圖:
圖1
3)MyTypeProperty1
? ? 自定義類型屬性,需要我們自己定義一個屬性編輯器Editor(typeof(MyTypeEditor1),typeof(UITypeEditor))。
4)MyTypeProperty2
? ? 自定義類型屬性,需要我們自己定義一個屬性編輯器Editor(typeof(MyTypeEditor2),typeof(UITypeEditor))。
5)ControlProperty
? ? Control類型屬性,我們可以將設(shè)計器中已經(jīng)存在的Control賦值給該屬性,指定了屬性編輯器Editor(typeof(ControlEditor),typeof(UITypeEditor))。
6)ImageListProperty
? ? ImageList屬性,這個就是我們常見的一些控件(比如TabControl)含有ImageList屬性,點擊右方的小三角形,就可以列出窗體設(shè)計器中已經(jīng)存在的ImageList,供你選擇。指定了屬性編輯器Editor(typeof(ImageListEditor),typeof(UITypeEditor))。
也就是說,當(dāng)我們在窗體設(shè)計器中設(shè)計一個對象的時候,如果該對象包含一些特殊(非.NET默認(rèn)自帶類型)類型屬性時,我們需要為該屬性提供一個“屬性編輯器”。
以下就分別為每個屬性對應(yīng)的屬性編輯器了(假設(shè)諸位看官都知道了UITypeEditor的作用,不知道可以查一下):
1)MyTypeProperty1屬性
?View Code鼠標(biāo)點擊MyTypeProperty1屬性右側(cè)的小三角形,出現(xiàn)一個下拉列表框。如下圖:
圖2
2)MyTypeProperty2屬性
?View Code鼠標(biāo)點擊MyTypeProperty2右側(cè)的小三角形,彈出一個對話框。
圖3
3)ControlProperty屬性
?View Code鼠標(biāo)點擊ControlProperty右側(cè)的小三角形,出現(xiàn)下拉列表框,列表中顯示的都是窗體設(shè)計器中已經(jīng)存在的Control。
圖4
4)ImageListProperty屬性
?View Code鼠標(biāo)點擊ImageListProperty右側(cè)的小三角形,出現(xiàn)下拉列表,列表中顯示窗體設(shè)計器中已經(jīng)存在的ImageList。
圖5
本篇博客介紹了怎么創(chuàng)建一個可以可視化設(shè)計的對象(準(zhǔn)確來講,應(yīng)該是怎樣創(chuàng)建一個可以可視化設(shè)計其對象的類型),首先讓類型(間接或者直接)實現(xiàn)IComponent接口(不要問我為什么,請參見前面的博客),然后為類型的某些特殊屬性創(chuàng)建對應(yīng)的屬性編輯器,就這么簡單。類型創(chuàng)建者工作量大一點,但是類型使用者更方便一些,Demo中MyComponent類型就是最后的核心成果,供使用者使用,使用者不需要知道其他的類似屬性編輯器之類的東西,在他們看來,這些相當(dāng)于沒有。
注意使用范圍,并不是任何時候都可以使用,最好參見前面提到的“相同點”和“不同點”那里。另外,本篇博客中設(shè)計到的知識點很多,像provider.GetService(typeof(IWindowsFormsEditorService))、foreach(Component c in context.Container.Components)這些需要和設(shè)計器打交道的地方我?guī)缀鯖]有詳細(xì)說到過,原因是這些太復(fù)雜了,要是細(xì)說的話,得說一大簍子,而且不是本篇文章的重點。
源碼下載地址:http://files.cnblogs.com/xiaozhi_5638/PropertyEditorInDesigner.rar
注意不要試圖運行源代碼,沒有任何效果,只能在設(shè)計器中看到效果。希望有幫助。
Update (2013-12-09)
上面結(jié)束提到了“支持可視化設(shè)計對象”的使用場合,建議如果該類型跟UI界面有關(guān)聯(lián)(需要與界面其他元素交互),并且對象的實例化不需要人工控制其時機,那么可以讓該類型支持可視化設(shè)計(也就是實現(xiàn)IComponent接口)。注意這里的建議,也就是說不是強制性的,你完全可以定義一個People類,讓其實現(xiàn)IComponent接口,這合法!結(jié)果就是,從工具欄中拖放一個People到窗體設(shè)計器中,它會自動幫你實例化了一個People對象實例,并且你也能通過屬性窗體編輯它的屬性值(這些后臺都能自動生成對應(yīng)代碼)。另外,經(jīng)發(fā)現(xiàn),具備某一些功能的類型,雖然跟界面無交互,但是它還是支持可視化設(shè)計,比如BackgroundWorker組件,拖一個BackgroundWorker到窗體設(shè)計器中去,設(shè)計器會自動幫你實例化一個Backgroundworker對象(生成對應(yīng)代碼),然后你可以通過屬性窗體編輯它的屬性值(生成對應(yīng)代碼),這跟你自己手動new BackgroundWorker沒有區(qū)別,你還是可以在其他地方一樣使用該backgroundworke對象,至少你目前看起來沒區(qū)別。
為了更好的說明通過窗體設(shè)計器設(shè)計出來的對象,跟我們手動編寫代碼搞出來的對象有什么相同和不同點,我們先來分析一下Form1.Designer.cs中的代碼:
我們注意到,我們在設(shè)計器中的每一步操作,對應(yīng)生成的代碼都在InitializeComponent()方法中,它像是word錄制宏的功能,你在word中的每一個操作,都可以生成對應(yīng)的VBA代碼,也就是說,窗體設(shè)計器從實例化對象,到編輯屬性,再到注冊事件等等等,都有代碼幫我們記錄下這些操作。我們再看一下實例化對象的代碼:
?View Code沒錯,任何一個實例化出來的對象,都給它傳遞了this.components容器,我們再看MyComponent的構(gòu)造方法是這樣的:
?View Code也就是說,對象實例化的時候,都將該對象放進(jìn)了一個components的容器,這個容器專門用來存放由窗體設(shè)計器實例化出來的對象(控件除外)。這就像一個大的容器,專門來存放這些小個體。接下來我們再來看一下Form1.Designer.cs中的dispose方法:
?View Code我們可以發(fā)現(xiàn),在Form1對象Dispose的時候,它將components中的所有個體都Dispose掉了。到此,我們可以總結(jié)出來一條:由窗體設(shè)計器設(shè)計出來的對象由父窗體(父控件)的InitializeComponents方法統(tǒng)一初始化,由父窗體(父控件)的Dispose()方法統(tǒng)一釋放資源。我們來看一張F(tuán)orm1運行結(jié)構(gòu)圖:
圖(更新)1
由此可以看出,由窗體設(shè)計器設(shè)計出來的對象,它們的生命周期以及存放結(jié)構(gòu)都比較有規(guī)律,這個就是窗體設(shè)計器設(shè)計出來對象的好處。
我們在往窗體設(shè)計器中拖放組件時,就是往Form1類型中添加新的成員對象,跟我們定義一個類型,向里面添加成員變量一個意思,只是前者更直觀,添加的每一個成員對象,在UI設(shè)計器中都能看見與它對應(yīng)的一個對象實例,只要我們改變了這個對象實例的屬性,就能馬上看見設(shè)計器中的對象實例效果,緊接著后臺生成代碼;而后者就沒有這么直觀了,只能手寫代碼,而且還看不見效果。
圖(更新)2
如上圖,窗體設(shè)計器中某一個對象實例屬性更新之后,馬上就能在設(shè)計器中看見效果(因為它是實實在在存在于堆中的對象),接著InitializeComponents中的代碼就會更新,注意,窗體設(shè)計器中的myComponent1對象實例跟.cs代碼中的myComponent1變量不是一個東西,他們只有一種映射關(guān)系。我們最終要的是.cs中的代碼文件,而不是我們看見的窗體設(shè)計器中的圖像,后者只是起到一個可視化的效果,最終一文不值。這個就像photoshop作圖一樣,最終保存到硬盤的圖片文件才是最重要的,作圖過程中作圖區(qū)域顯示的東西沒有價值。窗體設(shè)計器隱藏得越多,我們知道得越少。
作者:周見智?
出處:http://www.cnblogs.com/xiaozhi_5638/?
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。
本文轉(zhuǎn)自周見智博客博客園博客,原文鏈接:http://www.cnblogs.com/xiaozhi_5638/p/3463774.html,如需轉(zhuǎn)載請自行聯(lián)系原作者
總結(jié)
以上是生活随笔為你收集整理的.Net开发笔记(十九) 创建一个可以可视化设计的对象的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 邮件:事务失败。 服务器响应为:DT:S
- 下一篇: ArcGIS Engine 编辑介绍