如何使用 DynamicResource 更新 SolidColorBrush 颜色?

Posted

技术标签:

【中文标题】如何使用 DynamicResource 更新 SolidColorBrush 颜色?【英文标题】:How to update SolidColorBrush color with a DynamicResource? 【发布时间】:2018-01-26 04:23:46 【问题描述】:

我正在尝试动态更改画笔的颜色。我写了一个非常简单的例子,但我不明白为什么它不起作用。

我在我的应用程序的 ResourceDictionary 中定义了一种前景色和一个使用该颜色作为 DynamicResource 的前景色:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Color x:Key="Foreground">#FF0000</Color>
    <SolidColorBrush x:Key="ForegroundBrush" Color="DynamicResource Foreground" />

</ResourceDictionary>

然后,在我的 Window.xaml 中,我使用画笔设置文本块前景:

<Window x:Class="DictionaryColorTests.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <TextBlock Name="mTextBlock"  Foreground="DynamicResource ForegroundBrush">This is just a text block</TextBlock>
            <Button Name="Button1" Click="Button1_Click">Change color</Button>
        </StackPanel>
    </Grid>
</Window>

最后在我的代码后面,我正在改变颜色:

private void Button1_Click(object sender, RoutedEventArgs e)

    Application.Current.Resources["Foreground"] = Colors.Green;

很遗憾,画笔颜色没有更新。谁能解释一下为什么?

注意:我知道 如果我直接更改画笔的颜色,它可以正常工作,但我试图理解为什么使用颜色作为动态​​资源的 SolidColorBrush 不会更新其颜色变化时的颜色。


更新:使用样式按预期工作

如果我为文本块使用样式,只需更新颜色,它就可以正常工作并且画笔采用正确的颜色。直接使用画笔和使用样式有什么区别?

<Style TargetType="TextBlock" x:Key="TextBlockStyle">
    <Setter Property="Foreground" Value="StaticResource ForegroundBrush" />
</Style>

【问题讨论】:

【参考方案1】:

如果没有一个好的Minimal, Complete, and Verifiable code example 可以可靠地重现您的问题,就不可能确定问题出在哪里。但基本问题似乎是您试图以不受支持的方式使用DynamicResource

来自the documentation:

动态资源引用有一些明显的限制。以下至少一项必须为真:

•要设置的属性必须是 FrameworkElement 或 FrameworkContentElement 上的属性。该属性必须由 DependencyProperty 支持。

•引用是针对 StyleSetter 中的值。

•要设置的属性必须是 Freezable 上的属性,该属性作为 FrameworkElement 或 FrameworkContentElement 属性的值或 Setter 值提供。

因为要设置的属性必须是 DependencyProperty 或 Freezable 属性,所以大多数属性更改可以传播到 UI,因为属性更改(更改的动态资源值)已被属性系统确认。大多数控件都包含在 DependencyProperty 更改并且该属性可能会影响布局时强制控制另一个布局的逻辑。但是,并非所有将 DynamicResource 标记扩展作为其值的属性都可以保证以它们在 UI 中实时更新的方式提供值 [强调我的] .该功能仍可能因属性、拥有该属性的类型甚至应用程序的逻辑结构而异。

您正在为SolidColorBrush 对象设置属性,该对象不是FrameworkElementFrameworkContentElement。场景确实符合文档中的第三个要点(SolidColorBrushFreezable,而 Freezable 被提供为 FrameworkElement 的值)。但请注意我加粗的文字。即使满足这些要求,也不能保证它会起作用。

有趣的是,当在ResourceDictionary 直接由FrameworkElement 拥有的上下文中完成时,您似乎试图实现的基本方法确实 工作(例如Window) .

为什么会这样,我不知道。我的猜测是,只要您处于 FrameworkElement somewhere 的上下文中,就有足够的内部状态管理来处理更新 DynamicResource 代理,因此它可以工作。

无论如何,这意味着一种可能的解决方法是将您的资源放入Window.Resources,而不是您现在放置它们的任何地方。我不知道您是否可以指望此始终正常工作;毕竟,文档包含了所有内容,让微软说“也许它会工作,也许它不会”。但在我看来,这种情况可能不会很快被打破。

或者,您可以修改画笔资源本身。例如:

private void Button1_Click(object sender, RoutedEventArgs e)

    Application.Current.Resources["ForegroundBrush"] = new SolidColorBrush(Colors.Green);

确实符合文档中的更多要求,因此似乎更有可能适用于任何未来版本的 .NET(我已经验证它今天确实可以使用?)。

【讨论】:

【参考方案2】:

我认为这是唯一的原因,颜色是一种值类型的数据,当您从 ResourceDictionary 获取源时,它返回颜色的值(副本),重新分配的操作不会影响原始实例(资源)。 所以认为可以有很多解决方案来解决这个问题:

this.Resources["ForegroundBrush"] = new SolidColorBrush(Colors.Green);
Or
var brush = this.Resources["ForegroundBrush"] as SolidColorBrush;
if(brush != null)
   brush.Color = Colors.Green;

【讨论】:

如果我使用文本块的样式,只需更新颜色,就可以正常工作。

以上是关于如何使用 DynamicResource 更新 SolidColorBrush 颜色?的主要内容,如果未能解决你的问题,请参考以下文章

StaticResource和DynamicResource使用

[WPF]静态资源(StaticResource)和动态资源(DynamicResource)

[WPF]静态资源(StaticResource)和动态资源(DynamicResource)

DynamicResource 和 StaticResource

StaticResource和DynamicResource

DynamicResource 不工作,但 StaticResource 可以吗?