使用.Net Core開發WPF App系列教程( 四、WPF中的XAML)


使用.Net Core開發WPF App系列教程

一、.Net Core和WPF介紹

二、在Visual Studio 2019中創建.Net Core WPF工程

三、與.Net Framework的區別

四、WPF中的XAML

五、WPF中的布局

六、WPF中的常用控件(上)


XAML介紹

XAML(Extensible Application Markup Language)(發音:zammel)可擴展應用程序標記語言。XAML是為構建應用程序用戶界面而創建的一種新的描述性語言。XAML提供了一種便於擴展和定位的語法來定義和程序邏輯分離的用戶界面,而這種實現方式和ASP.NET中的"代碼后置"模型非常類似。XAML是一種解析性的語言,盡管它也可以被編譯。它的優點是簡化編程式上的用戶創建過程,應用時要添加代碼和配置等。

 

說明:

1、UWP中的XAML和WPF中的XAML會略有不同,本文介紹的所有XAML功能只適用於WPF

2、如果了解XML,學習XAML會感覺更輕松

 

XAML編譯

XAML經過編譯后,會生成BAML(Binary Application Markup Language 二進制應用程序標記語言)。BAML是標記化的,這樣可以用較短的標記來代替XAML。

在項目的obj文件夾下,可以找到編譯后的BAML文件。這是臨時文件,當可以運行的dll生成后,BAML已經被嵌入到dll中去了。

 

說明:

BAML可以被ILSpy、.Net Reflactor這樣的反編譯工具反編譯成XAML

 

XAML基礎

當我們創建一個WPF工程后,打開MainWindow.xaml可以看到如下代碼

<Window x:Class="WpfApp2.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:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>

    </Grid>
</Window>

該文檔包含兩個元素:Window元素及Grid元素

 

說明:

這里所指元素其實就是類,如Window類代表的是System.Windows.Window類

頂級Window元素代表整個窗口,Grid元素中可以放置任何控件。

 

通常來說,頂級元素一般為窗口(Window),頁(Page),用戶控件(UserControl)等。

與所有XML文檔一致,在XAML文檔中只能有一個頂級元素。

 

再查看Window元素,會發現有三個屬性Title、Height、Width。這樣是Window類所具備的屬性,設置不同的屬性,窗口也會發生相應的改變。

 

說明:

Window類的常用屬性

Title 窗口的標題
Width 窗口的寬度
Height 窗口的高度
Visibility 指示窗口可見性
TopMost 指定窗口是否是頂層窗口
WindowState 指示窗口的狀態(最大化,最小化等)
WindowStyle 指示窗口的樣式(有無邊框等)
WindowStartupLocation  指示窗口的啟動位置
ShowInTaskbar 指示窗口是否在狀態欄顯示

這里只介紹了一些常用的屬性,完整的屬性可以訪問以下鏈接進行查看

https://docs.microsoft.com/en-us/dotnet/api/system.windows.window?view=netcore-3.1#properties

 

XAML命名空間

舉個簡單的栗子:

WPF內置控件有一個Button類,當我們引用了某個三方組件中,也有一個Button類,那這個時候,XAML是如何區分的呢?

我們可以看到Windows元素中有這樣一段代碼,這里其實就是對XAML命名空間進行聲明。

        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:WpfApp2"

xmlns特性是XML中的一個特性,用來聲明命名空間。在WPF中,是依靠System.Windows.Markup.XmlnsDefinitionAttribute特性來實現這個功能。

 

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

上面這一行是WPF核心命名空間,它包含了WPF的類。這個命名空間沒有使用前綴,所以整個文檔都會使用這個命名空間

 

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"實際上對應的代碼是:

 1 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Controls");
 2 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Documents");
 3 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Shapes");
 4 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Shell");
 5 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Navigation");
 6 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Data");
 7 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows");
 8 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Controls.Primitives");
 9 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Media.Animation");
10 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Input");
11 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Media");

 

1 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

上面這一行是XAML命名空間,它包含了XAML的各種特性。這個命名空間使用了前綴x:,所以在使用時,也需要帶上x:來使用該命名空間。

<x:MyElementName></x:MyElementName>

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"實際上對應的代碼是:

1 System.Windows.Markup.XmlnsDefinitionAttribute("http://schemas.microsoft.com/winfx/2006/xaml", "System.Windows.Markup");

 

如何通過XAML命名空間引用三方組件或系統組件

引用三方或系統組件的過程是先引入命名空間前綴,再通過命名空間前綴使用該命名空間下的相應類。

