8

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

您可以从 Xaml 轻松设置 if。Binding和也是如此DynamicResource

<TextBox FontSize="{Binding MyFontSize}"
         Style="{DynamicResource MyStyle}"
         Text="{markup:CustomMarkup}"/>

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

  1. 绑定:使用 textBox.SetBinding 或 BindingOperations.SetBinding

    Binding binding = new Binding("MyFontSize");
    BindingOperations.SetBinding(textBox, TextBox.FontSizeProperty, binding);
    
  2. 动态资源:使用 SetResourceReference

    textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle");
    
  3. CustomMarkup:如何MarkupExtension从代码中设置自定义?在这种情况下,我应该打电话给我ProvideValue,我该如何获得IServiceProvider?*

    CustomMarkupExtension customExtension = new CustomMarkupExtension();
    textBox.Text = customExtension.ProvideValue(??);
    

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


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

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

我已经在几个地方读到过,如果它是 a ,则您应该返回它MarkupExtension本身,以允许它在应用于实际时重新评估,这是有道理的。TargetObjectSetterFrameworkElement

但是,这只适用TargetProperty于 type object,否则异常返回。如果您查看源代码,BindingBase您会发现它确实做到了这一点,但该框架似乎有一些使其工作的秘密成分。

4

6 回答 6

6

我认为没有等效代码,这些服务只能通过 XAML 获得。来自MSDN

MarkupExtension 只有一个虚方法,ProvideValue。输入 serviceProvider 参数是当 XAML 处理器调用标记扩展时服务与实现通信的方式。

于 2011-09-20T19:21:40.833 回答
5

作为替代方案,它是在代码中生成的,但不一定像 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”的引用。有了以上内容,您需要保留对它的引用。

于 2012-02-09T04:20:36.877 回答
3

如果您的标记扩展相当简单并创建一个绑定并从 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()。

显然,如果您正在做比这更复杂的事情,那么这个解决方案可能不合适。

于 2012-11-15T22:59:25.907 回答
2

这个 Silverlight 电视节目可能会对这个问题有所了解。我记得他们展示了一些可能有用的代码示例。

于 2011-09-20T19:24:57.177 回答
1

正如 HB 所指出的, aMarkupExtension仅打算在 XAML 中使用。

Binding独特之处在于它实际上派生自它,这使得MarkupExtension可以使用扩展语法{Binding ...}或完整标记<Binding>...</Binding>并在代码中使用它。

BindingOperations但是,您始终可以尝试创建一个知道如何使用自定义标记扩展并将其应用于目标的中间对象(类似于) DependencyObject

为此,我相信您需要使用XamlSetMarkupExtensionAttribute(对于 .NET 4)或IReceiveMarkupExtension接口(对于 .NET 3.x)。我不完全确定如何使用属性和/或接口,但它可能会为您指明正确的方向。

于 2011-09-20T21:31:13.363 回答
0

如何从代码中设置自定义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
    }
}

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

于 2017-06-23T11:36:44.120 回答