从代码中设置自定义MarkupExtension

时间:2011-09-20 18:15:41

标签: c# wpf xaml markup markup-extensions

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

您可以轻松地从Xaml设置。 BindingDynamicResource也是如此。

<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. DynamicResource:使用SetResourceReference

    textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle");
    
  3. CustomMarkup :如何从代码中设置自定义MarkupExtension?我是否应该致电ProvideValue,如果是这样,我如何获得IServiceProvider?*

    CustomMarkupExtension customExtension = new CustomMarkupExtension();
    textBox.Text = customExtension.ProvideValue(??);
    
  4. 我在这个问题上发现了很少,所以可以做到吗?


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

    问题在于,由于密封,您无法从Binding派生并覆盖ProvideValue。你必须做这样的事情:A base class for custom WPF binding markup extensions。但问题是,当您将Binding返回到Setter时会出现异常,但在Style之外,它会正常工作。

    我已经在几个地方读到,如果MarkupExtensionTargetObject,则应该返回Setter本身,以便在将其应用于实际FrameworkElement后重新提交TargetProperty 1}}这是有道理的。

    但是,仅当object的类型为BindingBase时才有效,否则异常就会返回。如果您查看{{1}}的源代码,您可以看到它确实如此,但看起来框架中有一些秘密因素可以使它工作。

6 个答案:

答案 0 :(得分:6)

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

  

MarkupExtension只有一个虚拟方法ProvideValue。输入serviceProvider参数是在XAML处理器调用标记扩展时将服务传递给实现的方式。

答案 1 :(得分: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”。有了上述内容,您需要依赖它来引用它。

答案 2 :(得分: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()。

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

答案 3 :(得分:2)

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

答案 4 :(得分:1)

H.B.指出,MarkupExtension仅用于XAML。

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

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

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

答案 5 :(得分: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
    }
}

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