Modify Resources at Runtime in WPF

Published 9 Mar 2010 9:32 AM

In general, it's no problem to modify WPF resources at runtime. You can use the TryFindResource method to search the same path as the XAML parser, given a starting point, and a name for the resource. like this:

Button btn = (Button)sender;
var brush =
  (SolidColorBrush)(btn.TryFindResource("TextBrush"));

if (brush != null)
{
  brush.Color = Colors.Red;
}

The problem comes in trying to modify application-level resources. In order to optimize for performance, application-level resources are frozen (that is, their IsFrozen property is set to True), so WPF "knows" that it doesn't need to monitor them for changes. What if you want to modify one one of these resources? That requires cloning the resource, making changes, and then applying it, like this (this example attempts to swap the colors in a RadialGradientBrush). One note: Because this code actually changes the content of the resource, in order for the markup to "notice" the change, the resource must be declared as a DynamicResource in the markup (as opposed to the more common StaticResource):

Button btn = (Button)sender;
RadialGradientBrush brush =
  (RadialGradientBrush)(btn.TryFindResource("BackgroundBrush"));

if (brush != null)
{
  // Swap the colors in the RadialGradientBrush:
  Color color = brush.GradientStops[1].Color;
  if (brush.IsFrozen)
  {
    // If it's frozen, you must create a copy,
    // modify it, and then apply it. This requires the
    // consumer of the resource to be dynamic.
    RadialGradientBrush newBrush = brush.Clone();
    newBrush.GradientStops[1].Color =
      brush.GradientStops[0].Color;
    newBrush.GradientStops[0].Color = color;
    Application.Current.
      Resources["BackgroundBrush"] = newBrush;
  }
  else
  {
    brush.GradientStops[1].Color =
      brush.GradientStops[0].Color;
    brush.GradientStops[0].Color = color;
  }
}

by KenG
Filed under: