使用WPF工做6個多月了,是時候寫一些WPF的基礎知識了。在這個主題上我已經寫了幾篇文章了。他們都是基於處理一些具體的問題而完成的。如今我拋磚引玉,並讓您理解如何/爲何WPF做爲革命性的UI開發走向了咱們。程序員
因爲這是一篇適合初學者和中級水平的程序員的文章,我將盡可能給出儘量多的基本的例子。編程
Windows Presectation Foundation瀏覽器
正如名字所示,WPF其實是.NET Framework3.0引入的幾個framework.它其實是提出了一套新的類和程序集並容許咱們更加有效和靈活的來編程。它是使用Direct3D渲染並使用顯卡來展示在顯示器上。所以這種形式的繪圖是很平滑的而且有好好利用您的機器中安裝的硬件能力的機會。針對傳統的GDI應用程序,它不能使用先進的圖形處理能力,所以Windows Forms應用程序和WPF應用程序相比老是很低效。還有另一個很重要的事情我必須標註一下,GDI Windows Form應用程序是利用操做系統控件來創建它的應用程序。所以它基本上很難在你的應用程序中自定義他們。WPF控件其實是在你的屏幕上繪製的,所以若是須要的話,你能夠徹底自定義控件而且修改他們的行爲。緩存
WPF有不少優點,讓我先介紹幾個:架構
WPF爲應用程序引入了與設備無關的DPI設置來構建應用程序。對於一個窗口來講,對於計算在屏幕中可以繪製多少DPI是很是重要的。這一般是由應用程序運行的硬件設備和操做系統和設備應用的DPI設置決定的。任何用戶均可以自定義這些設置,所以使得引用程序看起來很糟糕。Windows Form 應用程序使用基於像素的方法以便在DPI設置變動的時候,每一個控件都可以改變它的尺寸和外觀。框架
WPF解決了這個問題,而且使它可以獨立於計算機的DPI設置,讓咱們看看它是怎麼作到的:編程語言
比方說,如圖中所示你畫了一個框,它是1英寸長的96DPI屏幕,如今若是你看到120DPI設置的相同的應用程序,這個框就會變小。這是由於,咱們在屏幕中看到的東西都徹底依賴於DPI設置。ide
在WPF中,這種修改是採用基於密度的方法。這意味着當像素的密度被修改使,該元素將相應的調整他們,從而使得WPF應用程序的項目是與設備無關的像素。正如你在途中看到的,這個WPF中的控件在它須要更多的像素的狀況例如120DPI下,應用程序合適的調整它,使得它大小保持不變。函數
WPF應用程序做爲DirectX的環境渲染,它具備圖形和動畫功能的主要支撐能力。有一組單獨的類來實現處理動畫效果和圖形。在屏幕中繪製的圖形也是基於矢量的也是面向對象的。這就是說,當你在WPF應用程序中繪製一個矩形,你可以很容易的從屏幕中刪除它覺得Rectangle其實是一個你一直都在掌控中的對象。在傳統的基於Windows的應用程序,當你繪製了一個矩形,你不能單獨的選擇它。所以WPF的程序設計和傳統的Windows程序設計相比是徹底不一樣的,也是更加複雜的。咱們將在後面討論圖形和動畫的更多細節。工具
除了圖形和動畫方面的能力,WPF中還附帶了巨大的靈活性來定義樣式和控件模板。樣式基於的技術就和你也許碰到過的CSS差很少,它是一組定義。它定義控件渲染到屏幕上時,看起來是個什麼樣子。在傳統的Windows應用程序當中,樣式和每一個控件是緊耦合的,所以你須要爲每一個單獨的控件定義顏色,樣式等等使得它看起來不一樣。對WPF來講,樣式和UIElement是徹底分離的。一旦你定義了一個樣式,你能夠這個樣式應用到這個元素上市的你能夠改變它的外觀和風格。
一般咱們處理的大多數的UIElemnet實際上都不只僅是一個Element在用。WPF引用了一個模板的新概念,你可使用它來從新定義整個控件自己。例如上,你有一個CheckBox,它有一個Rectangle和ContentPresenter(TextBox展示的地方).而後你能夠從新定義你的CheckBox而後把一個ToggleButton放在裏面,使得這個Check看起來更像是個ToggleButton而不是Rectangle。這個是很是有趣的,在後續的文章中咱們將探究更多樣式和控件模板的細節。
WPF中另外一個重要的特色是可訪問的資源。在傳統的Windows應用程序中,從新定義一個樣式是很是麻煩的。所以若是你有1000個Button,你想要給每一個Button的顏色設置爲Gold,你須要建立1000個對象來給每一個單獨的元素分配。所以這樣使得資源看起來很龐大。
在WPF中,你可以存儲樣式,控件,動畫,甚至吧一個對象作爲資源。然而每一個資源在Form加載的時候,都要聲明一下,你能夠進一步把資源分配給控件。你能夠在一個單獨的ResourceDictionary文件中維護一個全局的樣式。這裏的樣式能夠被整個應用程序使用。所以WPF應用程序很容器被應用某個主題。
在接下來的步驟中,我必需要介紹一下WPF中的新的屬性系統。每一個WPF元素都定義了大量的依賴屬性。依賴屬性比普通屬性有更強的能力。所以使得咱們定義的新屬性,能夠很容易的給咱們任何想要註冊的對象註冊屬性。浙江給每一個對象都關聯了一個相同的觀察者。由於每一個元素都是從DependencyObject繼承的,每一個元素都包含了Dependency Observer。一旦你註冊了一個變量爲依賴屬性,在Observer上它將建立一個區域來把Control和它的值關聯起來。
根據定義,XAML是一種基於XML的生命是標記語言,用於指定和設置類的特性。換句話說,XAML是一種由WPF,Silverlight或者其餘能夠聲明本身的類的應用程序使用的語言。所以,在你的應用程序中你能夠爲任何類定義變量,定義屬性並直接使用。當渲染應用程序的時候XAML解析器將會自動轉化和建立實際的對象。
XAML一般是用來在元素和對象中靜態和可視化方面定義UI佈局的。咱們不能使用XAML來定義程序流。所以即便有了XAML的強大的能力,它實際上不是編程語言。它是用來爲應用程序設計UI的。所以XAML使用C#,VB.NET等其餘編程語言在背後定義代碼的邏輯。
對於每個新技術,對它的架構有個清晰的概念都是很是必要的。所以在開始構建你的應用程序以前,你必需要掌握幾個概念。若是你不喜歡瞭解WPF的一些細節,請跳過這一段。正如前面提到過的,WPF其實是構架整個Framework的一組程序集。這些程序集能夠這樣分類:
託管層
非託管層
核心API
託管層:WPF的託管層是使用一些程序集構建的。這些程序集構建了WPF的Framework來與底層的非託管API通訊,以便渲染它的內容。構成WPF框架的一些程序集是:
PresentationFramework.dll:建立頂層的佈局容器,控件,窗體,樣式等等。
PresentationCore.dll:它擁有基本的類型,例如派生於PresentationFramework.dll中的全部形狀和控件中的UIElement和可視化元素。
WindowsBase.dll:他們擁有更多可以在WPF環境外面使用的能力的基本的元素,例如Dispatcher, Dopendency,我將稍後逐一介紹他們。
非託管類型(milcore.dll):WPF中的非託管層稱爲milcore或者是媒體整合庫核心。它基本上是處理WPF更高層次的對象的處理,例如佈局容器,按鈕,動畫等等來達到Direct3D的預期。它是WPF主要的渲染引擎。
WindowsCodecs.dll:這個是WPF應用程序中用來處理圖像處理支持的更低層次的API。WindowsCodecs.dll包括不少將圖像轉換爲矢量圖形的編碼/解碼器,這些圖像而後被渲染到屏幕上。
Direct3D:這個是WPF圖形渲染的低層次的API。
User32:他是每一個程序都要使用的主要的核心API。它實際是管理內存和進程分離的。
GDI&Device Drivers :GDI和設備驅動是操做系統特有的,它也被用來讓應用程序訪問底層的API。
從上面的途中,就如上面咱們討論的,你能夠看到不一樣的Framework之間的元素進行通訊有什麼不一樣。
在開始構建WPF應用程序以前,有幾件事情,你必需要知道。Dispatcher和Thread Affinity意味着什麼?當WPF應用程序啓動時,它是家上是自動建立了兩個線程。一個是渲染線程,這個對程序員是隱藏的,所以在你的程序中你不能直接使用渲染線程。另一個是Dispatcher Thread,實際上它包含了全部的UI元素。換句話說,你可能會想Dispatcher是實際上就是UI Thread,在WPF應用程序中它約束了全部建立的元素。相反,WPF須要全部的UI元素都與Dispather Thread 關聯起來。這就是所謂的線程關聯。所以你不能在其餘任何線程更改在Dispatcher線程中建立的元素。它遵循Win32 API相同的限制。然而,它容許你基於API去互操做任何WPF組件爲HWND。Dispatcher是一個處理線程關聯的類,實際上全部的元素都是經過渠道的優先消息循環來處理的。每個UIElement都是從定義了一個叫作dispatcher屬性的DispatherObject對象繼承的,這個dispather屬性指向了UI Thread。所以從其餘的線程,若是你想要調用或者訪問UI組件,你須要經過Dispatcher Thread來調用。DispatcherObject 實際上有兩個主要的職責,來檢查和確認是否有訪問對象的權限。
什麼是可視化樹和邏輯樹?
每一個編碼樣式都包含一些不一樣種類的邏輯樹,從而組成了整個程序。邏輯樹包含在XAML中列出來的元素,所以他們只包含您在XAML中聲明的控件。
另外的可視化樹, 包括構成單個控件的組成部分。你一般不須要直接處理可視化樹。可是你應該知道每一個控件是怎麼構成的,所以使用它將更容易構建自定義模板。
我我的在使用它以前這哦你是喜歡看看可視化樹。ExpressionBuilder是一個可以生成實際控件的一個工具。
在C#語言中RoutedEvent是很新的, 可是對那些從JavaScript/Web 技術過來的人說,你可能在你的瀏覽器中發現了它。實際上有兩種RoutedEvent,一種是經過VisualTree元素到另一個元素冒泡,另一個是在VisualTree中的元素隧道。固然也有不經過冒泡和隧道的直接路由事件。
當調用了一個註冊了的路由事件,它經過冒泡/隧道可視化的元素,而後在VisualTree內逐個元素的調用被註冊的全部關聯的RoutedEventHandler。
二者之間的區分,WPF用這樣的方法來劃分,Preview**做爲隧道路由事件,**做爲冒泡事件。例如,IsPreviewMouseDown是當鼠標擡起時在可視的元素中的隧道事件。所以在IsPreviewMouseDown時,鼠標按下最外層的元素首先被調用;在MouseDown時,最裏面的元素的MouseDown事件將被調用。
每一個WPF控件都是從DependencyObject繼承來的。DependencyObject是支持依賴屬性的類,屬性系統是WPF中新建的。每一個對象都是從DependencyObject繼承而來,所以它能夠關聯它本身支持WPF內置的各類特性,例如EventTriggers, PropertyBindings, Animations等等。
每一個DependyencyObject實際上有個觀察者列表,它聲明三個方法ClearValue, SetValue, GetValue來添加/編輯/刪除這些屬性。所以當你用SetValue存儲一些東西的時候,DependencyProperty將僅僅建立它本身。這也是資源節約的一種方式。咱們還將在其餘的文章中對DependencyProperty進行詳細討論。
另一個你應該知道的很重要的事情是,你應該知道WPF的圖形是怎麼渲染的。事實上WPF渲染會自動檢測當前的操做系統支持多少硬件加速並據此來調整它本身。圖形渲染能檢測合適的層來相應的渲染輸出。
針對硬件渲染來講,影響很大的幾件事情是:
在WPF控件中有幾個對象。讓咱們逐個進行討論,如圖所示(抽象類用橢圓標註,實體類用矩形標註)
如今是時候開始建立你第一個WPF應用程序了。要建立程序,首先讓咱們打開Visual Studio 2008/2010/2012/2013,這個例子,我用了Visual Studio 2008.建立一個新的項目。你將看到一個新的窗體。XAML看起來應該像這樣:
<Window x:Class="FirstWindowsApplication.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="Window1" Title="Window1" Height="300" Width="300">
<Grid>
</Grid>
</Window>
這裏生成了一個空白的窗口。Height/Width表明窗體的大小。Title肯定顯示在窗口的標題欄上的文本。每一個在XAML中的控件都應該利用x:Name屬性來命名,被用來在XAML中引用Window對象。x:Class屬性表明和當前Window有關聯的類。正如我已經告訴你的,XAML不是自給自足的,所以爲了定義具體邏輯你須要用C#或VB.NET寫的類。
Grid是WPF應用程序中一個主要的佈局對象。Grid能夠有多個孩子元素。如今讓咱們把一些控件放在Grid當中來。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="50" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Enter Name :" Grid.Row="0" Grid.Column="0" />
<TextBox x:Name="txtName" Grid.Row="0" Grid.Column="1" MinWidth="50"/>
<Button Content="Click Me" Grid.Row="0" Grid.Column="2" Click="Button_Click"/>
</Grid>
你能夠看到,我定義了一個RowDefination和一個ColumnDefination。這將容許你將Grid劃分爲單元格以便你能把你的控件精確的放到你想要放置的地方。針對每一個RowDefination和ColumnDefination,你可使用它的Width和Height。你能看到我用了50,Auto和 * 做爲Width的值。Auto表明單元格的尺寸將在控件被定義時定義。* 表示它將佔據剩餘的全部控件。所以,你能夠看到button佔據了這個列當中剩餘的全部空間。
如今,在後置代碼中,我方了一個MessageBox來顯示TextBox的內容。
private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show(string.Format("Hi {0}", this.txtName.Text)); }
所以你能夠看到系統會提示你你的名字,是否是頗有趣!
若是你看了這個小小的XAML,你也許很好奇在其餘的控件中我怎麼才能定義這個Grid屬性。就像我在每一個控件中定義Grid.Row同樣。使用依賴屬性使得這成爲可能。這是WPF新引入的一個特性。咱們將在之後詳細討論。
這是WPF系列的第一部分,這包括WPF應用程序的初步討論,我將深刻到WPF程序的其餘方面,以及你如何利用WPF控件工做並直到您構建一個解決方案。
但願您能喜歡閱讀這篇文章