WPF中的StaticResource和DynamicResource有什么区别?

526
在WPF中,当使用像画笔、模板和样式这样的资源时,它们可以被指定为StaticResources。
<Rectangle Fill="{StaticResource MyBrush}" />

或作为动态资源。

<ItemsControl ItemTemplate="{DynamicResource MyItemTemplate}"  />

大多数情况下(总是?),只有一个有效,另一个在运行时会抛出异常。但我想知道为什么:

  • 主要区别是什么,例如内存或性能方面的影响?
  • 在 WPF 中是否有规则,例如“画笔始终是静态的”和“模板始终是动态的”等?

我认为,在静态和动态之间的选择并非像看起来那样任意... 但我未能找到规律。


33
需要注意的是,Windows 8 应用程序开发者没有 DynamicResource 的选项,只有 StaticResource。 - Jerry Nixon
2
@Jerry Nixon 感谢上帝,我已经数不清有多少次因为使用 DynamicResource 而不是 StaticResource,或者反之而导致无法正常工作。从程序员的角度来看,这是不必要的复杂性。一个类比是变量定义,我是否必须明确指定它是在堆上还是栈上?如果我搞错了,会抛出灾难性的运行时错误吗? - Contango
有关StaticResource和DynamicResource的更详细解释以及何时使用每个,请参见https://msdn.microsoft.com/en-us/library/ms750613%28v=vs.100%29.aspx。 - Michael Repucci
8个回答

525
一个 StaticResource 会在 XAML 加载期间解析并分配给属性,这发生在应用程序实际运行之前。它只会被分配一次,对资源字典的任何更改都将被忽略。
一个 DynamicResource 在加载期间向属性分配一个表达式对象,但直到运行时才查找资源,当表达式对象被要求提供值时才会查找。这推迟了资源查找,直到运行时需要它。一个很好的例子是在 XAML 中后向引用稍后定义的资源。另一个例子是直到运行时才存在的资源。如果源资源字典发生更改,它将更新目标。

4
在我需要使用DynamicResource之前,需要做出哪些改变呢?以模板为例:我定义了一个模板,但当然触发器和其他内容可以更改模板的内容,但模板本身仍然是相同的。这种情况下,StaticResource是否可行? - Isak Savo
8
如果需要将资源连接到 XAML 中定义之前且在应用程序运行期间不会更改,那么请使用 StaticResource。在这种情况下,使用 StaticResource 可以获得更好的性能表现。请注意,StaticResource 不会更改应用程序的生命周期内的资源。 - Phil Wright
4
这两个是否都适用双向绑定?如果是,那么在这种情况下有什么区别? - ns12345
15
最后一句话非常重要:如果源资源字典发生更改,它将更新目标。 - MEMark
5
考虑一个具有颜色主题的用户界面。使用动态资源,您可以交换一个字典以获取另一个字典,并且任何引用新字典中资源的内容都将自动更新。 - Gusdor
3
所以,StaticResource 就像一个常量,而 DynamicResource 就像一个变量。 - Ryan Lundy

135

我也对它们感到困惑。看下面的示例:

<Window x:Class="WpfApplicationWPF.CommandsWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="CommandsWindow" Height="300" Width="300">

    <StackPanel>
        <Button Name="ButtonNew" 
                Click="ButtonNew_Click" 
                Background="{DynamicResource PinkBrush}">NEW</Button>
        <Image Name="ImageNew" 
               Source="pack://application:,,,/images/winter.jpg"></Image>
    </StackPanel>


    <Window.Background>
        <DynamicResource ResourceKey="PinkBrush"></DynamicResource>
    </Window.Background>

</Window>

我在这里使用了动态资源来设置按钮和窗口的样式,但并没有在任何地方进行声明。在运行时,会检查层次结构的ResourceDictionary。由于我没有定义,所以我猜默认值会被使用。

如果我将下面的代码添加到按钮的click事件中,由于它们使用DynamicResource,背景将相应地更新。

private void ButtonNew_Click(object sender, RoutedEventArgs e)
{
    this.Resources.Add(  "PinkBrush"
                         ,new SolidColorBrush(SystemColors.DesktopColor)
                       );
}
如果他们使用了StaticResource:
  • 资源必须在XAML中声明
  • 而且必须在使用之前声明。
希望我解决了一些疑惑。

这对我来说是最有帮助和直接的例子,谢谢 :) - ajw170

39

StaticResource将在对象构造时解析。
DynamicResource将在控件需要资源时每次被评估和解析。


35
  1. 静态资源使用第一个值。动态资源使用最后一个值。
  2. 动态资源可用于嵌套样式,静态资源则不行。

