funkymushroom
funkymushroom

Reputation: 2139

Why is my icon color not changing when I change the base resource

I have an icon defined as:

<DrawingImage x:Key="CloseIcon">
    <DrawingImage.Drawing>
        <DrawingGroup ClipGeometry="M0,0 V27 H28 V0 H0 Z">
            <DrawingGroup.Transform>
                <TranslateTransform X="3.9828000068664551" Y="0" />
            </DrawingGroup.Transform>
            <DrawingGroup Opacity="1" Transform="1,0,0,1,0.885057,0.5">
              <DrawingGroup Opacity="1" Transform="...">
                <GeometryDrawing Brush="{DynamicResource PrimaryBrush}" Geometry="..." />
              </DrawingGroup>
              <DrawingGroup Transform="...">
                 <GeometryDrawing Brush="{DynamicResource PrimaryBrush}" Geometry="..." /> 
              </DrawingGroup>
            </DrawingGroup>
        </DrawingImage.Drawing>
    </DrawingImage>

I have simplified the above xaml, there are a ton more nested DrawingGroups than in the actual code.

At run time, I am trying to do the following: The PrimaryBrush resource is set on load.

Then later on in the application I am trying to do the following:

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

But the icon remains the same color. My understanding was that anything that had a DynamicResource would change when you changed that dynamic resource.

How do change the icon color at runtime?

(fyi, I have spent at least a week investigating and trying to figure this out, and to no avail),

Update (1/13/2023 14:10 EST

Upvotes: 1

Views: 187

Answers (2)

Andy
Andy

Reputation: 12276

Anything merged into application resources from a resource dictionary which inherits from Freezable will be frozen.

This is a good thing.

Frozen resources are more efficient because the framework does not have to allow for them changing. Internally I think pointers are used rather than copying data around and they save memory.

You can read more, much more if you google round.

Start here

https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/freezable-objects-overview?view=netframeworkdesktop-4.8

"The Freezable class makes it easier to use certain graphics system objects and can help improve application performance. Examples of types that inherit from Freezable include the Brush, Transform, and Geometry classes. Because they contain unmanaged resources, the system must monitor these objects for modifications, and then update their corresponding unmanaged resources when there is a change to the original object. Even if you don't actually modify a graphics system object, the system must still spend some of its resources monitoring the object, in case you do change it."

In any event, a frozen freezable is more efficient.

In this specific application it might make no significant difference.

Or it might be significant, we can only guess.

An alternative approach which retains freezing is to replace the resources entirely. This might mean an initial overhead on load if you have a lot of these but those frosty freezables will be garbage collected fairly quickly.

Some experimental code:

I have two colour resource dictionaries. RedResources and BlueResources. They only have the one brush in. When you're using multiple colours in iconography rather than just a shape and fill then you presumably would want a theme with multiple brushes.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <SolidColorBrush x:Key="PrimaryBrush" Color="Red"/>
</ResourceDictionary>

Hopefully obvious what the Blue one has in it :^)

Dictionary1 has some geometries and a drawingbrush. I usually use a brush rather than drawingimage for iconography and pictures.

