MarkupExtension с параметрами привязки - программирование

MarkupExtension с параметрами привязки

Я работаю над пользовательским расширением MarkupExtension, в котором мне нужны строковые параметры из XAML для создания нового объекта. Можно ли использовать привязку нестроковых параметров к полю в области данных datacontext?

Другими словами, как я могу сделать что-то вроде этого?

<ListBox ItemsSource="{Binding Source={local:MyMarkupExtension {x:Type Button},IncludeMethods={Binding Source=CustomerObject.IsProblematic}}}" />

где IncludeMethods=CustomerObject.IsProblematic дает мне эту ошибку: привязка не может быть установлена ​​в свойстве 'IncludeMethods' типа 'TypeDescriptorExtension'. "Связывание" может быть установлено только в DependencyProperty объекта DependencyObject.

Кто-нибудь может мне помочь?

спасибо

4b9b3361

Ответ 1

"Связывание" может быть установлено только в DependencyProperty объекта DependencyObject - это правда. Проблема в том, что класс MarkupExtension не выводится из DependencyObject, поэтому невозможно установить привязку к ним свойств.

[EDIT]

Временное решение использует ValueConverters. Другим обходным решением является изменение языка С#, чтобы разрешить множественное наследование. Кстати, в Silverlight MarkupExtension реализует интерфейс IMarkupExtension, поэтому я попытался реализовать его в своем пользовательском расширении и получить его из DependecyObject, добавив DependencyProperty туда и установив привязку к нему. Это не сбой, но привязка действительно установлена ​​ после Вызывается ProvideValue(). Поэтому даже в Silverlight нет решения (или это сложно - см. Ссылку, представленную в ответе Klaus78). В WPF MarkupExtension не реализует никакого интерфейса, поэтому вы не можете привязать его к свойствам.

Ответ 3

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

public class MarkupExtensionWithBindableParam : MarkupExtension
{
    public BindingBase Param1 { get; set; } // its necessary to set parameter type as BindingBase to avoid exception that binding can't be used with non DependencyProperty

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        DependencyObject targetObject;
        DependencyProperty targetProperty;

        if (target != null && target.TargetObject is DependencyObject && target.TargetProperty is DependencyProperty)
        {
            targetObject = (DependencyObject)target.TargetObject;
            targetProperty = (DependencyProperty)target.TargetProperty;
        }
        else
        {
            return this; // magic
        }

        // Bind the Param1 to attached property Param1BindingSinkProperty 
        BindingOperations.SetBinding(targetObject, MarkupExtensionWithBindableParam.Param1BindingSinkProperty, Param1);

        // Now you can use Param1

        // Param1 direct access example:
        object param1Value = targetObject.GetValue(Param1BindingSinkProperty);

        // Param1 use in binding example:
        var param1InnerBinding = new Binding() { Source = targetObject, Path = new PropertyPath("(0).SomeInnerProperty", Param1BindingSinkProperty) }); // binding to Param1.SomeInnerProperty
        return param1InnerBinding.ProvideValue(serviceProvider); // return binding to Param1.SomeInnerProperty
    }

    private static DependencyProperty Param1BindingSinkProperty = DependencyProperty.RegisterAttached("Param1BindingSink", typeof(object)// set the desired type of Param1 for at least runtime type safety check
                       , typeof(MarkupExtensionWithBindableParam ), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
}

Использование прост:

<TextBlock Text={local:MarkupExtensionWithBindableParam Param1={Binding Path="SomePathToParam1"}}/>