Установить пользовательское MarkupExtension из кода - PullRequest
7 голосов
/ 20 сентября 2011

Как установить пользовательский MarkupExtension из кода?

Вы можете легко установить, если из Xaml. То же самое касается Binding и DynamicResource.

<TextBox FontSize="{Binding MyFontSize}"
         Style="{DynamicResource MyStyle}"
         Text="{markup:CustomMarkup}"/>

Установка одинаковых значений через код требует немного другого подхода

  1. Binding: Использовать textBox.SetBinding или BindingOperations.SetBinding

    Binding binding = new Binding("MyFontSize");
    BindingOperations.SetBinding(textBox, TextBox.FontSizeProperty, binding);
    
  2. DynamicResource: Использовать SetResourceReference

    textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle");
    
  3. CustomMarkup: Как установить пользовательский MarkupExtension из кода? Должен ли я позвонить ProvideValue и в таком случае, как мне получить IServiceProvider? *

    CustomMarkupExtension customExtension = new CustomMarkupExtension();
    textBox.Text = customExtension.ProvideValue(??);
    

Я обнаружил удивительно мало по этому вопросу, так что, это можно сделать?


H.B. ответил на вопрос. Просто добавив некоторые детали здесь, почему я хотел сделать это. Я попытался создать обходной путь для следующей проблемы.

Проблема в том, что вы не можете получить из Binding и переопределить ProvideValue, так как он запечатан. Вместо этого вам нужно будет сделать что-то вроде этого: Базовый класс для пользовательских расширений разметки привязки WPF . Но тогда проблема в том, что когда вы возвращаете Binding в Setter, вы получаете исключение, но за пределами Style оно работает нормально.

В нескольких местах я читал, что вы должны возвращать сам MarkupExtension, если TargetObject - это Setter, чтобы позволить ему переоценивать, когда он применяется к фактическому FrameworkElement, и это имеет смысл.

Однако это работает только тогда, когда TargetProperty имеет тип object, в противном случае возвращается исключение. Если вы посмотрите на исходный код BindingBase, то увидите, что он делает именно это, но, похоже, у фреймворка есть какой-то секретный компонент, который заставляет его работать.

Ответы [ 6 ]

6 голосов
/ 20 сентября 2011

Я думаю, что нет эквивалентного кода, услуги доступны только через XAML.Начиная с MSDN :

MarkupExtension имеет только один виртуальный метод, ProvideValue.Параметр input serviceProvider указывает, как службы передаются реализациям, когда расширение разметки вызывается процессором XAML.

5 голосов
/ 09 февраля 2012

Как об этом в качестве альтернативы, он генерируется в коде, но не обязательно так элегантно, как XAML:

        var markup = new CustomMarkup();
        markup.ProvideValue(new Target(textBox, TextBox.TextProperty));

Реализация для Target просто:

public struct Target : IServiceProvider, IProvideValueTarget
{
    private readonly DependencyObject _targetObject;
    private readonly DependencyProperty _targetProperty;

    public Target(DependencyObject targetObject, DependencyProperty targetProperty)
    {
        _targetObject = targetObject;
        _targetProperty = targetProperty;
    }

    public object GetService(Type serviceType)
    {
        if (serviceType == typeof(IProvideValueTarget))
            return this;
        return null;
    }

    object IProvideValueTarget.TargetObject { get { return _targetObject; } }
    object IProvideValueTarget.TargetProperty { get { return _targetProperty; } }
}

Единственноеостается только возможность получить ссылку на CustomMarkup из объектной модели XAML.С учетом вышесказанного вам нужно держаться за ссылку на него.

3 голосов
/ 16 ноября 2012

Если ваше расширение разметки довольно простое и создает привязку и возвращает результат из ProvideValue (), тогда вы можете добавить простой вспомогательный метод:

public class CommandExtension : MarkupExtension
{
    public CommandExtension(string name)
    {
        this.Name = name;
    }

    public string Name { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return GetBinding(this.Name).ProvideValue(serviceProvider);
    }

    static Binding GetBinding(string name)
    {
        return new Binding("Commands[" + name + "]") { Mode = BindingMode.OneWay };
    }

    public static void SetBinding(DependencyObject target, DependencyProperty dp, string commandName)
    {
        BindingOperations.SetBinding(target, dp, GetBinding(commandName));
    }
}

А затем в коде вы можете просто вызвать CommandExtension.SetBinding () вместо BindingOperations.SetBinding ().

Очевидно, что если вы делаете что-то более сложное, чем это, то это решение может быть неуместным.

2 голосов
/ 20 сентября 2011

Эта телепередача Silverlight может пролить свет на эту проблему. Я помню, как они демонстрировали примеры кода, которые могут быть полезны.

1 голос
/ 21 сентября 2011

как H.B. указал, что MarkupExtension предназначен только для использования в XAML.

Что делает Binding уникальным, так это то, что оно на самом деле происходит от MarkupExtension, что позволяет использовать синтаксис расширения {Binding ...} или полную разметку <Binding>...</Binding> и использовать его в коде.

Однако вы всегда можете попробовать создать промежуточный объект (что-то похожее на BindingOperations), который знает, как использовать ваше собственное расширение разметки и применить его к цели DependencyObject.

Я полагаю, что для этого вам потребуется использовать интерфейс XamlSetMarkupExtensionAttribute (для .NET 4) или IReceiveMarkupExtension (для .NET 3). Икс). Я не совсем уверен, как использовать атрибут и / или интерфейс, но он может указать вам правильное направление.

0 голосов
/ 23 июня 2017

Как установить пользовательский MarkupExtension из кода?

Если вы можете изменить его, просто извлеките логику в отдельный SomeMethod, который может быть вызван отдельно и / или из ProvideValue.

Тогда вместо

textBox.Text = customExtension.ProvideValue(??);

Вы просто называете это

customExtension.SomeMethod(textBox, TextBox.TextProperty);

Часто мы создаем пользовательские расширения свойств (используется в xaml следующим образом):

<TextBox Text="{local:SomeExtension ...}" />

Это можно записать так:

public class SomeExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var provider =     serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        var target = provider.TargetObject as DependencyObject;
        var property = provider.TargetProperty as DependencyProperty;
        // defer execution if target is data template
        if (target == null)
           return this;
        return SomeMethod(target, property);
    }

    public object SomeMethod(DependencyObject target, DependencyProperty property)
    {
        ... // do something
    }
}

Поскольку я понял, что иногда необходимо использовать расширения разметки из кода, я всегда пытаюсь написать их таким образом.

...