天天看點

WPF之XMAL對象引用總結

             我們應該都知道,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的