从代码中设置自定义MarkupExtension
如何从代码中设置自定义MarkupExtension
?
您可以轻松地从Xaml设置。 Binding
和DynamicResource
。
通过代码设置相同的值需要一些不同的方法
-
绑定:使用textBox.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(??);
我在这个问题上发现了很少,所以可以做到吗?
HB回答了这个问题。 只是在这里添加一些细节,为什么我想这样做。 我试图为以下问题创建一个解决方法。
问题是你无法从Binding
派生并覆盖ProvideValue
因为它是密封的。 您将不得不这样做: 自定义WPF绑定标记扩展的基类 。 但问题是当你将Binding
返回到Setter
会得到一个exception,但是在Style
之外它可以正常工作。
我已经在几个地方读过你应该返回MarkupExtension
本身,如果TargetObject
是一个Setter
,一旦它被应用到一个实际的FrameworkElement
就允许它重新进行revavuate,这是有道理的。
- 数据触发器中的标记扩展
- MarkupExtension的巨大限制
- 自定义WPF绑定标记扩展的基类 (在注释中)
但是,仅当TargetProperty
是object
类型时才有效,否则返回exception。 如果你看一下BindingBase
的源代码,你可以看到它确实如此,但看起来框架有一些秘密成分使它工作。
我认为没有代码等价,服务只能通过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; } } }
唯一剩下的就是能够从XAML对象模型获取引用回“CustomMarkup”。 有了上述内容,您需要依赖它来引用它。
如果您的标记扩展非常简单并创建了一个绑定并从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电视节目可能会对这个问题有所了解。 我记得他们展示了一些可能有用的代码示例。
正如HB所指出的, MarkupExtension
仅用于在XAML中使用。
Binding
独特之处在于它实际上是从MarkupExtension
派生的,这使得可以使用扩展语法{Binding ...}
或完整标记
并在代码中使用它。
但是,您始终可以尝试创建一个中间对象(类似于BindingOperations
),它知道如何使用自定义标记扩展并将其应用于目标DependencyObject
。
为此,我相信您需要使用XamlSetMarkupExtensionAttribute
(对于.NET 4)或IReceiveMarkupExtension
接口(对于.NET 3.x)。 我不完全确定如何使用属性和/或界面,但它可能指向正确的方向。
如何从代码中设置自定义 MarkupExtension?
如果你可以修改它,那么只需将逻辑提取到单独的SomeMethod
,可以单独调用和/或从ProvideValue
。
而不是
textBox.Text = customExtension.ProvideValue(??);
你打电话给它
customExtension.SomeMethod(textBox, TextBox.TextProperty);
我们经常创建自定义属性扩展 (在xaml中使用如下):
这可以这样写:
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 } }
由于我意识到有时需要使用代码中的标记扩展,因此我总是试图以这种方式编写它们。