WPF學習之X名稱空間詳解
X名稱空間裡面的成員(如X:Name,X:Class)都是寫給XAML編譯器看的、用來引導XAML程式碼將XAML程式碼編譯為CLR程式碼。
4.1X名稱空間裡面到底都有些什麼?
x名稱空間對映的是:http://schemas.microsoft.com/winfx/2006/xaml,望文生義,它包含的類均與解析XAML語言相關,所以亦稱之為“XAML名稱空間”。
與C#語言一樣,XAML也有自己的編譯器。XAML語言被解析並編譯,最終形成微軟中間語言儲存在程式集中。在解析和編譯XAML的過程中,我們經常要告訴編譯器一些重要的資訊,如XAML編譯的結果應該和哪個C#程式碼編譯的結果合併、使用XAML宣告的元素是public還是private訪問級別等等。這些讓程式設計師能夠與XAML編譯器溝通的工具就存在X:名稱空間中。
我們注意到,它分為Attribute、標籤擴充套件、XAML指令元素三個種類。下面我們講講它們的具體用法:
4.2 X名稱空間中的Attribute
前面我們已經講過,Attribute和Property是兩個層面上的東西,Attribute是語言層面上的東西,是給編譯器看的,Property是面向物件層面上的東西,是給程式設計邏輯看。而且一個標籤中的Attribute大部分對應物件的Property。在使用XAML程式設計的時候,如果你想給它加一點特殊的標記來改變XAML對它的解析,這時候就需要額外的給它新增一些Attribute了。比如,你想告訴XAML編譯器將哪個編譯結果和那個C#編譯的類合併,這時候就必須為這個標籤新增X:Class Attribute來告訴編譯器。X:Class並不是物件成員,而是重X空間硬貼上去的。讓我們瀏覽一下常用的Attribute。
4.2.1 x:Class
這個Attribute是告訴XAML編譯器將XAML編譯器編譯的結果和後臺編譯結果的哪一個類進行合併,使用x:Class有以下幾點要求:
- 這個Attribute只能用於根節點。
- 使用x:Class的根節點的型別要與x:Class的值所指示的一致。
- x:Class的值所指示的型別在宣告的時候必須使用partial關鍵字。
- x:Class已經在剖析最簡單的XAML的時候已經講過,在這就不多講了。
4.2.2 X:ClassModiffier
這段程式碼是告訴XAML編譯器有標籤編譯成的類具有什麼樣的訪問級別。
使用這個Attribute的時候需要注意的是:
- 標籤必須具有x:Class Attribute。
- X:ClassModiffier的值必須與X:Class所指定類的訪問許可權一致。
- X:ClassModiffier的值隨後臺程式碼編譯語言的不同而有所不同。
<Window x:Class="WpfApplication2.Window5"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window5" Height="300" Width="300">
<Grid>
<StackPanel Height="218" HorizontalAlignment="Left" Margin="19,31,0,0" VerticalAlignment="Top" Width="237">
<TextBox Height="23" Width="120" />
<Button Content="Button" Height="23" Width="75" />
</StackPanel>
</Grid>
</Window>
這篇程式碼中通篇沒有出現一次名字,但是我們可以通過引用者的層級關係來找到我們最終想要的控制元件,我們在Button的Click下寫如下程式碼:
private void Button_Click(object sender, RoutedEventArgs e)
{
StackPanel panel = this.Content as StackPanel;
TextBox textBox = panel.Children[0] as TextBox;
if (!string.IsNullOrEmpty(textBox.Name))
{
textBox.Text = textBox.Text;
}
else
{
textBox.Text = "NULL";
}
}
this.Content引用著StackPanel的例項,StackPanel.Children[0]又引用著TextBox的例項。知道了這個關係,就可以一路順著查詢下來並同時進行型別轉換,最終TextBox中顯示的值是NULL。
理論上我們可以用上面的方法訪問到UI上的所有元素,但這畢竟太麻煩了。換句話說:XAML這種物件宣告語言只負責宣告物件而不負責為這些物件宣告引用變數。如果我們需要為物件準備一個引用變數以便在C#中直接訪問就必須顯示的告訴XAML編譯器-----為這個物件宣告引用變數,這時候,X:Name就派上用場了。
注意:
X:Name的作用有兩個:
(1)告訴編譯器,當一個標籤帶有x:Name時,除了為這個標
這個還真不能確定!籤生成例項還要給這個標籤宣告一個引用變數,變數名就是x:Name的值。
(2)將XAML標籤所對應的Name屬性(如果有)也設定為x:Name值,並把這個值註冊到UI樹上,以方便查詢。
4.2.4 x:FieldModifier
使用了x:Name後,XAML標籤對應的例項就具有了自己的引用變數,而且這些引用變數都是類的欄位,既然這樣就不免要關注一下它的訪問級別。預設情況下這些欄位的級別都被設定成了Internal。在程式設計的時候,有的時候需要用一個程式集裡的一個窗體元素訪問到另一個程式集的窗體元素,那麼就需要使用x:FieldModifier來改變變數的訪問級別!
<StackPanel Height="218" HorizontalAlignment="Left" Margin="19,31,0,0" VerticalAlignment="Top" Width="237">
<TextBox Height="23" Width="120" x:Name="txtName" x:FieldModifier="internal"/>
<Button Content="Button" Height="23" Width="75" Click="Button_Click" x:Name="btntest" x:FieldModifier="public"/>
</StackPanel>
因為x:FidleModifier是應用變數的訪問級別,所以要配合x:Name一起使用。否則沒有引用變數,何來引用變數訪問級別。
4.2.5 x:Key
最自然的檢索方式莫過於”key-value“的形式了。在XAML檔案中,我們可以把需要多次使用的類容提取出來放在資源字典中,需要使用的時候就用這個資源的key將這個資源檢索出來。
x:key的作用就是使用為資源貼上用於檢索的索引。在WPF中,幾乎每個元素都有自己的Resource屬性,這個屬性就是“key-value”的集合。只要把元素放進這個集合裡,這個元素就成了資源字典中的一個條目。當然,為了能檢索到這個條件,就必須為它新增x:Key。資源在WPF中非常重要,需要重複使用的XAML內容,如Style,各種Template和動畫都需要放在資源裡。
先讓我們看XAML程式碼:
<Window x:Class="WpfApplication2.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication2"
Title="Window4" Height="369" Width="675">
<Window.Resources>
<local:Human x:Key="human" Child="ABC"></local:Human>
<sys:String x:Key="myString">測試</sys:String>
<Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
<Setter Property="Width" Value="30"></Setter>
<Setter Property="Background" Value="black"></Setter>
</Style>
</Window.Resources>
<Grid>
<Label Content="{ StaticResource ResourceKey=myString}" Height="28" HorizontalAlignment="Left" Margin="177,81,0,0" Name="label1" VerticalAlignment="Top" />
</Grid>
</Window>
資源不但可以在XAML中使用,也可以在C#中訪問,C#中使用如下方式:
string str = this.FindResource("myString") as string;
this.label1.Content = str;
4.2.6 x:Shared
學習x:key的時候我們已經瞭解到,如果把某個物件作為資源放入資源字典裡後我們就可以把它們檢索起來重複使用。那麼每當我們檢索到一個物件,我們得到的究竟是同一個物件呢,還是這個對像的一個副本呢?這就要看我們為x:Shared賦什麼值了。x:Shared一定要與x:Key配合使用,如果x:Shared值為true,那麼每次檢索這個物件的時候,我們得到的都是同一個物件,反之,我們得到的就是這個物件的一個副本。預設這個值是true。也就是說我們使用的都是同一個物件。
4.3 x名稱空間下的擴充套件標籤
標記擴充套件實際就是一些MarkupExtension類的直接或間接派生類。x名稱空間中就包含一些這樣的類,所以稱其為x名稱空間標籤的標記擴充套件。
4.3.1 x:Type
顧名思義,x:Type應該是一個數據型別的名稱。一般情況下,我們在程式設計中操作的資料型別例項或者例項的引用。但有的時候我們也需要用到資料型別本身。
能讓程式設計師在程式設計的層面上自由的操作這些資料型別,比如在不知道具體型別的情況下建立這個型別的例項並嘗試呼叫它的方法,.NET名稱空間裡還包含了名為Type的類做為所有資料型別在程式設計層面上的抽象。
當我們想在XAML中表達某一資料型別就需要用到x:Type標記擴充套件。比如某個類的一個屬性,它的值要求的是一個數據型別,當我們在XAML中為這個屬性賦值是就需要用到x:Type。請看下面這個例子:
<UserControl x:Class="WpfApplication2.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="52" d:DesignWidth="128">
<Grid>
<Button Content="Button" Height="30" HorizontalAlignment="Left" Margin="10,10,0,0" Name="button1" VerticalAlignment="Top" Width="106" Click="button1_Click" />
</Grid>
</UserControl>
/// <summary>
/// UserControl1.xaml 的互動邏輯
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public Type MyWindowType { get; set; }
private void button1_Click(object sender, RoutedEventArgs e)
{
Window myWin = Activator.CreateInstance(this.MyWindowType) as Window;
if(myWin!=null)
{
myWin.Show();
}
}
}
<Window x:Class="WpfApplication2.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication2"
Title="Window4" Height="369" Width="675">
<Window.Resources>
<local:Human x:Key="human" Child="ABC"></local:Human>
<sys:String x:Key="myString">測試</sys:String>
<Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
<Setter Property="Width" Value="30"></Setter>
<Setter Property="Background" Value="black"></Setter>
</Style>
</Window.Resources>
<Grid>
<local:UserControl1 HorizontalAlignment="Left" Margin="292,244,0,0" x:Name="userControl11" VerticalAlignment="Top" MyWindowType="{x:Type TypeName=local:Window1}"/>
</Grid>
</Window>
回顧一下之前的標記擴充套件語法,因為TypeExtension類的構造器可以接受資料型別名做為引數,所以我們完全可以這樣寫:
UserWindowType="{x:Type local:Window1}"
編譯並執行程式,單擊主窗體上的按鈕,自定義視窗就會顯示出來,我們還可以多寫幾個窗體來擴充套件這個程式,到時後只需要修改MyWindowType裡面的值就可以了。
4.3.2 x:Null
在XAML裡面表示空值就是x:Null。
大部分時間我們不需要為屬性附一個Null值,但如果一個屬性就有預設值而我們有不需要這個預設值就需要用的null值了。在WPF中,Style是按照一個特定的審美規格設定控制元件的各個屬性,程式設計師可以為控制元件逐個設定style,也可以指定一個style目標控制元件型別,一旦指定了目標型別,所有的這類控制元件都將使用這個style----除非你顯示的將某個例項的Style設定為null。
請看下面的事例:
<Window x:Class="WpfApplication2.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication2"
Title="Window4" Height="369" Width="675">
<Window.Resources>
<Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
<Setter Property="Width" Value="30"></Setter>
<Setter Property="Background" Value="black"></Setter>
</Style>
</Window.Resources>
<Grid>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="180,256,0,0" Name="button1" VerticalAlignment="Top" Click="button1_Click" />
<Label Content="{ StaticResource ResourceKey=myString}" Height="28" HorizontalAlignment="Left" Margin="177,81,0,0" Name="label1" VerticalAlignment="Top" />
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="button2" VerticalAlignment="Top" />
<Button Content="{x:Static local:Window4.Test}" Height="23" HorizontalAlignment="Left" Margin="128,12,0,0" Name="button3" VerticalAlignment="Top" Style="{x:Null}"/>
</Grid>
</Window>
當然了,x:null也可以使用屬性標籤來設定這個值,前面已經講過,在這就不在講了。
4.3.3 x:Array
通過它的item屬性向使用者暴露一個型別已知的ArrayList例項,ArrayList內成員的型別由x:Array的Type指明。下面這個例子就是把ArrayList做為資料來源向一個ListBox提供資料:
<Window x:Class="WpfApplication2.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication2"
Title="Window4" Height="369" Width="675">
<Grid>
<ListBox Height="100" HorizontalAlignment="Left" Margin="435,110,0,0" Name="listBox1" VerticalAlignment="Top" Width="176">
<ListBox.ItemsSource>
<x:Array Type="sys:String">
<sys:String>Jim</sys:String>
<sys:String>Darren</sys:String>
<sys:String>Frank</sys:String>
</x:Array>
</ListBox.ItemsSource>
</ListBox>
</Grid>
</Window>
4.3.4 x:Static
這是一個很常用的標記擴充套件,它的作用是在XAML文件中使用資料型別為static的成員。因為XAML不能編寫邏輯程式碼。所以使用x:Static訪問的static成員一定是資料型別的屬性和欄位。我們看如下一些例子:
public Window4()
{
InitializeComponent();
//SolidColorBrush brush = new SolidColorBrush();
//brush.Color = Colors.Blue;
//this.rectangle1.Fill = brush;
}
public static string Test = "明月鬆間照,清泉石上流。";
<Window x:Class="WpfApplication2.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication2"
Title="Window4" Height="369" Width="675">
<Grid>
<Button Content="{x:Static local:Window4.Test}" Height="23" HorizontalAlignment="Left" Margin="128,12,0,0" Name="button3" VerticalAlignment="Top" Style="{x:Null}"/>
</Grid>
</Window>
如果一個程式需要支援國際化,一般需要把顯示的字串儲存在一個資源類的Static屬性中,所以支援國際化的程式UI中對x:Static的使用相當的頻繁。
4.4 XAML指令元素XAML指令元素只有兩個:
- x:Code
- x:XData
小結: 我已經比較完整的掌握了XAML的語法和常用元素。
寫部落格真的是一件辛苦的事,寫的我頭暈眼花。