6

is there a way to get notified on value changes of a specific WPF resource?

We need to dynamically adapt the content font size in a WPF application... For WPF controls, we set Control.FontSize to a dynamic resource and the font resizes automatically. Unfortunately, we also have an embedded winforms control, which font size cannot be set that way. The idea was to subscribe to an event triggered on each resource value-change and to implement a custom refresh of the winforms control. Any suggestion?

Thank you in advance!

jeromerg
  • 2,997
  • 2
  • 26
  • 36
  • Did you take a look at http://stackoverflow.com/questions/92100/is-it-possible-to-set-code-behind-a-resource-dictionary-in-wpf-for-event-handlin ? – Josh Apr 03 '14 at 16:03
  • Code-behind could help declaring a public event. But when should this event be triggered? Is there an event anywhere in WPF framework, that tells us, that a resource has changed? – jeromerg Apr 04 '14 at 08:43
  • 1
    I found the event `FrameworkElement.ResourcesChanged` in the **.Net** source, but it is completely ***internal***. No way to hook some handler to this event... Any suggestion? I think the only remaining way, consists of setting the dynamic resource to a dedicated dependency property (using `Prop={DynamicResource MyResource}` and use the dependency property to trigger a change-event) – jeromerg Apr 04 '14 at 09:21

2 Answers2

7

So,

after considering all possible ways, I introduced a new behavior that triggers an event on each change of a specific WPF resource.

The source can be downloaded or cloned from https://github.com/jeromerg/ResourceChangeEvent.

public class ResourceChangeNotifierBehavior 
  : System.Windows.Interactivity.Behavior<FrameworkElement>
{
    public static readonly DependencyProperty ResourceProperty 
            = DependencyProperty.Register("Resource", 
                   typeof(object),
                   typeof(ResourceChangeNotifierBehavior),
                   new PropertyMetadata(default(object), ResourceChangedCallback));

    public event EventHandler ResourceChanged;

    public object Resource
    {
        get { return GetValue(ResourceProperty); }
        set { SetValue(ResourceProperty, value); }
    }

    private static void ResourceChangedCallback(DependencyObject dependencyObject,
                                                DependencyPropertyChangedEventArgs args)
    {
        var resourceChangeNotifier = dependencyObject as ResourceChangeNotifierBehavior;
        if (resourceChangeNotifier == null)
            return;

        resourceChangeNotifier.OnResourceChanged();
    }

    private void OnResourceChanged()
    {
        EventHandler handler = ResourceChanged;
        if (handler != null) handler(this, EventArgs.Empty);
    }
}

So that an event-handler OnResourceChanged can be hooked in the XAML file as following:

<i:Interaction.Behaviors>
    <Behaviours:ResourceChangeNotifierBehavior 
                Resource="{DynamicResource MyDynamicResourceKey}"
                ResourceChanged="OnResourceChanged"/>
</i:Interaction.Behaviors>

Hope, it helps...

jeromerg
  • 2,997
  • 2
  • 26
  • 36
0

I had the same problem, but I indeed wanted to get notified if any resource changed. In my case, a DynamicResource was used on some DependencyObject which was not in the visual tree. Therefore, the property did not automatically update if the resource changed.

Using the internal event found by jeromerg, I have written a small extension method to register to the ResourceChanged event:

public static void NotifyOnResourcesChanged(this FrameworkElement element, EventHandler onResourcesChanged)
{
    ResourcesChangedEvent.AddMethod.Invoke(element, new object[] { onResourcesChanged });
}
private static readonly EventInfo ResourcesChangedEvent = typeof(FrameworkElement).GetEvent("ResourcesChanged", BindingFlags.Instance | BindingFlags.NonPublic);

In my event handler, I notified the target properties via InvalidateProperty to force an update.

LionAM
  • 1,271
  • 10
  • 28