【问题标题】:How to update SolidColorBrush color with a DynamicResource?如何使用 DynamicResource 更新 SolidColorBrush 颜色?
【发布时间】: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>

【问题讨论】:

    标签: c# wpf colors


    【解决方案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;
      }
      

      【讨论】:

      • 如果我使用文本块的样式,只需更新颜色,就可以正常工作。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-22
      • 2022-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多