這里以Nuget包BlurWindow為例,這個包里有一個窗體透明的BlurWindows類。

1、安裝nuget包

2、引入命名空間前綴

1 xmlns:blur="clr-namespace:TianXiaTech;assembly=BlurWindow"

3、使用外部組件 

<blur:BlurWindow x:Class="ImportXmlns.MainWindow">
      ......
</blur:BlurWindow>

可以在本文末查看示例代碼

 

后台代碼類

 XAML只用於構造用戶界面,那它的事件處理程序代碼在哪呢?

一般我們在WPF中添加窗口時,可以到一個xxx.xaml會帶一個xxx.xaml.cs,這個xxx.xaml.cs就是后台代碼類,用於處理事件。

而這個這台代碼類是通過Class特性來進行連接的。Class帶了前綴x,說明這里是XAML語言中通用的部分

1 <Window x:Class="xClass.MainWindow"

 

 這個后台代碼類在創建窗口時,會自動生成,我們可以看一下它的結構

1 public partial class MainWindow : Window
2     {
3         public MainWindow()
4         {
5             InitializeComponent();
6         }
7     }

可以看到這是一個分部類(partial class)

 

說明:

雖然這是一個分部類,但建議不要創建多個后台代碼類文件,事件的邏輯代碼全部寫在默認的.cs文件里即可。

 

這個類比較簡單,只有一個構造函數,構造函數里執行了

InitializeComponent();

 這個 InitializeComponent() 的作用就是調用System.Windows.Application類的LoadComponent()方法從程序集中提取BAML,並用它來構造用戶界面。當解析BAML時,會創建控件對象,設置其屬性並關聯所有事件處理程序

 如果不執行InitializeComponent() ,UI就不會被正常加載 ,而只顯示默認空白窗口。所以當我們自己添加構造函數時,也要確保執行了InitializeComponent() 

 

命名元素

可以通過x:NameName來對元素進行命名,以便在后台代碼類中進行操作。

1 window.Title = "Naming Element";
2 
3 grid.Background = Brushes.LightSkyBlue;

使用Name命名后,系統實際上為我們生成了以下代碼

 

說明:

Namex:Name的區別

Name:這個元素包含Name屬性,就可以直接使用Name屬性對這個元素進行命名。

x:Name: x是實際上引入了XAML的相關功能,所以當一個元素沒有Name屬性時,就可以使用x:Name來對元素進行命名。概括來說就是x:Name是XAML的功能,是通用的。不管元素有沒有Name屬性

所以不管元素有沒有Name屬性,都使用x:Name來進行命名肯定是不會錯的

 

XAML中的屬性

屬性

屬性我們在前面已經有一些簡單的介紹,像下面這樣

1 <Label Content="WPF Property" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20" FontWeight="Bold" Foreground="LightSkyBlue" FontFamily="Arial"/>

 

說明:

目前暫時只介紹屬性如何使用,而不會針對某個控件。控件的使用會在后面的文章中說明

 

類型轉換器

XAML屬性的值總是純文本字符串,但對象的屬性可以是任何.Net類型。在上面的示例中,

