XAML 資源概觀 (WPF .NET)
資源是可在應用程式中不同位置重複使用的物件。 資源的範例包括筆刷和樣式。 此概觀描述如何使用 Extensible Application Markup Language(XAML) 中的資源。 您也可以使用程式代碼來建立和存取資源。
注意
本文所述的 XAML 資源與 應用程式資源 不同,這些資源通常是新增至應用程式的檔案,例如內容、數據或內嵌檔案。
在 XAML 中使用資源
下列範例會將 SolidColorBrush 定義為頁面之根項目上的資源。 然後,此範例會參考資源,並用它來設定數個子項目的屬性,包括 Ellipse、 TextBlock和 Button。
<Window x:Class="resources.ResExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ResExample" Height="400" Width="300">
<Window.Resources>
<SolidColorBrush x:Key="MyBrush" Color="#05E0E9"/>
<Style TargetType="Border">
<Setter Property="Background" Value="#4E1A3D" />
<Setter Property="BorderThickness" Value="5" />
<Setter Property="BorderBrush">
<Setter.Value>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="#4E1A3D"/>
<GradientStop Offset="1.0" Color="Salmon"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TextBlock" x:Key="TitleText">
<Setter Property="FontSize" Value="18"/>
<Setter Property="Foreground" Value="#4E87D4"/>
<Setter Property="FontFamily" Value="Trebuchet MS"/>
<Setter Property="Margin" Value="0,10,10,10"/>
</Style>
<Style TargetType="TextBlock" x:Key="Label">
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="Foreground" Value="{StaticResource MyBrush}"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Margin" Value="0,3,10,0"/>
</Style>
</Window.Resources>
<Border>
<StackPanel>
<TextBlock Style="{StaticResource TitleText}">Title</TextBlock>
<TextBlock Style="{StaticResource Label}">Label</TextBlock>
<TextBlock HorizontalAlignment="Right" FontSize="36" Foreground="{StaticResource MyBrush}" Text="Text" Margin="20" />
<Button HorizontalAlignment="Left" Height="30" Background="{StaticResource MyBrush}" Margin="40">Button</Button>
<Ellipse HorizontalAlignment="Center" Width="100" Height="100" Fill="{StaticResource MyBrush}" Margin="10" />
</StackPanel>
</Border>
</Window>
每個架構等級項目(FrameworkElement 或 FrameworkContentElement)都有一個 Resources 屬性,這是包含已定義資源的 ResourceDictionary 類型。 您可以在任何項目上定義資源,例如 Button。 不過,資源最常定義於根項目上,即範例中的 Window。
資源字典中的每個資源都必須有唯一索引鍵。 當您在標記中定義資源時,您可以透過 x:Key 指示詞指派唯一索引鍵。 索引鍵通常是字串;不過,您也可以使用適當的標記延伸,將它設定為其他物件類型。 WPF 中某些功能區域會使用非字串索引鍵,特別是樣式、元件資源和數據樣式。
您可以使用已定義的資源搭配資源標記延伸語法,以指定資源的索引鍵名稱。 例如,使用資源作為另一項目上的屬性值。
<Button Background="{StaticResource MyBrush}"/>
<Ellipse Fill="{StaticResource MyBrush}"/>
在上述範例中,當 XAML 載入器處理 {StaticResource MyBrush}
上 Background 屬性值 Button時,資源查閱邏輯會先檢查 Button
項目的資源字典。 如果 Button
沒有資源索引鍵的定義 MyBrush
(在此範例中並非如此;其資源集是空的),則查閱會檢查 Button
的父元素。 如果未在父代上定義資源,它會繼續向上檢查物件的邏輯樹狀結構,直到找到為止。
如果您在根元素上定義資源,邏輯樹狀結構中的所有元素 (Window 或 Page) 都可以存取它。 而且,您可以重複使用相同的資源來設定接受資源所代表之相同類型之任何屬性的值。 在前一個範例中,相同的 MyBrush
資源會設定兩個不同的屬性:Button.Background 和 Ellipse.Fill。
靜態和動態資源
資源參考可以是靜態或動態。 參考是使用 StaticResource 標記延伸 或 DynamicResource 標記延伸來建立。 標記延伸是一項 XAML 功能,您可以使用這項功能,讓標記延伸處理屬性字串並將物件傳回 XAML 載入器,以指定物件參考。 如需標記延伸行為的詳細資訊,請參閱標記延伸和 WPF XAML。
當您使用標記延伸時,您通常會提供一或多個字串形式的參數,以供該特定標記延伸處理。 StaticResource 標記延伸會藉由在所有可用的資源字典中尋找該索引鍵的值來處理索引鍵。 載入時會處理,也就是在載入需要指派屬性值的程式時。 DynamicResource 標記延伸 則是藉由建立運算式來處理索引鍵,該運算式會保持未評估,直到應用程式執行,此時運算式會進行評估以提供值。
當您參考資源時,下列考量可能會影響您使用靜態資源參考或動態資源參考:
考量以下因素,確定應用程式資料建立方式的整體設計(每頁、在應用程式中、在鬆散的 XAML 中、或資源專用組件中):
應用程式的功能。 是否在應用程式需求中即時更新資源?
該資源參考型別的相關查閱行為。
特定屬性或資源類型,以及這些類型的原生行為。
靜態資源
靜態資源參考最適用於下列情況:
應用程式設計將其大部分的資源集中到頁面或應用程式層級資源字典中。
靜態資源參考不會根據執行階段行為重新評估,例如重載頁面。 因此,當資源和應用程式設計不需要動態資源參考時,可能會有一些效能優點,以避免大量的動態資源參考。
您正在設定不在 DependencyObject 或 Freezable上的屬性值。
您正在建立資源字典,該字典會編譯成應用程式之間共用的 DLL。
您要建立自訂控制項的佈景主題,然後定義用於佈景主題的資源。
在此情況下,您通常不想要動態資源參考查閱行為。 請改用靜態資源參考行為,讓查閱可預測且獨立於主題。 使用動態資源參考時,甚至是佈景主題內的參考也會在執行階段之前保持未評估。 在套用佈景主題時,有些區域元素可能會重新定義佈景主題嘗試參考的索引鍵,而且區域元素將會落在查閱中的佈景主題本身之前。 如果發生這種情況,您的佈景主題不會如預期般運作。
您要使用資源來設定大量相依性屬性。 相依性屬性具有屬性系統已啟用的有效值快取,因此如果您將值提供給可在載入時評估的相依性屬性,相依性屬性不必檢查是否有重新評估的運算式,而且可能傳回最後一個有效值。 這項技術可能會提升效能。
您想要變更所有消費者的基礎資源,或想要使用 x:Shared 屬性為每個消費者維護不同的可寫入執行個體。
靜態資源查閱行為
下列描述屬性或項目參考靜態資源時自動發生的查閱流程:
查閱程序會檢查設定屬性之項目所定義的資源字典內,是否有要求的索引鍵。
查閱程序接著會在邏輯樹狀結構中,向上周游到父項目及其資源字典。 此程式會繼續直到到達根項目。
已檢查應用程式資源。 應用程式資源是位於 Application WPF 應用程式的物件所定義之資源字典內的資源。
資源字典內的靜態資源參考必須參考資源參考之前已在語彙上定義的資源。 靜態資源參考無法解析向前參考。 因此,設計資源字典結構,讓所有資源都在每個相關資源字典開頭或附近定義。
靜態資源查閱可以延伸到佈景主題或系統資源中,但是因為 XAML 載入器會延遲要求,所以此查閱才支援此做法。 為了將頁面載入時的執行階段佈景主題正確套用至應用程式,必須有此延遲。 不過,不建議已知只存在於佈景主題或系統資源的索引鍵的靜態資源參考,因為如果使用者即時變更主題,就不會重新評估這類參考。 當您要求佈景主題或系統資源時,動態資源參考會更可靠。 但佈景主題項目本身要求另一個資源時則例外。 這些參考應該是靜態資源參考,原因如前所述。
找不到靜態資源參考時的例外,狀況行為會有所不同。 如果已延遲資源,則會在執行階段發生例外狀況。 如果未延遲資源,則會在載入時發生例外狀況。
動態資源
動態資源在下列情況下運作最佳:
資源的值,包括系統資源,或其他使用者可設定的資源,取決於執行階段之前未知的條件。 例如,您可以建立 setter 值,將系統屬性顯示為 SystemColors、 SystemFonts或 SystemParameters。 由於這些值最終是來自使用者和作業系統的執行階段環境,因此是真正動態的。 您也可能有會變更的應用程式層級佈景主題,其中的頁面層級資源存取也必須擷取該變更。
您要建立或參考自訂控制項的佈景主題樣式。
您想要在應用程式存留期內調整 ResourceDictionary 的內容。
您有內含相互依存性的複雜資源結構,可能需要向前參考。 靜態資源參考不支援向前參考,但動態資源參考則支援,因為這些資源不需要在執行階段之前評估,因此向前參考不是相關概念。
您所參考的資源從編譯或工作集的觀點來看很大,因此當頁面載入時,可能無法立即使用資源。 頁面載入時,一律會從 XAML 載入靜態資源參考。 不過,在使用之前不會載入動態資源參考。
您要建立樣式,其中的 setter 值可能來自受到佈景主題或其他使用者設定影響的其他值。
您要將資源套用至可能在應用程式存留期間於邏輯樹狀結構中重設父代的項目。 變更父代也可能會變更資源查閱範圍,因此如果您想要根據新範圍重新評估重設父代項目的資源,請一律使用動態資源參考。
動態資源查閱行為
如果您呼叫 FindResource 或 SetResourceReference,動態資源參考的資源查閱行為會平行處理程序代碼中的查閱行為:
查閱會檢查設定屬性之項目所定義的資源字典內,是否有要求的索引鍵:
該項目是否定義 Style 屬性,項目的 System.Windows.FrameworkElement.Style 會檢查其 Resources 字典。
該項目是否定義 Template 屬性,則會檢查該項目的 System.Windows.FrameworkTemplate.Resources 字典。
查閱會在邏輯樹狀結構中,向上周游到父項目及其資源字典。 此程式會繼續直到到達根項目。
已檢查應用程式資源。 應用程式資源是位於 Application WPF 應用程式的物件所定義之資源字典內的資源。
檢查目前使用中佈景主題的佈景主題資源字典。 如果佈景主題在執行階段變更,則會重新評估其值。
檢查系統資源。
例外狀況行為 (如果有的話) 則有所不同:
如果 FindResource 呼叫要求資源,但找不到資源,則會擲回一個例外狀況。
如果 TryFindResource 呼叫要求資源且找不到,但未擲回例外狀況,則傳回的值為
null
。 如果所設定的屬性不接受null
,則仍然可能會擲回更深入的例外狀況 (這取決於所設定的個別屬性)。如果 XAML 中的動態資源參考要求資源,但找不到資源,則行為會取決於一般屬性系統。 一般行為就像資源所在的層級上沒有發生任何屬性設定作業一樣。 例如,如果您嘗試使用無法評估的資源來設定個別按鈕項目的背景,則沒有值會設定結果,但有效值仍然可能來自屬性系統和值優先順序中的其他參與值。 例如,背景值仍然可能來自本機定義的按鈕樣式,或來自佈景主題樣式。 針對不是由佈景主題樣式定義的屬性,資源評估失敗後的有效值可能來自屬性中繼資料內的預設值。
限制
動態資源參考有一些值得注意的限制。 至少必須符合下列其中一個情況:
所設定的屬性必須是 FrameworkElement 或 FrameworkContentElement上的屬性。 該屬性必須由 DependencyProperty支援。
參考適用於
StyleSetter
內的值。所設定的屬性必須是 Freezable 上的屬性,該屬性會以 FrameworkElement 或 FrameworkContentElement 屬性或 Setter 值的形式提供。
由於所設定的屬性必須是 DependencyProperty 或 Freezable 屬性,因此大多數屬性變更可以傳播至 UI,因為屬性變更 (變更的動態資源值) 是由屬性系統認可。 大多數控制項所包含的邏輯,將會在 DependencyProperty 變更時強制執行控制項的另一個版面配置,而且其中的屬性可能會影響版面配置。 不過,並非所有以 DynamicResource 標記延伸作為其值的屬性,都保證會以可在 UI 中即時更新。 該功能仍然可能會依屬性、擁有屬性的類型,甚至是應用程式的邏輯結構而有所不同。
樣式、DataTemplate 和隱含索引鍵
雖然 ResourceDictionary 中的所有項目都必須有一個索引鍵,但這並不表示所有資源都必須有一個明確的 x:Key
。 有幾種物件類型會在定義為資源時支援隱含索引鍵,其中索引鍵值會繫結至另一個屬性的值。 這類索引鍵稱為隱含索引鍵,而 x:Key
屬性是明確索引鍵。 您可以藉由指定明確索引鍵,來覆寫任何隱含索引鍵。
這些資源一個很重要的適用情況,是當您定義 Style時。 事實上, Style 幾乎一定會定義為資源字典中的項目,因為樣式原本就是為了重複使用而設計。 如需樣式的詳細資訊,請參閱樣式和範本 (WPF .NET)。
控制項的樣式可以透過隱含索引鍵來建立及參考。 定義預設控制項外觀的佈景主題樣式會依賴此隱含索引鍵。 從要求的觀點來看,隱含索引鍵是控制項本身的 Type。 從定義資源的觀點來看,隱含索引鍵是樣式的 TargetType 。 因此,如果您要建立自訂控制項的佈景主題,以及建立與現有佈景主題樣式互動的樣式,則不需要針對該 指定Style。 此外,如果您想要使用佈景主題樣式,則完全不需要指定任何樣式。 例如,下列樣式定義會運作正常,不過 Style 資源似乎沒有索引鍵:
<Style TargetType="Button">
<Setter Property="Background" Value="#4E1A3D" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderThickness" Value="5" />
<Setter Property="BorderBrush">
<Setter.Value>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="#4E1A3D"/>
<GradientStop Offset="1.0" Color="Salmon"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
該樣式確實有索引鍵:隱含索引鍵:System.Windows.Controls.Button
類型。 在標記中,您可以將 TargetType 直接指定為類型名稱(或選擇性地使用 {x:Type...} 來傳回 Type。
透過WPF使用的預設佈景主題樣式機制,該樣式會套用為頁面上 Button 的執行階段樣式,即使 Button 本身不會嘗試指定其 Style 屬性或樣式的特定資源參考。 定義於頁面中的樣式會在查閱順序的佈景主題字典樣式之前找到,並使用佈景主題字典樣式所具有的相同索引鍵。 只要在頁面中任何位置指定 <Button>Hello</Button>
,以 TargetType 的 Button
定義的樣式就會套用至該按鈕。 如果需要,您仍然可以明確地使用與 TargetType 相同類型的值,明確建立樣式的索引鍵,以在標記中清楚呈現,但這並不必要。
如果 OverridesDefaultStyle 為 true
,則控制項不適用樣式的隱含索引鍵。 (另請注意,OverridesDefaultStyle 可能會被設定成控件項類別原生行為的一部分,而不是明確設定在控控項的實例上。)此外,為了支援衍生類別情節的隱含索引鍵,控制項必須覆寫 DefaultStyleKey (作為 WPF 的一部分提供的所有現有控件項,包含此覆寫)。 如需樣式、佈景主題和控制項設計的詳細資訊,請參閱設計可設定樣式控制項的方針。
DataTemplate 也有隱含索引鍵。 DataTemplate 的隱含索引鍵是 DataType 屬性值。 DataType 也可以指定為類型的名稱,而不是明確使用 {x:Type...}。 如需詳細資訊,請參閱資料範本化概觀。