WPF: Changing Resources (colors) from the App.xaml during runtime

30,158

Solution 1

It looks like you're trying to do some sort of skinning?

I'd recommend defining the resources in a Resource Dictionary contained in a separate file. Then in code (App.cs to load a default, then elsewhere to change) you can load the resources as so:

//using System.Windows
ResourceDictionary dict = new ResourceDictionary();
dict.Source = new Uri("MyResourceDictionary.xaml", UriKind.Relative);

Application.Current.Resources.MergedDictionaries.Add(dict);

You could also define the default resource dictionary in App.xaml and unload it in code just fine.

Use the MergedDictionaries object to change the dictionary you're using at runtime. Works like a charm for changing an entire interface quickly.

Solution 2

Changing application wide resources in runtime is like:

Application.Current.Resources("MainBackgroundBrush") = Brsh

About the InvalidOperationException, i guess WallStreet Programmer is right. Maybe you should not try to modify an existing brush, but instead create a new brush in code with all the gradientstops you need, and then assign this new brush in application resources.

Another Approach on changing the color of some GradientStops is to define those colors as DynamicResource references to Application Wide SolidColorBrushes like:

<LinearGradientBrush x:Key="MainBrush" StartPoint="0, 0.5" EndPoint="1, 0.5" >
<GradientBrush.GradientStops>
    <GradientStop Color="{DynamicResource FirstColor}" Offset="0" />
    <GradientStop Color="{DynamicResource SecondColor}" Offset="1" />
</GradientBrush.GradientStops>

and then use

Application.Current.Resources["FirstColor"] = NewFirstColorBrsh
Application.Current.Resources["SecondColor"] = NewSecondColorBrsh

HTH

Solution 3

Use the Clone() method to make a deep copy of the brush (or any other freezable object like Storyboard) and then use it:

LinearGradientBrush myBrush = FindResource("MainBrush") as LinearGradientBrush;
myBrush = myBrush.Clone();
myBrush.GradientStops[0].Color = Colors.Red;

@WallstreetProgrammer is right - all application level resources are frozen by default.

Thats why you need to clone the object first.

Solution 4

You get an exception because you are trying to modify a frozen object. All application level resources are automatically frozen if they are freezable and LinearGradientBrush is. If you add it on a lower level like window level it will work.

Share:
30,158
Andreas Grech
Author by

Andreas Grech

+++++[&gt;+++++[&gt;++++&lt;-]&lt;-]&gt;&gt;+++.--..++++++. Contactable at $(echo qernfterpu#tznvy?pbz | tr ?a-z# .n-za-m@)

Updated on January 13, 2020

Comments

  • Andreas Grech
    Andreas Grech over 4 years

    I am trying to make my application more customizable by allowing users to pick a color from a Color Picker dialog, and then changing the style of the application in real time (with DynamicResource)

    How do I go about in changing specific resources that reside in the app.xaml ?


    I have tried something like this but no luck (just a test):

    var colorDialog = new CustomControls.ColorPickerDialog();
    var dResult = colorDialog.ShowDialog();
    var x = Application.Current.Resources.Values.OfType<LinearGradientBrush>().First();
    x = new LinearGradientBrush();
    x.GradientStops.Add(new GradientStop(colorDialog.SelectedColor,1));
    

    This an excerpt of the app.xaml file:

    <Application.Resources>
        <LinearGradientBrush x:Key="HeaderBackground" StartPoint="0.5,0" EndPoint="0.5,1">
            <GradientStop Color="#82cb02" Offset="1"/>
            <GradientStop Color="#82cb01" Offset="0.2"/>
            <GradientStop Color="#629a01" Offset="0.5"/>
        </LinearGradientBrush>
    </Application.Resources>
    

    What is the best way to allow this form of customizability (basically just changing some colors) to an application?


    [Update]

    I just found this answer from a previous question that was asked, and tried it but I am getting the same InvalidOperationException exception Petoj mentioned in the comments for the given answer. Here is the sample code from the answer:

    Xaml:

    <LinearGradientBrush x:Key="MainBrush" StartPoint="0,0.5" EndPoint="1,0.5" >
        <GradientBrush.GradientStops>
            <GradientStop Color="Blue" Offset="0" />
            <GradientStop Color="Black" Offset="1" />
        </GradientBrush.GradientStops>
    </LinearGradientBrush>
    

    C#:

    LinearGradientBrush myBrush = FindResource("MainBrush") as LinearGradientBrush;
    myBrush.GradientStops[0].Color = Colors.Red;
    
  • Andreas Grech
    Andreas Grech about 15 years
    But I cannot add it in a window because this style applies to every window in my project
  • Fᴀʀʜᴀɴ Aɴᴀᴍ
    Fᴀʀʜᴀɴ Aɴᴀᴍ over 8 years
    This is exactly what I wanted.
  • mbomb007
    mbomb007 over 7 years
    Note that this doesn't work in Silverlight. DynamicResource is not supported.
  • fadden
    fadden over 4 years
    Bear in mind that MergedDictionaries is a container. The resource in the most-recently-added ResourceDictionary wins. If the intent is to flip back and forth between dictionaries with any regularity, removing the previous dictionary from the list may be beneficial. (The answer mentions "unload it in code" w.r.t a XAML-specified default, but doesn't show how to identify and remove it.)
  • Alexandru Dicu
    Alexandru Dicu about 3 years
    This helped me fix the problem. I moved the LinearGradientBrush which had <GradientStop Color="{DynamicResource WindowBorderColor}" Offset="0.5"/> from App.xaml to my Window.Resources and the DynamicResource started to apply as it should. Thank you.