概念
對象資源是使用代碼定義的一系列可以重用的對象,包括 畫刷,樣式,模板 等。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都可以。
使用資源字典
-
將本項目中的資源文件共享到項目中所有地方(修改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>
-
引用別的項目中的資源
要想引用別的項目中的資源首先需要引用資源所在項目,然後下面介紹兩種引用的方法
// 使用代碼引用別的項目中的資源(只能用在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一定要理解喫透。