如何使用DynamicResource更新SolidColorBrush颜色?

时间:2017-08-17 16:25:01

标签: c# wpf colors

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

我在我的应用程序的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>

2 个答案:

答案 0 :(得分:2)

如果没有可靠地再现您的问题的好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的值提供的1}})。但请注意我加粗的文字。即使满足这些要求,也不能保证它会起作用。

有趣的是,当您在ResourceDictionary直接拥有的FrameworkElement的上下文中完成时,您似乎尝试实施的基本方法可以正常工作(例如Window)。

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

无论如何,这意味着一种可能的解决方法是将您的资源放入Window.Resources而不是现在放置它们的任何地方。我不知道你可以指望总是工作;毕竟,文档中包含的内容可以让微软说出来,也许它会起作用,也许它会赢得“不可能”。但在我看来,这似乎不是一个很快就会被打破的情景。

或者,您可以修改画笔资源本身。 E.g:

private void Button1_Click(object sender, RoutedEventArgs e)
{
    Application.Current.Resources["ForegroundBrush"] = new SolidColorBrush(Colors.Green);
}

符合文档中的更多要求,因此更有可能适用于任何未来版本的.NET(我已经证实它今天可以正常工作)。

答案 1 :(得分:0)

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

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