从代码中设置自定义MarkupExtension

如何从代码中设置自定义MarkupExtension

您可以轻松地从Xaml设置。 BindingDynamicResource

  

通过代码设置相同的值需要一些不同的方法

  1. 绑定:使用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(??); 

我在这个问题上发现了很少,所以可以做到吗?


HB回答了这个问题。 只是在这里添加一些细节,为什么我想这样做。 我试图为以下问题创建一个解决方法。

问题是你无法从Binding派生并覆盖ProvideValue因为它是密封的。 您将不得不这样做: 自定义WPF绑定标记扩展的基类 。 但问题是当你将Binding返回到Setter会得到一个exception,但是在Style之外它可以正常工作。

我已经在几个地方读过你应该返回MarkupExtension本身,如果TargetObject是一个Setter ,一旦它被应用到一个实际的FrameworkElement就允许它重新进行revavuate,这是有道理的。

  • 数据触发器中的标记扩展
  • MarkupExtension的巨大限制
  • 自定义WPF绑定标记扩展的基类 (在注释中)

但是,仅当TargetPropertyobject类型时才有效,否则返回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 } } 

由于我意识到有时需要使用代码中的标记扩展,因此我总是试图以这种方式编写它们。