假设您有此嵌套的Style字典。LightGreen在根级别,而Pink嵌套在Grid内部。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="{x:Type Grid}">
        <Style.Resources>
            <Style TargetType="{x:Type Button}" x:Key="ConflictButton">
                <Setter Property="Background" Value="Pink"/>
            </Style>
        </Style.Resources>
    </Style>
    <Style TargetType="{x:Type Button}" x:Key="ConflictButton">
        <Setter Property="Background" Value="LightGreen"/>
    </Style>
</ResourceDictionary>

鉴于:

<Window x:Class="WpfStyleDemo.ConflictingStyleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ConflictingStyleWindow" Height="100" Width="100">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Styles/ConflictingStyle.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <Button Style="{DynamicResource ConflictButton}" Content="Test"/>
    </Grid>
</Window>

使用StaticResource,按钮将以LightGreen渲染,这是在样式中找到的第一个值。使用DynamicResource,当它呈现网格时,将覆盖LightGreen按钮并将其渲染为Pink。

StaticResource 静态资源

DynamicResource 动态资源

请注意,VS Designer将DynamicResource视为StaticResource处理。它将获取第一个值。在这种情况下,即使最终呈现为粉色,但VS Designer仍会将按钮呈现为LightGreen。

如果删除根级样式(LightGreen),StaticResource将抛出错误。


仍然不清楚为什么在资源字典中首先找到了LightGreen,因为Pink是首先声明的(作为更上面的那个)。我猜当xaml寻找样式时,它首先匹配“非嵌套”的那个。不过一个简短的解释会很有帮助。 - Szabolcs Antal
我有同样的问题...为什么浅绿色是第一个值?这是一种“后进先出”的条件吗? - undefined
如果你将 {DynamicResource ConflictButton} 改为 {StaticResource ConflictButton},那么按钮将会变成绿色。 - undefined

20

主要的区别在哪里,例如内存或性能方面的影响?

静态资源和动态资源之间的区别在于底层对象发生变化时。如果在代码中访问了在资源集合中定义的Brush并将其设置为不同的对象实例,则Rectangle不会检测到此更改。

静态资源仅在引用元素时检索一次,并在资源的整个生命周期内使用。而DynamicResources每次使用时都会检索。

动态资源的缺点是它们往往会降低应用程序的性能。

在WPF中是否有规则,如“brushes始终是静态的”和“模板始终是动态的”等?

最佳实践是除非有特定原因(例如想要在代码后台动态更改资源),否则应使用静态资源。另一个使用动态资源的示例是当您使用SystemBrushes、SystenFonts和System Parameters时。


12

发现所有答案都很有用,只想再补充一个用例。

在复合 WPF 场景中,您的用户控件可以使用在任何其他父窗口/控件中定义的资源(即将托管此用户控件的窗口/控件)通过将该资源称为“DynamicResource”。

如其他人所述,StaticResource 将在编译时查找。用户控件无法引用在托管/父控件中定义的那些资源。但是,在这种情况下可以使用 DynamicResource。


“Staticresource将在编译时查找。” 我不这么认为。“编译时间”意味着“应用程序构建时”。请参见静态资源查找行为。Staticresource是在XAML首次加载时查找的。这发生在运行时。静态和动态之间的区别在于静态查找只发生一次(并遵循更简单的查找规则)。考虑“静态资源查找可以扩展到主题或系统资源中”。 - ToolmakerSteve
1
啊,我在思考一个页面如何使用你的控件,而不是你的控件在其资源字典中可以做什么。我明白了你的观点 - 它被隐含在“从资源字典内部引用静态资源必须引用在资源引用之前已经在词法上定义的资源”的规定中 - 我看到这需要在编译时可解析。 - ToolmakerSteve

5

动态资源的重要优势

如果应用程序启动时间非常长,您必须使用动态资源,因为静态资源总是在窗口或应用程序创建时加载,而动态资源是在首次使用时加载。

但是,除非您的资源非常大且复杂,否则您将看不到任何好处。


对于DynamicResources,它是只在第一次创建(第一次使用)时会产生性能问题,还是每次元素使用时都会产生性能问题? - Morgane
在这种情况下,最常用的字段必须是静态资源,自定义使用的字段可以是动态的,例如对于主窗口资源是静态的,而对话框窗口资源可以是动态的。 - zamoldar
在某些情况下,你不能通过编写代码,在第二页的XAML加载之前向资源字典添加内容来解决这个限制吗? - ToolmakerSteve

5

动态资源只能在属性被设置的对象源自依赖对象或可冻结对象时使用,而静态资源则可以在任何地方使用。

以下情况下使用静态资源:

  1. 不需要在运行时更改反应资源时。
  2. 当需要大量资源并具有良好性能时。
  3. 在同一字典中引用资源时。

动态资源:

  1. 在运行时不知道属性或样式设置主题的值
    • 这包括系统、应用程序、基于主题的设置
    • 这也包括向前引用。
  2. 引用可能在页面、窗口、用户控件加载时不会加载的大型资源。
  3. 在自定义控件中引用主题样式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接