TOC

This article has been localized into Chinese by the community.

WPF應用程式:

資源(Resources)

WPF引入了一个非常方便的概念:将数据作为一种资源来存储,既可以是控件的本地数据,也可以是整个窗口的本地数据,亦或是整个应用程序的全局数据。这些数据可以是你想要的任何东西,从实际信息到WPF控件的层次结构。这使你能够将数据一处存放,随处使用,非常实用。

这个概念经常用于样式和模板,这些我们将在本教程的后面讨论,但正如本章所说明的那样,你也可以把它用于许多其他方面。请允许我通过一个简单的例子来说明:

<Window x:Class="WpfTutorialSamples.WPF_Application.ResourceSample"
        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"
        Title="ResourceSample" Height="150" Width="350">
    <Window.Resources>
        <sys:String x:Key="strHelloWorld">Hello, world!</sys:String>
    </Window.Resources>
    <StackPanel Margin="10">
        <TextBlock Text="{StaticResource strHelloWorld}" FontSize="56" />
        <TextBlock>Just another "<TextBlock Text="{StaticResource strHelloWorld}" />" example, but with resources!</TextBlock>
    </StackPanel>
</Window>

各個資源將透過「x:Key」屬性給予一個鍵值,這允許你在應用程式的其他地方透過該鍵值結合「StaticResource」之標記延伸來參照該資源。在這個例子中,我只儲存了一個間單的字串,之後使用在兩個不同的「TextBlock」控制項中。

StaticResource vs. DynamicResource

目前為止的例子中,我使用了「StaticResource」之標記延伸來參照資源,然而有另外一個以「DynamicResource」形式的選擇。

主要的差異即是靜態資源僅會在XAML載入的時間點被設定一次,如果這個資源之後被改變,這項改變將不會反映到你使用「StaticResource」的地方。

另一方面,一個「DynamicResource」會在它實際需要時設定一次,並且當資源改變時再次設定。可以想成「繫結一個靜態值」與「繫結一個監視這個值並且每當該值改變時將它傳送給你的函式」的差異,這並不是它實際上運作的方式,不過可以讓你比較容易了解何時該使用哪個。動態資源也允許你使用設計階段尚未存在的資源,例如你在後置程式碼中於應用程式啟動時加入的資源。

更多的資源類別

共用一個簡單字串非常的容易,但你可以做的更多。在下一個例子中,我同時會儲存一個完整的字串陣列搭配一個用於背景的漸層筆刷,這應該能非常好的讓你了解你可以透過資源做到多少事:

<Window x:Class="WpfTutorialSamples.WPF_Application.ExtendedResourceSample"
        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"
        Title="ExtendedResourceSample" Height="160" Width="300"
        Background="{DynamicResource WindowBackgroundBrush}">
    <Window.Resources>
        <sys:String x:Key="ComboBoxTitle">Items:</sys:String>

        <x:Array x:Key="ComboBoxItems" Type="sys:String">
            <sys:String>Item #1</sys:String>
            <sys:String>Item #2</sys:String>
            <sys:String>Item #3</sys:String>
        </x:Array>

        <LinearGradientBrush x:Key="WindowBackgroundBrush">
            <GradientStop Offset="0" Color="Silver"/>
            <GradientStop Offset="1" Color="Gray"/>
        </LinearGradientBrush>
    </Window.Resources>
    <StackPanel Margin="10">
        <Label Content="{StaticResource ComboBoxTitle}" />
        <ComboBox ItemsSource="{StaticResource ComboBoxItems}" />
    </StackPanel>
</Window>

這次我們加入了一些額外的資源,所以我們的視窗現在包含了一個簡單字串、一個字串陣列以及一個「LinearGradientBrush」。字串使用在標籤(label)上,而字串陣列作為「ComboBox」控制項的項目,漸層筆刷則用於整個視窗的背景。如你所見,幾乎任何東西都可以儲存為資源。

區域及應用程式範圍資源

目前我們將資源儲存在視窗階層,也就是你可以在該視窗的任何地方存取這些資源。

如果你需要一個資源僅僅用在特定的控制項上,你可以藉由將資源加在該特定控制項內而非視窗內,來讓資源更區域性。使用方法完全一樣,唯一的差別是你現在只能在你加入資源的控制項範圍內存取該資源。

<StackPanel Margin="10">
    <StackPanel.Resources>
        <sys:String x:Key="ComboBoxTitle">Items:</sys:String>
    </StackPanel.Resources>
    <Label Content="{StaticResource ComboBoxTitle}" />
