在實際的WPF開發中遇到很多再用Winform寫法來寫WPF的開發人員,很多時候項目進度延期、出現非必要的BUG等等、大多是因為開發人員雖然是再寫WPF。
但是沒有好好的學過WPF,就導致無法發揮出WPF的優勢、很多地方都是開個線程處理完成后一個回調UI線程函數里面套一整段代碼,這種情況下不僅難處理多線程問題、也會出現很多偶發性的BUG,不好追蹤問題在哪里。同時也會因為開發人員對WPF的理解參差不齊導致架構設計和使用過程中代碼凌亂。所以今天開始hello world 來梳理WPF。
我們通過hello world理解以下幾個關鍵內容來整理XAML相關的知識點:
一、使用XAML創建可以顯示hello world的文本框。
二、理解XAML中的文法規范級命名空間
三、XAML中的的屬性和事件
四、從生成過程日志查看XAML的編譯。
一、使用XAML創建可以顯示hello world的文本框
經常寫winform窗體的同學一定深有感觸、拿到UI給的圖之后、我們如果想做的比較漂亮。大部分都是通過拖控件、然后把用戶界面的后台代碼中設置動畫效果、大小、位置等等。如果需要適配多種分辨率是更難受的。特別是一些需要深度定制的東西、可能很多開發時間都會用在這里。解決一些稀奇古怪被測試出來的的顯示問題。
而在WPF中提出了XAML的概念、我們可以使用XAML標簽來設計和構成我們的用戶界面。而XAML豐富的標簽一旦正確的使用,則會大大提高我們寫出更漂亮的界面的能力。
只寫文字大家都會反感。我們通過講解例子來認識XAML。首先創建一個WPF工程。
1、寫下第一行Hello world
打開VS選擇創建WPF項目,點擊下一步。
設置項目名稱為Hello World點擊創建。就創建了我們第一個WPF項目。
通過解決方案標簽項我們可以看到整個解決方案的目錄結構。而項目中的MainWindow就是我們本次分析的重點。
我們這次的重點在MainWindow.xaml和其對應的cs,所以其他部分不是本次的關注重點,也就不講了。
首先雙擊MainWindow.xaml,在Grid標簽中添加<TextBox Text="Hello world!"/>節點。然后在VS中按下F5鍵。程序就會執行編譯,如果成功的話就會跑起來,並且看到我們輸入的Hello world!。
<Window x:Class="HelloWorld.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:HelloWorld" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <TextBox Text="Hello world!"/> </Grid> </Window>
從我們看到這個顯示出來的hello world!的這一刻開始,就正式開始了本章內容的講解。
二、理解XAML中的文法規范級命名空間
1 <Window x:Class="HelloWorld.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:HelloWorld" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="450" Width="800"> 9 <Grid> 10 <TextBox Text="Hello world!"/> 11 </Grid> 12 </Window>
為了方便理解,我加入了行號,如果想系統的學習XAML可以先學習一下XML,XAML是遵循XML文法規范的,但是對於我們入門來說了解嵌套、 屬性的賦值和設置,就可以了。
我們的整體XAML結構大致為
<Window> <Grid> <TextBox/> </Grid> </Window>
我們當前的XAML根節點是一個Window節點,Window節點代表當前所顯示的整個窗口、在Window節點內部有一個Grid節點,Grid節點是一個布局元素里面可以放置的所有的控件、Grid節點包含了一個內部的TextBox節點,而這個TextBox就是我們所說的控件,而我們看到的顯示的內容是在Text=“”內賦值的。對於XAML中的內容。可以理解為把后台的.cs代碼文件通過xaml可視化的方式進行設置,如果有不知道含義的地方,可以用鼠標點在對應的節點上然后按F12就可以跳轉到對應的定義中。
比如我們把光標點在Grid上。按下F12。就會跳轉到下圖的位置。原來Grid繼承自一個Panel。里面包含了Column、Row等等。我們就知道了。Grid是一個擁有橫、列的網格面板。
而在TextBox上按F12之后,就會看到這是一個控件、顯示或編輯文本的。
其他的元素同樣的道理。把鼠標放置在對應的元素節點上按下F12就會顯示對應的內容。
接下來我們來看命名空間。
名稱空間看上去好像是一個URI的WEB位置,但實際上不是,URI格式的命名空間,不同組織就基本不會使用相同的名稱空間創建不同的基於XAML的語言。
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 是WPF核心名稱空間。 它包含了所有WPF類,包括創建界面的控件。
該名稱空間沒有使用前綴、所以它成為整個文檔的默認命名空間。也就是說除非指明了名稱空間,否則每個元素都直接使用這個名稱空間。
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 是XAML名稱空間,它包含各種XAML的特性,該命名空間被映射為前綴x。可以在元素名稱之前放置x
來使用該名稱空間。例如x:Name,x:Class="HelloWorld.MainWindow"。
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"是旨在提供設計師支持,該名稱空間使XAML元素上的這些屬性僅影響XAML行為的設計方面。
而在運行過程中會忽略掉這些屬性。比如d:DesignHeight="100" d:DesignWidth="100",這個在設計時看不到該控件時,在對應的控件上添加此屬性特別有效。
在Blend下使用比較多的是針對集合的d:DataContext、d:DesignData。等等。感興趣的可以看看Blend相關的。
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 標識使運行時XAML解析器能夠忽略設計屬性,比如mc:Ignorable:d。d前綴的在運行時XAML解析器解析時就會忽略了。
xmlns:local="clr-namespace:HelloWorld" 這個就是我們工程自己的名稱空間了。
x:Class="HelloWorld.MainWindow" x:Class特性是告訴XAML解析器用指定的名稱生成一個新類,該類繼承自XAML元素命名的類,也就是我們的根節點Window。
三、理解XAML中的的屬性和事件
XAML中的屬性:
我們通過前面的講解了通過跳轉查看XAML節點的含義,比如<Grid>是布局用的Panel。<TextBox/>是顯示和編輯無格式文本的。
那么現在我們來理解屬性。
在元素中我們通過跳轉了解到了XAML節點都是一個一個的類對象。里面定義了屬性、方法及事件。那么在XAML中,我們可以在這里設置屬性、事件。通過設置這些來修改樣式及監聽對應的事件消息。
那么在屬性中分為簡單屬性和復雜屬性。我們在Textbox上按F12跳轉過去,然后我們找個屬性來演示一下簡單屬性和復雜屬性如何使用。
在Foreground上我們看到了該屬性是一個Brush類型。而在WPF中Brush支持純色、線性漸變色等等。我們設置代碼如下。
這樣我們就在第一個TextBox 上設置了背景色為一個枚舉值。用於表示一個顏色值。 而當我們想設置復雜Brush的時候,我們就可以使用復雜屬性,第二個TextBox通過屬性的嵌套來創建一個復雜的線性畫刷。2個TextBox顯示效果如下:
但是新的問題又來了,現在大部分主流的App都支持更換皮膚,明暗主題什么的。而我們只能通過硬編碼的形式設置屬性,那豈不是要實現更換皮膚,每個控件都要設置一遍?
有沒有什么辦法能讓XAML屬性綁定一個變量,而我們去修改這個變量?
實現方法的辦法之一就是使用擴展標記:假設我們的內容都放在一個windows里做演示:
添加window的資源。同時使用DynamicResource來綁定資源。就可以實現。一個資源修改,所有使用的地方都跟着變化。后面會詳細講到。如何使用擴展標記來進行主題切換。
而在WPF中提出的是依賴項屬性的概念。這個和路由事件需要花費精力好好學一下。才能更好的使用和理解WPF。
XAML中的事件:
為了增強事件的傳播能力,WPF中提出了路由事件的概念,路由事件可以在元素樹中向上和向下傳播,並且沿着傳播路徑被事件處理程序消費。我們在XAML中添加2個處理按下和抬起事件的代碼。
后台的處理代碼。
路由事件和依賴項屬性這些主要會放在MVVM中講。這里主要了解一下。XAML下如何創建事件處理程序。
四、從生成過程日志查看XAML的編譯。
對於加載和編譯XAML,有四種方式:
1、只使用代碼來創建XAML對應的元素,類似於winform的寫法;
2、使用代碼和未經編譯的XAML。
3、使用代碼和編譯過的XAML。
4、只使用XAML。
這里我們只去理解一個使用代碼和編譯過的XAML。其他的工作到目前未知,覺得用到的比較少,等真正用到了。只要理解了可以在去查如何使用。
當我們編譯WPF程序時,編譯過程分為了2個階段,第一個個階段是將XAML文件編譯為BAML文件。我們使用的C#語言,所以會在臨時文件生成對應的.g.cs文件
g代表generated。
當從XAML到BAML的編譯結束后,編譯器會編譯代碼和生成的部分類文件。編譯過的代碼會變成單個程序集。每個窗口的BAML都會作為獨立資源被嵌入到程序集中。
我們通過修改VS的設置來查看編譯過程。
在VS選擇工具=>選項=>項目和解決方案=>生成並運行=>MSBuild項目生成輸出詳細程序=>設置為詳細。而后點擊編譯。我們來觀察輸出的內容。
我們查看關鍵的幾處
以上截圖就是完整的編譯過程。
我創建了一個C#相關的交流群。用於分享學習資料和討論問題。歡迎有興趣的小伙伴:QQ群:542633085