Как вы можете установить DynamicResource в коде позади, если целью не является FrameworkElement?

Рассмотрим этот класс BindingProxy, который является подклассом Freezable (поэтому он участвует в поисках иерархии ресурсов при добавлении в FrameworkElement"s Resources коллекция)...

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 и таким образом он будет автоматически отслеживать изменения ресурса, определенного этим ключом, как и ожидалось.

Теперь, если вы хотите создать DynamicResource в коде позади XAML, если целевой объект 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"Мясо" того, как это работает, - внутреннее, так что я тоже не могу этого понять.

Так в коде позади, как я могу установить DynamicResource на моем Freezableобъект для отражения того, что я могу сделать в XAML?

2 ответа

Решение

Вы можете использовать DynamicResourceExtension экземпляр в вашем коде:

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

Если вы видите ссылку на код здесь, вы увидите, что ProvideValue возвращает ResourceReferenceExpression когда serviceProvider нулевой. Просто почти то же самое, что SetResourceReference делает

Создать 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 динамически.

Другие вопросы по тегам