You can set the fill on a rectangle if you want to box a brush up.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Geometry x:Key="XInCircleIcon">
        M10.661012,7.5689991L7.5990001,10.650999 12.939089,15.997999 7.5990001,21.336999 10.661012,24.405 16.007082,19.065 21.369997,24.405 24.430058,21.336999 24.429081,21.336 19.088991,15.998999 24.429081,10.662001 21.345095,7.5819996 16.007082,12.919001z M15.997072,0C24.828983,0 31.994999,7.1770013 31.994999,15.999998 31.994999,24.826997 24.828007,31.999999 15.997072,31.999999 7.1569835,31.999999 1.5270052E-07,24.826997 0,15.999998 1.5270052E-07,7.1799997 7.1569835,0 15.997072,0z
    </Geometry>
    <Geometry x:Key="BeeIcon">
        M15.551045,25.144995L16.748029,25.144995 16.249038,28.637C16.249038,28.637,15.651051,25.543997,15.551045,25.144995z M21.237988,21.152998L21.437999,21.152998C20.340014,23.049002 18.345033,24.247 16.150041,24.247 13.95404,24.247 11.959059,23.049002 10.861073,21.252997L10.96108,21.252997C15.351034,22.550002,18.943021,21.952002,21.237988,21.152998z M21.337995,14.967002L21.437999,15.067001C21.936988,15.965002,22.235997,16.962996,22.235997,18.160002L22.235997,18.658995 22.036994,18.759001C17.746037,20.653998,12.458048,19.556998,10.16308,18.858L10.063075,18.858 10.063075,18.759001 10.063075,18.160002C10.063075,17.162001,10.263086,16.164,10.761068,15.365997L10.861073,15.365997C16.150041,17.560996,20.141011,15.665,21.337995,14.967002z M16.150041,12.072998C17.746037,12.072998,19.243007,12.671997,20.340014,13.769996L20.44002,13.869003 20.340014,13.869003C17.247018,15.365997 13.356052,14.568 11.560075,14.069 12.657052,12.772003 14.353055,12.072998 16.150041,12.072998z M27.523957,8.3820033C28.821952,8.3820033 29.918929,8.6809992 30.517924,9.3789973 32.91293,12.072998 32.712918,16.663001 27.623963,15.566001 22.534976,14.468002 18.345033,11.674003 17.646031,10.875999L19.043027,10.376998C19.143001,10.376998,23.93197,8.3820033,27.523957,8.3820033z M4.5751051,8.3820033C8.0670862,8.3820033,12.957068,10.376998,12.957068,10.376998L14.353055,10.875999C13.655062,11.674003 9.4640798,14.468002 4.3751249,15.566001 -0.71285403,16.663001 -0.91286493,12.072998 1.4821384,9.3789973 2.1801321,8.5810008 3.2781166,8.3820033 4.5751051,8.3820033z M16.150041,3.2929987C18.145022,3.292999 19.64202,4.8889994 19.64202,6.7850031 19.64202,8.7809977 18.045016,10.277 16.150041,10.277 14.154051,10.277 12.657052,8.6809992 12.657052,6.7850031 12.557047,4.8889994 14.154051,3.292999 16.150041,3.2929987z M12.757057,1.0979993L13.056066,1.5960004 12.957068,1.5960004C12.857062,1.5960005 12.657052,1.4970015 12.557047,1.3970029 12.557047,1.2969969 12.657052,1.0979994 12.757057,1.0979993z M19.542016,9.6861186E-11C19.741996,-6.1625087E-08 19.840995,-6.1625087E-08 20.041005,0.099998488 20.839003,0.399002 21.237988,1.2969969 20.839003,2.0950009 20.44002,3.093002 19.342006,3.5920026 18.345033,3.1930003L18.943021,1.996002 19.043027,1.996002C19.44201,2.0950009 19.840995,1.8960036 19.940999,1.4970014 20.041005,1.1969984 19.940999,0.79800405 19.64202,0.69799812 19.342006,0.59899892 19.143001,0.69799794 18.943021,0.99800076 18.856397,1.1721237 18.921365,1.3470176 19.072932,1.4550116L19.129043,1.4886372 19.44201,1.0979993C19.542016,1.1969984 19.64202,1.2969969 19.542016,1.3970029 19.44201,1.4970015 19.342006,1.5960005 19.143001,1.5960004L19.103326,1.5960004 18.943021,1.996002C18.444032,1.7959975 18.245027,1.1969984 18.444032,0.69799812 18.644011,0.29900353 19.043027,-6.1625087E-08 19.542016,9.6861186E-11z M12.757057,9.6861186E-11C13.256047,-6.1625087E-08 13.655062,0.29900353 13.855041,0.79800428 14.054046,1.2969969 13.855041,1.8960036 13.356052,2.0950009L13.056066,1.5960004C13.256047,1.4970015 13.356052,1.2969969 13.256047,1.0979993 13.156041,0.79800405 12.857062,0.69799794 12.557047,0.79800428 12.258068,0.89800252 12.059064,1.2969969 12.258068,1.5960004 12.458048,1.996002 12.857062,2.1949994 13.156041,2.0950009L13.256047,2.0950009 13.855041,3.1930003C12.857062,3.5920026 11.759047,3.093002 11.360063,2.0950009 11.061054,1.2969969 11.460069,0.399002 12.158062,0.099998488 12.358073,-6.1625087E-08 12.557047,-6.1625087E-08 12.757057,9.6861186E-11z
    </Geometry>

    <DrawingBrush x:Key="CloseIconBrush" Stretch="Uniform">
        <DrawingBrush.Drawing>
            <DrawingGroup>
                <DrawingGroup>
                    <GeometryDrawing Brush="{StaticResource PrimaryBrush}" Geometry="{StaticResource BeeIcon}" />
                </DrawingGroup>
                <DrawingGroup>
                    <GeometryDrawing Brush="{StaticResource PrimaryBrush}" Geometry="{StaticResource XInCircleIcon}" />
                </DrawingGroup>
            </DrawingGroup>
        </DrawingBrush.Drawing>
    </DrawingBrush>
</ResourceDictionary>

Merged in app.xaml

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/RedResources.xaml"/>
            <ResourceDictionary Source="/Dictionary1.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Mainwindow xaml

<Grid Background="{DynamicResource CloseIconBrush}">
        <Button Content="Blue" 
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Click="Button_Click"/>
</Grid>
</Window>

The button removes and re merges those resource dictionaries. This is just proof of concept code shows it works at all.

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var rd = new ResourceDictionary { Source = new Uri("BlueResources.xaml", UriKind.Relative) };
        var rd1 = new ResourceDictionary { Source = new Uri("Dictionary1.xaml", UriKind.Relative) };
        Application.Current.Resources.MergedDictionaries.Clear();
        Application.Current.Resources.MergedDictionaries.Add(rd);
        Application.Current.Resources.MergedDictionaries.Add(rd1);
    }

When I try this, it starts red and changes to blue when I click the button.

Perhaps worth mentioning other resources.

Let's imagine instead of a resource dictionary you used Window.Resources and put your drawingbrush in there directly.

Maybe you only have one or three icons which are only used in main window.

Freezables in that sort of resource directly are not frozen so you could put a drawingbrush or drawingimage in there and rely on dynamicresource notifying change.

If you don't have many of these things then you probably won't notice the overhead.

Upvotes: 0

BionicCode
BionicCode

Reputation: 28948

DrawingImage extends Freezable. To allow the modification of nested Freezable resources you must define them with the x:Shared attribute set to false:

<DrawingImage x:Key="CloseIcon"
              x:Shared="False">
</DrawingImage>

Upvotes: 2

Related Questions