WPF高級教程(十五)對象資源

概念

對象資源是使用代碼定義的一系列可以重用的對象,包括 畫刷,樣式,模板 等。WPF允許在代碼中以及在xaml中各個位置定義對象資源。

資源集合

在FrameworkElement中定義了一個Resources屬性,該屬性使用Resource Dictionary類的實例填充,用於存儲元素上的資源。根據我們之前學習的體系結構,定義在FrameworkElement也就表明了所有的WPF元素都具有資源的屬性。

每個元素的子元素可以訪問自己的和父元素的資源。所以一般我們將資源定義在窗口的資源屬性中。

下面我們舉例看一下資源的定義和使用

<Window.Resources>
    <SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<Grid>
    <StackPanel>
        <Button Background="{StaticResource RedBrush}">測試1</Button>
    </StackPanel>
</Grid>

資源層次

我們之前講了,元素會從自己的資源屬性和父元素的資源屬性中查找資源進行使用,事實上窗口元素也不是最後一站,其查找層次是:

自己的資源 - 父元素的資源 - 應用程序資源 - 系統資源

其中應用程序資源和系統資源我們下面再將,我們可以先看一個問題:如果自己有一個資源,父元素中有一個同名的資源,會怎麼樣呢?

首先我們先問行不行,再看怎麼樣,能否同名呢?答案是能,下面的例子展示了這種情況

<Window.Resources>
    <SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<Grid>
    <StackPanel>
        <Button>
            <Button.Resources>
                <SolidColorBrush x:Key="RedBrush" Color="Blue"/>
            </Button.Resources>
            <Button.Background>
                <StaticResource ResourceKey="RedBrush"/>
            </Button.Background>
            <Button.Content>
                測試1
            </Button.Content>
        </Button>
    </StackPanel>
</Grid>

上面代碼在按鈕的Resources中也定義了同名的Brush,最終運行結果是 藍色,說明元素在碰到同名資源的時候優先使用自己的,換句話說,自己的資源會覆蓋父元素的資源。

我們再看下面這段代碼

<Window.Resources>
    <SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<Grid>
    <StackPanel>
        <Button Background="{StaticResource RedBrush}">
            <Button.Resources>
                <SolidColorBrush x:Key="RedBrush" Color="Blue"/>
            </Button.Resources>
            <Button.Content>
                測試1
            </Button.Content>
        </Button>
    </StackPanel>
</Grid>

運行結果是紅色,說明資源必須是在使用之前定義的才能索引到,定義在使用之後的不能被查找到。

靜態資源和動態資源

  • 靜態資源只加載一次,之後的變化不會響應
  • 動態資源會隨着資源的變化而變化

我們直接看一個例子,還是剛剛的例子,我們給Button設置一個Click事件,事件中寫入下面的代碼

private void Button_Click(object sender, RoutedEventArgs e)
{
    this.Resources["RedBrush"] = new SolidColorBrush(Colors.Blue);
}

我們測試發現,當我們使用

Background="{DynamicResource RedBrush}"

綁定的時候,按下按鈕,按鈕顏色變化爲藍色,當我們使用靜態綁定則不會有任何顏色的變化。

兩者對比

  • 靜態資源加載較快,在創建窗口的時候就加載完成了
  • 動態資源消耗更大,在使用的時候纔會加載

當我們碰到下面的情況的時候必須使用動態資源

  • 資源依賴於系統的屬性設置(主題顏色等)
  • 需要使用代碼替換資源的時候

由於兩者加載時機的不同,在資源很大並且很複雜的情況之下,使用動態資源可以提升加載窗口的速度。

資源的共享與非共享

通常我們使用的資源都是共享的,即資源是一個對象,大家使用的都是這個對象,但是WPF同樣允許我們使用非共享的對象,這個技術十分不常使用,大家瞭解即可

<SolidColorBrush x:Key="RedBrush" Color="Red" x:Shared="False"/>

使用代碼訪問資源

  • 在擁有Resources屬性的元素上使用 FindResource 方法查找資源
  • 在擁有Resources屬性的元素上使用 TryFindResource 方法查找資源,這個方法沒有找到不會報異常
  • 可以給元素的Resources屬性調用Add方法添加資源

應用程序資源

