在VisualStudio.Net之前,大多數使用者介面的設計都需要用代碼實現。雖然那個時候也有VC和VB,但VC/VB設計器的功能還非常弱;雖然Java有LayoutEngine,但是多數情況下只能用來設計一個規整的介面;雖然還有很多IDE也有視覺化設計工具,但是它們的表現還達不到我們的期望。在這種情況下,如果我們要實現一個複雜的介面,將會是一件痛苦的事情,而且一旦實現了就不能輕易改動,否則就不得不重複調整某個控制項數個像素後再運行工程看效果無數次。
而VisualStudio.Net的出現讓我們眼前一亮,我們只需要在Form設計器或者其它設計器上拖拖拽拽就能完成大部分介面的設計了,而且幾乎一切都是所見即所得 (WYSIWYG)的,何其爽哉!
也許你也會和曾經的我一般的好奇,這麼強大的設計器是怎麼實現的?為什麼在設計器上拖拖拽拽就能產生正確的代碼?Form設計器到底是怎麼回事,那上面真的就放了一個Form執行個體嗎?我從ToolBox上拖拽了一個Control到Form上以後,就真的在Form上建立了一個Control嗎?Control在設計時的各種行為又是怎麼實現的?我需要如何控制我自己的Control在設計時的行為?
如果你還對此保持了一份好奇心,那麼就請隨本文一起去探究一下可視化設計的奧秘。本文是一篇介紹性的文章,面向的是初學者或者對DesignTime不甚瞭解的朋友,所以不會出現一些深層次的知識,也不會太擴散開了去說,僅能點到而已。如果您對本文中提到到某方面或者某個技術點有興趣,那請參考MSDN或者參考他人的部落格,或者關注一下我以後的一些這方面的文章。
首先,我可以肯定的告訴你,你所在設計器上看到的所有Component,Control,包括Form本身,IDE都在記憶體中建立了實際的對象。你在設計器中看到的Form,就是有一個Form執行個體放在那,而Form上的某個Control,也有一個實際的Control被添加到了這個Form中,就像你運行這個工程以後看到的Form介面一樣。所不同的是你運行起來的哪個Form是產生了程式集以後從程式集中建立的;而設計器中的Form則是IDE先把你的代碼轉換成了CodeDom,然後再從CodeDom還原序列化成執行個體的。
所以,你在在Form上拖動Control,或者通過屬性視窗設定某個Control的屬性,實際上都在修改記憶體中的對象。那麼,你對對象的修改又是如何產生代碼的呢?答案也在CodeDom,IDE會把你設計器中的所有Component都序列化成CodeDom,然後再從CodeDom產生代碼。(關於Codedom ,這裡為什麼要用CodeDom,以及一些其它細節,將在以後的文章中描述)。
在Form設計器上最重要的操作只有兩種:一是在Form上用滑鼠點擊,拖拽等;二是通過屬性視窗設定Component的屬性。
大家可以看到,滑鼠鍵盤在設計器中Form上的操作和在運行時Form的操作截然不同,而且選中一個Control以後這個Control周圍會畫一圈虛線以及一些熱點。那麼這又是如何?的呢?實際上,IDE在設計器中大家所看到的Form上又蒙了兩層透明的視窗。一層視窗用來截獲訊息,你所有對Form上的操作的訊息都會被這層串口截獲處理,所以你無法點擊Form右上方的關閉按鈕關閉Form,也無法按下Form上的一個Button;另一層視窗用來繪製裝飾,比如選中Control以後周圍出現的一圈虛線以及熱點等。
而你如果注意看屬性視窗上顯示的Component上的屬性的時候,就會發現那上面的屬性和Component上的屬性並不太一樣,有些屬性看不到了,有些屬性不起作用了(比如Visible屬性),而且又多了一些屬性。影響PropertyWindow中屬性的方式有很多;一是某些Attribute,比如BrowsableAttribute(在某個屬性的腦袋瓜上加一個[Browsable(false)]就能讓它在屬性視窗中消失);二是某些服務,比如IExtenderProviderService(Component的Name屬性就是通過這玩意加上去的);三是通過ComponentDesigner上的PreFilteProperties(Control的Visible屬性就是在這裡面被狸貓換太子了)和PostFilterProperties;四是通過設定TypeDescriptor,這是最根本的方式,實際上前3種方式最終也是通過TypeDescriptor實現的,關於TypeDescriptor我會在以後專門寫文章說明。
這裡再提一下DesignerAttribute,Component在設計器中的行為多半都是通過Designer實現的,你可以給自己的Component加一個Designer,你自己的Designer應當從ComponentDesigner或者它的衍生類別(比如ControlDesigner)中繼承。在你的Designer中你就可以通過重寫ActionLists屬性來自訂智能標籤(SmartTag);可以重寫Verbs屬性來在右鍵菜單中添加一個功能表項目;如果從ControlDesigner派生更可以SnapLines來自訂拖動時的吸附效果等等。假設你的Designer叫MyDesigner,你的Component叫MyComponent,那麼把MyDesigner掛到MyComponent上的方法是在MyComponent類上加一個Attribute:[Designer(typeof(MyDesigner)] 。
好了,對於VisualStudio設計器的介紹先到這裡,如果你對這部分內容感興趣的話,請繼續關注本部落格,如果你有什麼問題或者想法,歡飲討論。