我們應該都知道,XMAL是一種聲明式語言,XMAL的标簽聲明的就是對象。一個XMAL标簽會對應着一個對象,這個對象一般會是一個控件類的執行個體。在.Net平台上,所有類都是引用類型,我們是通過引用來通路對象執行個體。當一個對象執行個體不再被任何引用者引用時,它将自動被GC回收。
在WPF開發過程中,背景代碼經常會操作前台XMAL聲明的控件對象執行個體,這就涉及到如何查找這些控件對象的引用。
有兩種方式可以查找前台控件對象的引用,以下分别介紹:
方式1:利用引用者的層級關系來查找控件對象引用
window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="WindowServer" Height="300" Width="300" MaxWidth="500" MaxHeight="600" MinHeight="200" MinWidth="200">
<StackPanel >
<Button Content="OK" Margin="5" Click="Button_Click"/>
<TextBox Margin="5,0" Height="100" >
</StackPanel>
</Window>
以上是用XMAL申明了兩個控件對象執行個體,下面背景代碼Button控件點選事件器,需要完成對TextBox的内容填字一段字元串。
private void Button_Click(object sender, RoutedEventArgs e)
{
StackPanel stackPanel = this.Content as StackPanel; //this.Content屬性引用着StackPanel的執行個體
TextBox textBox = stackPanel.Children[1] as TextBox; //StackPanel執行個體的Children[1]引用着TextBox執行個體
textBox.Text = "Hello,World!";
}
通過這個關系就可以一路查找下來,同時進行類型的轉換。看懂以上關系,那麼就很容易知道stackPanel.Childere[0]引用的對象是什麼了。
方式2:利用XMAL申明的控件引用變量名引用控件對象執行個體
window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="WindowServer" Height="300" Width="300" MaxWidth="500" MaxHeight="600" MinHeight="200" MinWidth="200">
<StackPanel >
<Button x:Name="button" Content="OK" Margin="5" Click="Button_Click"/>
<TextBox x:Name="textBox" Margin="5,0" Height="100" >
</StackPanel>
</Window>
我們從方式1很容易發現,使用這種方法通路UI上的所有控件對象執行個體太繁瑣了。XMAL通過為對象聲明引用變量,背景代碼通過引用變量就可以直接擷取到該對象引用。
而XMAL申明對象的引用變量是使用x:Name來做到的。原則上,所有UI控件都存在該屬性。
檢視以上XMAL申明,發現它和方式1唯一的差別是控件了x:Name的引用變量聲明。這時,我們的背景代碼擷取控件對象執行個體方法如下:
private void Button_Click(object sender, RoutedEventArgs e)
{
// StackPanel stackPanel = this.Content as StackPanel; //this.Content屬性引用着StackPanel的執行個體
//TextBox textBox = stackPanel.Children[1] as TextBox; //StackPanel執行個體的Children[1]引用着TextBox執行個體
textBox.Text = "Hello,World!";
}
這段代碼通過XMAL申明的對象引用變量直接通路對象,相對方式1确實簡潔多了,呵呵。那麼如何我們需要通路Button對象呢,當然直接使用button就OK了。
這裡需要提一點,x:Name申明對象引用變量的同時,如果控件存在Name屬性,那麼采用x:Name将一舉兩得,可自動對Name屬性指派,指派的名稱當然與引用變量名稱相同。是以,建議使用x:Name,因為它的功能已涵蓋了控件的Name屬性功能,可以增強代碼的統一性和可讀性。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
背景代碼除了經常通路前台XMAL聲明的控件對象執行個體,還會經常通路前台聲明的控件資源。
看一下以下這段XMAL申明:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="WindowServer" Height="300" Width="300" MaxWidth="500" MaxHeight="600" MinHeight="200" MinWidth="200">
<Window.Resources>
<sys:String x:Key="myString">Hello,World!</sys:String>
</Window.Resources>
<StackPanel>
<Button x:Name="button1" Content="Send Command" Margin="5" Click="Button_Click" >
<TextBox x:Name="textBoxA" Margin="5,0" Height="100" >
</StackPanel>
</Window>
這段XMAL聲明和之前的沒什麼差別,除了黑體字部分。這段申明是向Window.Resources資源字典添加一個條目,條目的内容是一段字元串。
再仔細觀察,你會發現除了字元串,還有x:Key="myString"這一語句。它的作用是幹嘛呢,很簡單是建立資源字典的鍵值對,友善索引資源條目。如何使用過C++的STL,對Map應該就不陌生。這個x:Key就相當于Map的key。下面看一看,背景代碼如何通路這段字元串。
private void Button_Click(object sender, RoutedEventArgs e)
{
string str = this.FindResource("myString") as string; //調用一個擁有Resource屬性對象的FindResource方法就可以從資源字典裡檢索資源
this.textBox.Text = str;
}
值得注意一下,檢索資源傳回的是Object對象,需要根據相應類型轉換即可。
除了背景代碼經常檢索控件資源,前台控件也可能會通路該資源,引用的方法是使用StaticResource或DynamicResource.
下面的示例在page的根元素定義了一個SolidColorBrush畫刷作為一個資源,并展示如何用它來設定Page中子元素的屬性
<Page Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Page.Resources>
<SolidColorBrush x:key=”MyBrush” Color=”Gold”/>
<Style TargetType=”Border” x:Key=”PageBackground”>
<Setter Property=”Background” Value=”Blue”/>
</Style>
<Page.Resources>
<SolidColorBrush x:Key="MyBrush" Color="Gold"/>
<Style TargetType="Border" x:Key="PageBackground">
<Setter Property="Background" Value="Blue"/>
</Style>
</Page.Resources>
<StackPanel>
<DockPanel>
<Button DockPanel.Dock="Top" HorizontalAlignment="Left" Height="30" Background="{StaticResource MyBrush}" Margin="40">Button</Button>
</DockPanel>
</StackPanel>
</Page>
每個架構級别的元素(FrameworkElement或FrameworkContentElement)都有一個Resources屬性,我們可以在任何元素上定義資源,不過習慣上在根元素上定義,如上面 的xmal代碼中<Page.Resources/>定義資源。
通過屬性x:Key給每個資源賦予一個唯一的關鍵字。這樣我們就可以在Xmal的其它地方通過Key值來操作對應的資源了。如下示例,使用資源給元素的屬性指派
<Button Background=”{StaticResource MyBrush}” />
StaticResource 和DynamicResource的差別:
資源可以被當作StaticResource和DynamicResource兩種類型來引用。
當引用資源時,下面的考慮将影響你是選擇StaticResource還是DynamicResource來它。
1〉 如何為應用程式建立資源(資源是在一個Page中,在APP範圍還是在松散的Xaml中或僅僅在程式集中)
2〉 應用程式功能:是否在運作時改變資源
3〉 每個資源引用類型不同的尋找行為
StaticResources
StaticResources在如下情況下使用比較好
1〉 在資源第一次引用之後無需再修改資源的值
2〉 StaticResource引用不會基于運作時的行為進行重新計算。比如在重新加載Page的時候。
3〉 當需要設定的屬性不是DependencyObject或Freezable類型的時候,需要用staticResource
4〉 當需要将資源編譯到dll中,并打包為程式的一部份,或者希望在各應用程式之間共享
5〉 當需要為一個自定義控件建立一個theme,并theme中使用資源,就需要使用StaticResource。因為StaticResource的資源查找行為時可預測的,并且本身包含在theme中。而對于DynamicResource,即使資源是定義在theme中,也隻能等到運作時确定,導緻一些可能意料不到的情況發生。
6〉 當需要使用資源設定大量的依賴屬性的時候(dependency property),依賴屬性具有屬性系統提供的值緩存機制,是以如果能在程式裝載時設定依賴屬性的值,依賴屬性就不需要檢查自己的值并傳回最後的有效值了。可以獲得顯示時的好處。
Static resource 查詢行為
1〉 查找使用該資源的元素的resource字典
2〉 順邏輯樹向上查找父元素的資源字典,直到根節點
3〉 查找Application資源
4〉 不支援向前引用。即不能引用在引用點之後才定義的資源
Dynamic Resource
Dynamic resources一般使用在如下場合
1〉 資源的值依賴一些條件,而該條件直到運作時才能确定。這包括系統資源,或是使用者可設定的資源。例如,可以建立引用系統屬性諸如SystemColors,SystemFonts來設定值,這些屬性是動态的,他們的值來自于運作環境和作業系統
2〉 為自定義控件引用或建立theme style
3〉 希望在程式運作期間調整資源字典的内容
4〉 希望資源可以向前引用
5〉 資源檔案很大,希望在運作時加載
6〉 要建立的style的值可能來自于其它值,而這些值又依賴于themes或使用者設定
7〉 當引用資源的元素的父元素有可能在運作期改變,這個時候也需要使用動态資源。因為父元素的改變将導緻資源查詢的範圍。
Dynamic resource 查詢行為
1〉 查找使用該資源的元素的resource字典
如果元素定義了一個Style 屬性,将查找Style中的資源字典
如果元素定義了一個Template屬性,将查找FrameworkTemplate中的資源字典
2〉 順邏輯樹向上查找父元素的資源字典,直到根節點
3〉 查找Application資源
4〉 查找目前激活狀态下的theme資源字典。
5〉 查找系統資源
Dynamic resource的限制條件
1〉 屬性必須是依賴屬性,或是Freezable的