我們可以在App.xaml中定義資源,我們之前學過,App.xaml中有一個Application元素,同樣,我們可以在Application的Resources屬性中添加資源。

應用程序資源用於在不同的窗口中共享資源。

<Application x:Class="WpfApp1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp1"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <SolidColorBrush x:Key="AppBrush" Color="AliceBlue"/>
    </Application.Resources>
</Application>

系統資源

如果我們想要構建一個跟隨系統主題顏色變化變化,跟隨系統字體變化,跟隨系統字號變化的軟件,就必須使用系統資源。

系統資源其實就是系統提供的一系列靜態的資源,有下面三個類

  • SystemColors 系統顏色設置
  • SystemFonts 字體設置
  • SystemParameters 屏幕設置 鼠標設置 陰影 拖放等

如果不要求跟隨系統設置變化實時變化,我們可以使用下面的方法進行系統資源的使用

<Button Background="{x:Static SystemColors.WindowTextBrush}">Test</Button>

或是在代碼中使用

// 使用系統顏色
btn.Background = new SolidColorBrush(SystemColors.WindowTextColor);

// 使用系統畫刷
btn.Background = SystemColors.WindowTextBrush;

這裏如果我們需要系統變化的時候變化這個資源,就需要使用到動態綁定,明顯這裏動態綁定不是那麼方便,如何做呢,我們使用下面的代碼

<Button x:Name="btn" Background="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">Test</Button>

最後我們總結一下,靜態的顏色資源都提供了三個版本

  • 顏色版本,供我們構建畫刷
  • 畫刷版本,可以直接使用
  • Key版本,供我們動態綁定

資源字典 Resource Dictionary

上文中我們學習了對象資源,知道了資源集合,也知道了資源使用的優先級,查找的順序,也知道了資源定義的幾個位置以及系統內置的資源,但是我們都是現成的資源屬性,我們之前講了資源屬性是由 Resource Dictionary 來構建的,它是一系列資源的集合,那我們就來學學 Resource Dictionary,學習了資源字典之後,我們就能將一系列的資源放到一個字典中,一起共享到別的項目中。

創建資源字典

創建資源字典我們可以像創建一個類一樣,創建新建項,選擇資源字典,系統就會幫我們創建一個資源字典,如下面的代碼

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfApp1">
    
</ResourceDictionary>

需要注意的是,資源文件的生成屬性可以設置成Page或者Resource都可以。

使用資源字典

  1. 將本項目中的資源文件共享到項目中所有地方(修改App.xaml)

    <Application x:Class="StyleStudy.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:StyleStudy"
             StartupUri="MainWindow.xaml">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Generic.xaml"></ResourceDictionary>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    在項目中的其他地方,可以直接使用這個資源

    <TextBlock Text="{StaticResource nameStr}" FontWeight="{StaticResource fontWeight}"></TextBlock>
    
  2. 引用別的項目中的資源

    要想引用別的項目中的資源首先需要引用資源所在項目,然後下面介紹兩種引用的方法

    // 使用代碼引用別的項目中的資源(只能用在cs代碼中)
    ResourceDictionary resource = new ResourceDictionary();
    resource.Source = new Uri("Views;component/Brushes.xaml", UriKind.Relative);
    // resource指向了資源文件,使用字典的取值語法就可以獲取相應的資源
    textBox.Foreground = (SolidColorBrush)resource["MainColor"];
    
    // xaml中引入別的項目的資源文件
    <Application x:Class="StyleStudy.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:StyleStudy"
         StartupUri="MainWindow.xaml">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Generic.xaml"></ResourceDictionary>
                    <ResourceDictionary Source="/StyleStudy.Views;component/Themes/Default/Generic.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    注意要使用MergedDictionaries就可以將資源字典整個提升到項目級別在多窗口中共享,結合我們之前講的Uri的語法,就可以Merge其他項目的資源字典,這個在多項目的解決方案中十分有用。

    在不同程序集之間共享資源還有一種辦法就是使用ComponentResourceKey,這種方法相較於上面兩種辦法來說要複雜的多,我們這裏就不詳細講了,感興趣的同學可以自行查找相關資料進行學習,然而,掌握了上面講的方法在實際的開發中就完全夠用了,尤其是MergedDictionaries一定要理解喫透。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章