</StackPanel>

在這個例子中,我們將資源加在一個「StackPanel」中並在它的子控制項「Label」中使用該資源,而其他在「StackPanel」中的控制項也能使用該資源,就如同該子控制項的子項有權存取它一樣。反之,在該特定「StackPanel」外的控制項則無法存取。

如果你需要從多個視窗存取資源也是可行的,在App.xaml檔案中可以容納像是視窗及任何其他種類的WPF控制項資源,而當你將這些資源儲存於App.xaml,專案中的所有視窗及使用者控制項都能夠全域地存取它們,使用方法也和在視窗內儲存及使用時完全一樣。

<Application x:Class="WpfTutorialSamples.App"
             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"
             StartupUri="WPF application/ExtendedResourceSample.xaml">
    <Application.Resources>
        <sys:String x:Key="ComboBoxTitle">Items:</sys:String>
    </Application.Resources>
</Application>

使用時也一樣:WPF會自動地從區域控制項到視窗再到App.xaml向上尋找指定的資源。

<Label Content="{StaticResource ComboBoxTitle}" />

從後置程式碼存取資源

目前為止,我們已經直接從XAML透過標記延伸存取所有我們的資源,然而你當然也可以從後置程式碼中存取你的資源,這在很多情況下非常實用。在前面的例子中,我們看到了我們如何將資源儲存在多個不同的地方,所以在這個例子中,我們將從後置程式碼存取分別儲存在不同範圍內的三個不同的資源:

App.xaml:

<Application x:Class="WpfTutorialSamples.App"
             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"
             StartupUri="WPF application/ResourcesFromCodeBehindSample.xaml">
    <Application.Resources>
        <sys:String x:Key="strApp">Hello, Application world!</sys:String>
    </Application.Resources>
</Application>

視窗:

<Window x:Class="WpfTutorialSamples.WPF_Application.ResourcesFromCodeBehindSample"
        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"
        Title="ResourcesFromCodeBehindSample" Height="175" Width="250">
    <Window.Resources>
        <sys:String x:Key="strWindow">Hello, Window world!</sys:String>
    </Window.Resources>
    <DockPanel Margin="10" Name="pnlMain">
        <DockPanel.Resources>
            <sys:String x:Key="strPanel">Hello, Panel world!</sys:String>
        </DockPanel.Resources>

        <WrapPanel DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10">
            <Button Name="btnClickMe" Click="btnClickMe_Click">Click me!</Button>
        </WrapPanel>

        <ListBox Name="lbResult" />
    </DockPanel>
</Window>

後置程式碼:

using System;
using System.Windows;

namespace WpfTutorialSamples.WPF_Application
{
	public partial class ResourcesFromCodeBehindSample : Window
	{
		public ResourcesFromCodeBehindSample()
		{
			InitializeComponent();
		}

		private void btnClickMe_Click(object sender, RoutedEventArgs e)
		{
			lbResult.Items.Add(pnlMain.FindResource("strPanel").ToString());
			lbResult.Items.Add(this.FindResource("strWindow").ToString());
			lbResult.Items.Add(Application.Current.FindResource("strApp").ToString());
		}
	}
}

誠如你所見,我們儲存了三個不同的「Hello, world!」訊息:一個在App.xaml中、另一個在視窗內、而最後一個是主面板的區域資源。介面則由一個按鈕及一個「ListBox」組成。

在後置程式碼中,我們處理了按鈕的點擊事件,如同截圖所示,我們將各個文字字串加入到「ListBox」中。我們使用「FindResource()」方法,這個方法將會將資源回傳為一個物件(如果有找到),接著我們使用眾所皆知的方法「ToString()」將其轉換為字串。

請注意我們如何對不同的範圍使用「FindResource()」方法:首先對於面板(panel)、再來對於視窗、最後對於當前應用程式物件。在我們已知的位置找尋資源是很合理的,不過就如同前面提到的,如果資源沒有找到,搜尋程序將會往更高的階層找尋。所以原則上在上面三個情況下,由於在找不到的時候程序都會繼續向上對於視窗階層、接著對於應用程式階層找尋,因此我們可以都對於面板使用「FindResource()」方法。

相反的情況則不成立,搜尋將不會反向往下遍歷樹狀結構,所以如果你的資源定義在控制項或視窗,你不能從應用程式階層開始尋找資源。


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!