Настройка пользовательского MarkupExtension из кода
как вы устанавливаете пользовательский MarkupExtension
из кода?
вы можете легко установить, если из Xaml. То же самое касается Binding
и DynamicResource
.
<TextBox FontSize="{Binding MyFontSize}"
Style="{DynamicResource MyStyle}"
Text="{markup:CustomMarkup}"/>
установка тех же значений через код позади требует немного другого подхода
-
обязательные: использовать текстовое поле.SetBinding или BindingOperations.SetBinding
Binding binding = new Binding("MyFontSize"); BindingOperations.SetBinding(textBox, TextBox.FontSizeProperty, binding);
-
DynamicResource: использовать SetResourceReference
textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle");
-
CustomMarkup: Как установить пользовательский
MarkupExtension
из кода? Должен ли я позвонитьProvideValue
и это тот случай, как я могу получитьIServiceProvider
?*CustomMarkupExtension customExtension = new CustomMarkupExtension(); textBox.Text = customExtension.ProvideValue(??);
я нашел удивительно мало на эту тему, так что, это можно сделать?
Г. Б.: ответил на вопрос. Просто добавляю некоторые детали, почему я хотел это сделать. Я попытался создать обходной путь для следующей проблемы.
проблема в том, что вы не можете наследовать от Binding
и заменить ProvideValue
так как он запечатан. Вам придется сделать что-то вроде этого: базовый класс для пользовательских расширений разметки привязки WPF. Но тогда проблема в том, что когда вы возвращаете Binding
до Setter
вы получаете исключение, но за пределами Style
он работает нормально.
я читал в нескольких местах, что вы должны вернуть MarkupExtension
сама если TargetObject
это Setter
чтобы позволить ему reeavaluate после того, как он применяется к фактическому FrameworkElement
и это имеет смысл.
- расширение разметки в Data Trigger
- огромное ограничение MarkupExtension
- базовый класс для пользовательских расширений разметки привязки WPF (в комментариях)
однако это работает только тогда, когда TargetProperty
типа object
, иначе исключение-обратно. Если вы посмотрите на исходный код BindingBase
вы можете видеть, что он делает именно это, но кажется, что структура имеет какой-то секретный ингредиент, который заставляет его работать.
6 ответов
Я думаю, что нет эквивалента кода, службы доступны только через XAML. От MSDN:
MarkupExtension имеет только один виртуальный метод, ProvideValue. Входной параметр serviceProvider-это способ передачи служб реализациям при вызове расширения разметки процессором XAML.
Как насчет этого в качестве альтернативы, он генерируется в коде, но не обязательно так элегантно, как 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. С вышеизложенным вам нужно повиснуть на ссылке на него.
если расширение разметки довольно простое и создает привязку и возвращает результат из 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().
очевидно, что если вы делаете что-то более сложное, чем это, то это решение может быть неуместным.
Это Silverlight ТВ-шоу может пролить свет на эту проблему. Я помню, что они показывали некоторые примеры кода, которые могут быть полезны.
как указал Х. Б., a MarkupExtension
предназначен только для использования в XAML.
что составляет Binding
уникальным является то, что на самом деле происходит от MarkupExtension
что позволяет использовать синтаксис расширения {Binding ...}
или полная разметка <Binding>...</Binding>
и использовать его в коде.
однако вы всегда можете попробовать создать промежуточный объект (что-то сродни BindingOperations
), который знает, как использовать пользовательские расширения разметки и применить его к цели DependencyObject
.
чтобы сделать это, я считаю, что вам нужно будет использовать XamlSetMarkupExtensionAttribute
(для .NET 4) или IReceiveMarkupExtension
интерфейс (для .NET 3.икс.) Я не совсем уверен, как использовать атрибут и/или интерфейса, но он может указать вам в правильном направлении.
Как установить таможни 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
}
}
поскольку я понял, что иногда необходимо использовать расширения разметки из кода, я всегда пытаюсь написать их таким образом.