一、什么是動態資源和靜態資源
靜態資源(StaticResource)和動態資源(DynamicResource)
資源可以作為靜態資源或動態資源進行引用。這是通過使用 StaticResource 標記擴展或 DynamicResource 標記擴展完成的。
StaticResource 通過替換已定義資源的值(x:Key)來為 XAML 屬性提供值。
DynamicResource 通過將值推遲為對資源的運行時引用來為XAML 屬性提供值。動態資源引用強制在每次訪問此類資源時都重新進行查找。
通常來說,不需要在運行時更改的資源使用靜態資源;而需要在運行時更改的資源使用動態資源。動態資源需要使用的系統開銷大於靜態資源的系統開銷。例如以下的例子:
<Window x:Class="WPFResource.StaticAndDynamicResource" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="StaticAndDynamicResource" Height="200" Width="300"> <Window.Resources> <SolidColorBrush x:Key="ButtonBrush" Color="Red" /> </Window.Resources> <StackPanel> <Button Margin="5" Content="Static Resource Button A" Background="{StaticResource ButtonBrush}" /> <Button Margin="5" Content="Static Resource Button B" Background="{StaticResource ButtonBrush}"> <Button.Resources> <SolidColorBrush x:Key="ButtonBrush" Color="Yellow" /> </Button.Resources> </Button> <Button Margin="5" Content="Change Button Resource" Click="Button_Click" /> <Button Margin="5" Content="Dynamic Resource Button A" Background="{DynamicResource ButtonBrush}" /> <Button Margin="5" Content="Dynamic Resource Button B" Background="{DynamicResource ButtonBrush}"> <Button.Resources> <SolidColorBrush x:Key="ButtonBrush" Color="Yellow" /> </Button.Resources> </Button> </StackPanel> </Window> private void Button_Click(object sender, RoutedEventArgs e) { SolidColorBrush brush = new SolidColorBrush(Colors.Green); this.Resources["ButtonBrush"] = brush; }
以上的例子在運行時顯示如下:
而點擊“Change Button Resource”按鈕后,顯示的結果為:
從程序執行的結果來看,我們可以得到如下的結論:
- 靜態資源引用是從控件所在的容器開始依次向上查找的,而動態資源的引用是從控件開始向上查找的(即控件的資源覆蓋其父容器的同名資源)
- 更改資源時,動態引用的控件樣式發生變化(即"Dynamic Resource Button A"發生變化)
如果要更改"Dynamic Resource Button B"的背景,需要在按鈕的事件中添加以下代碼(將"Dynamic Resource Button B"的控件的x:Name設置為"btn4")
SolidColorBrush brushB = new SolidColorBrush(Colors.Blue); this.btn4.Resources["ButtonBrush"] = brushB;
執行的結果如下:
靜態資源引用最適合於以下情況:
-
您的應用程序設計幾乎將所有的應用程序資源集中到頁或應用程序級別的資源字典中。靜態資源引用不會基於運行時行為(例如重新加載頁)進行重新求值,因此,根據您的資源和應用程序設計避免大量不必要的動態資源引用,這樣可以提高性能。
-
您正在設置不在 DependencyObject 或 Freezable 上的屬性的值。
-
您正在創建將編譯為 DLL 並打包為應用程序的一部分或在應用程序之間共享的資源字典。
-
您正在為自定義控件創建一個主題,並定義在主題中使用的資源。對於這種情況,通常不需要動態資源引用查找行為,而需要靜態資源引用行為,以使該查找可預測並且獨立於該主題。使用動態資源引用時,即使是主題中的引用也會直到運行時才進行求值,並且在應用主題時,某個本地元素有可能會重新定義您的主題試圖引用的鍵,並且本地元素在查找中會位於主題本身之前。如果發生該情況,主題將不會按預期方式運行。
-
您正在使用資源來設置大量依賴項屬性。依賴項屬性具有由屬性系統啟用的有效值緩存功能,因此,如果您為可以在加載時求值的依賴項屬性提供值,該依賴項屬性將不必查看重新求值的表達式,並且可以返回最后一個有效值。該方法具有性能優勢。
-
您需要為所有使用者更改基礎資源,或者需要通過使用 x:Shared 屬性為每個使用者維護獨立的可寫實例。
動態資源最適合於以下情況:
-
資源的值取決於直到運行時才知道的情況。這包括系統資源,或用戶可設置的資源。例如,您可以創建引用由 SystemColors、SystemFonts 或 SystemParameters 公開的系統屬性的 setter 值。這些值是真正動態的,因為它們最終來自於用戶和操作系統的運行時環境。您還可以使用可以更改的應用程序級別的主題,在此情況下,頁級別的資源訪問還必須捕獲更改。
-
您正在為自定義控件創建或引用主題樣式。
-
您希望在應用程序生存期調整 ResourceDictionary 的內容。
-
您有一個存在依存關系的復雜資源結構,在這種情況下,可能需要前向引用。靜態資源引用不支持前向引用,但動態資源引用支持,因為資源直到運行時才需要進行求值,因此,前向引用不是一個相關概念。
-
從編譯或工作集角度來說,您引用的資源特別大,並且加載頁時可能無法立即使用該資源。靜態資源引用始終在加載頁時從 XAML 加載;而動態資源引用直到實際使用時才會加載。
-
您要創建的樣式的 setter 值可能來自受主題或其他用戶設置影響的其他值。
-
您正在將資源應用到元素,而在應用程序生存期中可能會在邏輯樹中重新設置該元素的父級。更改此父級還可能會更改資源查找范圍,因此,如果您希望基於新范圍對重新設置了父級的元素的資源進行重新求值,請始終使用動態資源引用。
不同類型的資源
1、程序集資源。這種常見於將圖片設定到程序集中,做為程序集的資源。
程序集資源在定義時,將文件復制到解決方案-項目所在的目錄或其子目錄中,並將文件的屬性中的Build Action設置為Resource。(注意,WPF不支持項目屬性中的資源)
然后在XAML文件中使用如Image的Source屬性,指定到此文件:
<StackPanel Background="#FFC7DAFF"> <TextBlock Margin="5" Text="Assembly Resource" /> <Image Margin="5" Source="Image/Users.png" Width="32" Height="32" /> <Button Margin="5"> <Button.Content> <StackPanel Orientation="Horizontal"> <Image Margin="5" Source="Image/Users.png" Width="32" Height="32" /> <TextBlock Text="My Button" VerticalAlignment="Center" /> </StackPanel> </Button.Content> </Button> </StackPanel>
此項目編譯后,在Assembly中將封裝該圖片文件。此種方法適用於較小的資源。
2、對象資源
除剛剛我們使用的圖片做為程序集資源外,前面例子中所使用的資源均是對象資源。系統對於對象資源使用ResouceDictionary這個字典集合處理,其Key對應即x:Key聲明的鍵,Value對應資源。我們前面使用的都是SolidColorBrush對象,再例如使用字符串及ImageBrush對象做為資源:
<StackPanel Background="#FFE6FDC8"> <StackPanel.Resources> <c:String x:Key="strMessage">My String Value.</c:String> <ImageBrush x:Key="imgBrush" ImageSource="Image/Users.png" ViewportUnits="Absolute" Viewport="0 0 32 32"/> </StackPanel.Resources> <TextBlock Margin="5" Text="Object Resource" /> <TextBox Margin="5" Text="{StaticResource strMessage}" /> <Button Margin="5" Height="32" HorizontalContentAlignment="Right" Content="{StaticResource strMessage}" Background="{StaticResource imgBrush}" Width="125" /> </StackPanel>
程序執行結果為: