1

Consider this BindingProxy class which is a subclass of Freezable (so it participates in resource-hierarchy lookups when added to a FrameworkElement's Resources collection)...

public class BindingProxy : Freezable {

    public BindingProxy(){}
    public BindingProxy(object value)
        => Value = value;

    protected override Freezable CreateInstanceCore()
        => new BindingProxy();

    #region Value Property

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
            nameof(Value),
            typeof(object),
            typeof(BindingProxy),
            new FrameworkPropertyMetadata(default));

        public object Value {
            get => GetValue(ValueProperty);
            set => SetValue(ValueProperty, value);
        }

    #endregion Value Property
}

You add it to your XAML like so...

<Window.Resources>
    <is:BindingProxy x:Key="TestValueProxy" Value="{DynamicResource TestValue}" />
</Window.Resources>

As you can see, Value is set to a DynamicResource and it will thus automatically track changes to a resource defined by that key, as expected.

Now if you wanted to set up a DynamicResource in code-behind instead of XAML, if the target object is a FrameworkElement, you would simply call SetResourceReference on it, like so...

myTextBlock.SetResourceReference(TextBlock.TextProperty, "MyTextResource")

However, SetResourceReference is only available on FrameworkElement objects, not Freezables so you can't use that on BindingProxy.

Digging into the source code for FrameworkElement.SetResourceReference, you find this...

public void SetResourceReference(DependencyProperty dp, object name){
    base.SetValue(dp, new ResourceReferenceExpression(name));
    HasResourceReference = true;
}

Unfortunately, ResourceReferenceExpression--the 'meat' of how this works--is internal so I can't get to that either.

So in code-behind, how can I set a DynamicResource on my Freezable-based object to mirror what I can do in XAML?

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • 1
    Why not to use `DynamicResourceExtension` in code like `proxy.Value = dynamicResourceExtension.ProvideValue(null);` ? – taquion Sep 03 '18 at 16:11
  • I had actually tried that, but interestingly, not by passing 'null'. I was caching the service provider and that was part of the problem. Lemme see if the 'null' fixes it. (Btw, for completeness, can you make this a code example for others to follow? Just looks better here on SO.) – Mark A. Donohoe Sep 03 '18 at 16:14

2 Answers2

2

You can use a DynamicResourceExtension instance in your code:

var proxy = new BindingProxy();
var dynamicResourceExtension = new DynamicResourceExtension("TestValue");
proxy.Value = dynamicResourceExtension.ProvideValue(null);

If you see the code reference here you will see that ProvideValue returns a ResourceReferenceExpression when serviceProvider is null. Just almost the same thing that SetResourceReference does

taquion
  • 2,667
  • 2
  • 18
  • 29
0

Create a ResourceReferenceExpression using reflection:

Type type = typeof(System.Windows.Window).Assembly.GetType("System.Windows.ResourceReferenceExpression");
ConstructorInfo ctor = type.GetConstructors()[0];
object resourceReferenceExpression = ctor.Invoke(new object[] { "TestValue" });
TestValueProxy.SetValue(BindingProxy.ValueProperty, resourceReferenceExpression);

Obviously this code may break if the internal type changes, but there is not much else you can do if you really need to be able to apply a DynamicResource to a value of a Freezable dynamically.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Not crazy about having to use Reflection, but considering that's what the Markup Extension already uses, it should be safe. On a side-note, I'm experimenting with loading a XAML snippet that both creates the instance of, and assigns the DynamicResource, since this *should* still follow the proper rules without relying on reflection. If that however doesn't work, I'll mark this as the answer and move on. – Mark A. Donohoe Sep 03 '18 at 15:56