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.
Кто-нибудь может мне помочь?
спасибо
Ответы
Ответ 1
"Связывание" может быть установлено только в DependencyProperty объекта DependencyObject - это правда. Проблема в том, что класс MarkupExtension
не выводится из DependencyObject
, поэтому невозможно установить привязку к ним свойств.
[EDIT]
Временное решение использует ValueConverters. Другим обходным решением является изменение языка С#, чтобы разрешить множественное наследование. Кстати, в Silverlight MarkupExtension
реализует интерфейс IMarkupExtension
, поэтому я попытался реализовать его в своем пользовательском расширении и получить его из DependecyObject
, добавив DependencyProperty
туда и установив привязку к нему. Это не сбой, но привязка действительно установлена после Вызывается ProvideValue(). Поэтому даже в Silverlight нет решения (или это сложно - см. Ссылку, представленную в ответе Klaus78). В WPF MarkupExtension не реализует никакого интерфейса, поэтому вы не можете привязать его к свойствам.
Ответ 2
Эта ссылка содержит информацию о
Пользовательская расшивка разметки со свойствами связывания
ИЗМЕНИТЬ
Кто-то заставляет меня заметить, что это работает только для Silverlight, потому что в WPF MarkupExtension не реализует интерфейс IMarkupExtension. (Спасибо EvAlex)
Ответ 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"}}/>