【问题标题】:How can you set a DynamicResource in code-behind if the target is not a FrameworkElement?如果目标不是 FrameworkElement,如何在代码隐藏中设置 DynamicResource?
【发布时间】:2018-09-03 08:15:35
【问题描述】:

考虑这个 BindingProxy 类,它是 Freezable 的子类(因此当添加到 FrameworkElementResources 集合时,它会参与资源层次结构查找)...

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
}

你像这样将它添加到你的 XAML...

<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 对象,而不是 Freezables,因此您不能在 BindingProxy 上使用它。

深入研究FrameworkElement.SetResourceReference 的源代码,你会发现这个...

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

不幸的是,ResourceReferenceExpression——它的工作原理的“核心”——是内部的,所以我也无法理解。

那么在代码隐藏中,如何在基于Freezable 的对象上设置DynamicResource 以反映我在XAML 中可以执行的操作?

【问题讨论】:

  • 为什么不在proxy.Value = dynamicResourceExtension.ProvideValue(null); 这样的代码中使用DynamicResourceExtension
  • 我实际上已经尝试过了,但有趣的是,不是通过传递“null”。我正在缓存服务提供者,这是问题的一部分。让我看看'null'是否修复它。 (顺便说一句,为了完整起见,您能否将此作为代码示例供其他人遵循?只是在 SO 上看起来更好。)

标签: c# wpf dynamicresource freezable


【解决方案1】:

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

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

如果您看到代码引用here,您将看到ProvideValueserviceProvider 为空时返回ResourceReferenceExpression。与SetResourceReference 所做的几乎相同

【讨论】:

  • 只是给你一个提示。这就是它的用途,它完美无瑕!谢谢!使用动态资源绑定:stackoverflow.com/q/33816511/168179
  • 传递null 是缺少的成分。 :)
【解决方案2】:

使用反射创建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 的值,则无能为力。

【讨论】:

  • 不必为必须使用反射而疯狂,但考虑到标记扩展已经使用它,它应该是安全的。在旁注中,我正在尝试加载一个 XAML sn-p,它既创建实例又分配 DynamicResource,因为这 应该 仍然遵循正确的规则而不依赖于反射。如果这不起作用,我会将其标记为答案并继续。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-07
  • 2012-11-28
  • 2021-06-07
  • 2018-08-23
  • 1970-01-01
相关资源
最近更新 更多