Reputation: 31897
I'm trying to change the color of the brush dynamically. I wrote a very simple example but I don't understand why it does not work.
I have defined a foreground color, and a foreground brush that uses that color as a DynamicResource, in a ResourceDictionary from my app:
<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>
Then, in my Window.xaml I use the brush to set the textblock foreground:
<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>
Finally in my code behind, I'm changing the color:
private void Button1_Click(object sender, RoutedEventArgs e)
{
Application.Current.Resources["Foreground"] = Colors.Green;
}
Unfortunately, the brush color is not updated. Someone could please explain why?
NOTE: I know that if I change directly the color of the brush it works fine, but I'm trying to understand why a SolidColorBrush that uses a color as a dynamic resource does not update its color when the color changes.
If I use a style for the textblock, just updating the color, it works fine and the brush take the right color. What is the difference about using the brush directly or using the style?
<Style TargetType="TextBlock" x:Key="TextBlockStyle">
<Setter Property="Foreground" Value="{StaticResource ForegroundBrush}" />
</Style>
Upvotes: 6
Views: 4900
Reputation: 70701
Without a good Minimal, Complete, and Verifiable code example that reliably reproduces your problem, it's not possible to know for sure what the problem is. But the basic issue seems to be that you are trying to use DynamicResource
in an unsupported way.
From the documentation:
Dynamic resource references have some notable restrictions. At least one of the following must be true:
•The property being set must be a property on a FrameworkElement or FrameworkContentElement. That property must be backed by a DependencyProperty.
•The reference is for a value within a StyleSetter.
•The property being set must be a property on a Freezable that is provided as a value of either a FrameworkElement or FrameworkContentElement property, or a Setter value.
Because the property being set must be a DependencyProperty or Freezable property, most property changes can propagate to UI because a property change (the changed dynamic resource value) is acknowledged by the property system. Most controls include logic that will force another layout of a control if a DependencyProperty changes and that property might affect layout. However, not all properties that have a DynamicResource Markup Extension as their value are guaranteed to provide the value in such a way that they update in realtime in the UI [emphasis mine]. That functionality still might vary depending on the property, as well as depending on the type that owns the property, or even the logical structure of your application.
You are setting a property on a SolidColorBrush
object, which is not a FrameworkElement
or FrameworkContentElement
. The scenario does though meet the third bulleted point in the documentation (SolidColorBrush
is a Freezable
, and that Freezable
is being provided as a value of a FrameworkElement
). But note the text I've bolded. Even when meeting these requirements, you are not guaranteed it will work.
Interestingly, the basic approach you appear to be trying to implement does work, when done in the context of a ResourceDictionary
that is directly owned by a FrameworkElement
(e.g. Window
).
Why this would be, I don't know. My guess is that as long as you're in the context of a FrameworkElement
somewhere, there's enough internal state management to handle updating DynamicResource
proxies, and so it lets it work.
Anyway, that means one possible work-around would be to put your resources into the Window.Resources
instead of wherever you're putting them now. I don't know that you can count on this always working; after all, the documentation has that catch-all out that lets Microsoft say "maybe it'll work, maybe it won't". But it seems likely to me that's not a scenario that's going to get broken any time soon.
Alternatively, you can modify the brush resource itself. E.g.:
private void Button1_Click(object sender, RoutedEventArgs e)
{
Application.Current.Resources["ForegroundBrush"] = new SolidColorBrush(Colors.Green);
}
That does comply with more of the requirements in the documentation and so seems even more likely to work for any future version of .NET (I've verified that it does work today 😊 ).
Upvotes: 7
Reputation: 28
I think this is the only reason,Color is a kind of a value-typed data,when you get a source from ResourceDictionary,it returns value(copy) of the Color,the reassigned action won't affect on the original instance(Resource). So think there can be lots of solutions to solve this:
this.Resources["ForegroundBrush"] = new SolidColorBrush(Colors.Green);
Or
var brush = this.Resources["ForegroundBrush"] as SolidColorBrush;
if(brush != null){
brush.Color = Colors.Green;
}
Upvotes: 0