如果目标不是FrameworkElement,如何在后台代码中设置DynamicResource?

时间:2018-09-03 08:15:35

标签: c# wpf dynamicresource freezable

请考虑此BindingProxy类,它是Freezable的子类(因此,当添加到FrameworkElement的{​​{1}}集合中时,它将参与资源层次结构查找)...

Resources

您可以像这样将其添加到XAML中...

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
}

如您所见,<Window.Resources> <is:BindingProxy x:Key="TestValueProxy" Value="{DynamicResource TestValue}" /> </Window.Resources> 设置为Value,因此它将按预期自动跟踪对由该键定义的资源的更改。

现在,如果您想在代码隐藏中设置DynamicResource而不是XAML,如果目标对象是DynamicResource,则只需在其上调用FrameworkElement,就像这样...

SetResourceReference

但是,myTextBlock.SetResourceReference(TextBlock.TextProperty, "MyTextResource") 仅在SetResourceReference个对象上可用,而在FrameworkElement个对象上不可用,因此您不能在Freezable上使用它。

浏览BindingProxy的源代码,您会发现...

FrameworkElement.SetResourceReference

不幸的是,public void SetResourceReference(DependencyProperty dp, object name){ base.SetValue(dp, new ResourceReferenceExpression(name)); HasResourceReference = true; } 是它的工作原理,它是内部的,所以我也无法做到这一点。

因此,在代码隐藏中,如何在基于ResourceReferenceExpression的对象上设置DynamicResource来反映在XAML中可以执行的操作?

2 个答案:

答案 0 :(得分:0)

使用反射创建一个ResourceReferenceExpression

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);

很明显,如果内部类型发生更改,此代码可能会中断,但是如果您确实需要能够动态地将DynamicResource应用于Freezable的值,则您无能为力。

答案 1 :(得分:0)

您可以在代码中使用DynamicResourceExtension实例:

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

如果您看到代码参考here,则您会看到ProvideValue为空时ResourceReferenceExpression返回serviceProvider。与SetResourceReference几乎一样