HorizontalAlignment和VerticalAlignment使用的是枚舉類型(System.Windows.Horizontalalignment 和 System.Windows.VerticalAlignment

FontFamily使用的是字符串類型

FontSize使用的是整型

ForeGround使用的是Brush類型

那系統是如何從純文本字符串轉換到對應類型的呢? 這里就用到了類型轉換器(TypeConverter)

XAML通過以下兩個步驟來查找類型轉換器:

1、檢查屬性聲明。查找TypeConverter特性。這個特性可以指示,使用哪個類來進行轉換

2、如果在屬性聲明中沒有TypeConverter特性,XAML解析器將檢查對應數據類型的類聲明。例如,Foreground屬性使用的是Brush對象,那XAML解析就會去檢查Brush類是否使用TypeConverter特性修飾。

查找System.Windows.Media.Brush源碼,可以看到,Brush類使用了TypeConverter(typeof(BrushConverter))進行修飾,所以Brush及其子類都會使用BrushConverter類進行轉換

1     [Localizability(LocalizationCategory.None, Readability=Readability.Unreadable), ValueSerializer(typeof(BrushValueSerializer)), TypeConverter(typeof(BrushConverter))]
2     public abstract class Brush : Animatable, IFormattable, DUCE.IResource
3     {
4         ......
5     }

如果屬性聲明或類聲明都沒有與其關聯的類型轉換器,XAML解析器就會報錯。

 

說明:

XAML元素是case sensitive(大小寫敏感)

而元素的屬性不是case sensitive  

 

嵌套元素

XAML文檔就是一顆嵌套的元素樹。

可以看到下面的示例代碼:

Window元素包含了Grid元素,Grid元素包含了Button、Label、TextBox元素。

1 <Window>
2     <Grid>
3         <Button Content="OK"/>
4         <Label Content="label"/>
5         <TextBox/>
6     </Grid>
7 </Window>

 

 XAML讓每個元素決定如何處理嵌套元素,處理順序如下:

1、如果父元素實現了IList接口,解析器將調用IList.Add()方法,並且為該方法傳入子元素作為參數

2、如果父元素實現了IDictionary接口,解析器將調用IDictionary.Add()方法,並且為該方法傳遞子元素作為參數。 當使用字典集合時,需要設置x:Key特性,以便指定鍵名。

3、如果父元素使用ContentProperty特性進行修飾,解析器將使用子元素設置相應的屬性

 

Grid控件支持ContentProperty特性,這個特性是繼承自System.Windows.Controls.Panel類。可以看到Panel類的部分聲明如下:

[Localizability(LocalizationCategory.Ignore), ContentProperty("Children")]
public abstract class Panel : FrameworkElement, IAddChild

這表示可以使用任何子元素或內部文本來設置Children的屬性。XAML解析器根據是否是集合屬性(集合實現了IList或IDictionary接口),采用不同方式處理內容屬性。因為Panel.Children屬性返回一個UIElementCollection對象,而UIElementCollection類又實現了IList接口,所以解析器使用IList.Add()方法將嵌套的內容添加到Grid控件中

 

上面的XAML代碼實際上對應以下的C#代碼

 1 Grid grid = new Grid();
 2 
 3 Button button = new Button();
 4 button.Content = "OK";
 5 
 6 Label label = new Label();
 7 label.Content = "label";
 8 
 9 TextBox textBox = new TextBox();
10 
11 grid.Children.Add(button);
12 grid.Children.Add(label);
13 grid.Children.Add(textBox);
14 
15 this.Content = grid;

 

說明:

在WPF中,會經常使用ContentProperty特性。

就Grid而言,Grid類使用ContentProperty特性來標識Panel.Children屬性。

而其它控件會用 ContentProperty來標識 其它屬性。

如Button類會使用ContentProperty特性標識 Button.Content屬性(System.Windows.Controls.Button類繼承自System.Windows.Controls.ContentControl)

1 [DefaultProperty("Content"), ContentProperty("Content")]
2 public class ContentControl : Control, IAddChild

 

復雜屬性

有了類型轉器的理論基礎后,又遇到了一個新的問題。那就是如果屬性需要的對象自己還擁有一組屬性,這種情況應該怎么辦?

XAML提供了property-element syntax(屬性元素語法)。這里也比較簡單

例如:Grid控件有一個Background屬性,如果我們想使用更復雜的Brush,就需要用到這種語法,

如下:

<Grid>
    <Grid.Background>
        <ImageBrush ImageSource="xx.jpg"/>
    </Grid.Background>
</Grid>

目前我們先不用管這些代碼實現了什么功能,只需要了解屬性元素語法這種語法格式就可以了。

property-element syntax最核心的就是這個 (.)符號,它把屬性和嵌套內容區分開來。

 

擴展標記

當我們需要將屬性值設置為一個已經存在的對象,或者希望將屬性值綁定到另一個控件來動態地設置屬性值。這個時候就需要使用標記擴展。

語法如下:

{標記擴展類 參數}

如:

1 <Label Foreground="{x:Static SystemColors.SystemColors.ControlLightBrush}"></Label>

XAML中定義了以下擴展標記

擴展標記 功能說明
x:Type 為命名類型提供 Type 對象。 此擴展最常用於樣式和模板。在后面的文章中會經常用到此擴展標記
x:Static 生成靜態值。 這些值來自於值類型代碼實體,它們不直接是目標屬性值的類型,但可以計算為該類型。上面的示例代碼演示了如何使用x:Static
x:Null null 指定為屬性的值,可用於特性或屬性元素值。有時候我們不想使用默認值或繼承的值,就需要使用x:Null來進行置空
x:Array 這個用得比較少,不詳細介紹 了。可以訪問以下鏈接了解詳細 https://docs.microsoft.com/en-us/dotnet/desktop-wpf/xaml-services/xarray-markup-extension

 

WPF中定義了以下擴展標記(僅限WPF)

StaticResource  通過替換已定義資源的值來為屬性提供值。 StaticResource 計算最終在 XAML 加載時進行,並且在運行時沒有訪問對象圖的權限
DynamicResource  通過將值推遲為對資源的運行時引用來為屬性提供值。 動態資源引用強制在每次訪問此類資源時都進行新查找,且在運行時有權訪問對象圖。 為了獲取此訪問權限,WPF 屬性系統中的依賴項屬性和計算出的表達式支持 DynamicResource概念。
Binding  使用在運行時應用於父對象的數據上下文來為屬性提供數據綁定值。 此標記擴展相對復雜,因為它會啟用大量內聯語法來指定數據綁定
RelativeSource 提供用於Binding的源信息,該信息可以在運行時對象樹中導航多個可能的關系。 對於在多用途模板中創建的綁定,或在未充分了解周圍的對象樹的情況下以代碼創建的綁定,此標記擴展為其提供專用源
TemplateBinding 使控件模板能夠使用模板化屬性的值,這些屬性來自於將使用該模板的類的對象模型定義屬性
ColorConvertedBitmap  這個用得比較少,可以訪問以下鏈接了解詳細https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/colorconvertedbitmap-markup-extension
ComponentResourceKey   這個用得比較少,可以訪問以下鏈接了解詳細https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/componentresourcekey-markup-extension
ThemeDictionary   這個用得比較少,可以訪問以下鏈接了解詳細https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/themedictionary-markup-extension

 

附加屬性

附加屬性是 XAML 中引入的一種編程概念,

附加屬性可以理解為:這種屬性可用於多個控件,但這一類屬性不是在控件內部定義,而是在其它類中定義。

附加屬性常用於控件布局。自己封裝控件時,也會用到。

 

附加屬性語法格式如下:

typeName.propertyName

 

附加屬性的工作原理如下:

每個控件都擁有自己內部定義的屬性。但當在容器中放置控件時,控件會根據容器的類型而獲得額外特性。這時就需要使用附加屬性來設置這些附加的細節

例如我們在布局時,常用一種結構如下

 1 <Grid>
 2         <Grid.RowDefinitions>
 3             <RowDefinition/>
 4             <RowDefinition/>
 5         </Grid.RowDefinitions>
 6 
 7         <Grid.ColumnDefinitions>
 8             <ColumnDefinition/>
 9             <ColumnDefinition/>
10         </Grid.ColumnDefinitions>
11 
12         <!--通過附加屬性來設置在Grid的哪一行,哪一列-->
13         <Label Content="HelloWorld" Grid.Row="1" Grid.Column="1"/>
14  </Grid>

附加屬性是一個編程概念,並不是真正的屬性。我們在設置附加屬性的時候,這們實際上被轉換為方法調用。

格式如下

DefiningType.SetPropertyName()

1 Grid.SetRow(label, 1);
2 Grid.SetColumn(label, 1);

當調用SetPropertyName()方法時,解析器傳遞兩個參數:被修改的對象,指定的屬性值。

 

說明:

如果是剛開始接觸WPF,可能不好理解附加屬性這一概念。目前來說,只需要學會在XAML中使用即可,隨着學習的深入,我們會慢慢理解 這一概念。

 

XAML中的事件

在XAML中,為控件關聯事件處理程序比較簡單。語法格式為:事件名=“事件處理程序方法名”

如Button的Click事件,在XAML中:

1 <Button Content="OK" Click="Button_Click"/>

在后台代碼中可以看到系統生成的事件處理程序

 

說明:

1、在Visual Studio 2019中,鍵入 事件名=后,Visual Studio 2019提示生成默認的事件處理程序,這個時候我們點擊回車,就可以生成相關代碼

2、如果需要修改事件處理程序方法名,可以在手動輸入方法名后,按F12,系統會幫我們生成相關代碼。

 

XAML中的特殊符號

XAML中的特殊符號和XML里是一樣的,可參考以下鏈接

https://www.cnblogs.com/zhaotianff/p/12469944.html

 

推薦閱讀:

https://www.w3school.com.cn/xml/index.asp

https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/xaml-namespaces-and-namespace-mapping-for-wpf-xaml

https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/xaml-syntax-in-detail

 

本文示例代碼:

https://github.com/zhaotianff/DotNetCoreWPF/tree/master/四、WPF中的XAML


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM