从代码设置自定义 MarkupExtension

发布于 2024-12-05 17:27:02 字数 2157 浏览 1 评论 0原文

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

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

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

通过代码隐藏设置相同的值需要稍微不同的方法

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

    绑定绑定 = new Binding("MyFontSize");
    BindingOperations.SetBinding(textBox, TextBox.FontSizeProperty, 绑定);
    
  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 时,您会遇到异常,但在 Style 之外它工作正常。

我在几个地方读到,如果 TargetObjectSetter,则应该返回 MarkupExtension 本身,以允许它在被执行后重新评估。应用于实际的FrameworkElement,这是有道理的。

但是,只有当 TargetProperty 的类型为 object 时才有效,否则异常会再次出现。如果您查看 BindingBase 的源代码,您会发现它正是这样做的,但似乎该框架有一些秘密成分可以使其工作。

How do you set a custom MarkupExtension from code?

You can easily set if from Xaml. The same goes for Binding and DynamicResource.

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

Setting the same values through code behind requires a little different approach

  1. Binding: Use textBox.SetBinding or BindingOperations.SetBinding

    Binding binding = new Binding("MyFontSize");
    BindingOperations.SetBinding(textBox, TextBox.FontSizeProperty, binding);
    
  2. DynamicResource: Use SetResourceReference

    textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle");
    
  3. CustomMarkup: How do I set a custom MarkupExtension from code? Should I call ProvideValue and it that case, how do I get a hold of a IServiceProvider?*

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

I found surprisingly little on the subject so, can it be done?


H.B. has answered the question. Just adding some details here to why I wanted to do this. I tried to create a workaround for the following problem.

The problem is that you can't derive from Binding and override ProvideValue since it is sealed. You'll have to do something like this instead: A base class for custom WPF binding markup extensions. But then the problem is that when you return a Binding to a Setter you get an exception, but outside of the Style it works fine.

I've read in several places that you should return the MarkupExtension itself if the TargetObject is a Setter to allow it to reeavaluate once it is being applied to an actual FrameworkElement and this makes sense.

However, that only works when the TargetProperty is of type object, otherwise the exception is back. If you look at the source code for BindingBase you can see that it does exactly this but it appears the framework has some secret ingredient that makes it work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

如果没有你 2024-12-12 17:27:02

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

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

I think there is no code-equivalent, the services are only available via XAML. From MSDN:

MarkupExtension has only one virtual method, ProvideValue. The input serviceProvider parameter is how the services are communicated to implementations when the markup extension is called by a XAML processor.

情话墙 2024-12-12 17:27:02

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

What about this as an alternative, it is generated in code but not necessarily as elegant as XAML:

        var markup = new CustomMarkup();
        markup.ProvideValue(new Target(textBox, TextBox.TextProperty));

The implementation for Target is simply:

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; } }
}

The only thing that remains is the ability to get a reference back to 'CustomMarkup' from the XAML object model. With the above you need to hang-on to a reference to it.

怎言笑 2024-12-12 17:27:02

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

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

If your markup extension is fairly simple and creates a binding and returns the result from ProvideValue() then you can add a simple helper method:

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));
    }
}

And then in code, you can just call CommandExtension.SetBinding() instead of BindingOperations.SetBinding().

Obviously, if you are doing anything more complex than this then this solution may not be appropriate.

孤凫 2024-12-12 17:27:02

此 Silverlight 电视节目可能会提供一些线索这个问题。我记得他们展示了一些可能有用的代码示例。

This Silverlight TV show might shed some light on this issue. I recall them showing some code samples that might be helpful.

离不开的别离 2024-12-12 17:27:02

正如 HB 所指出的,MarkupExtension 只能在 XAML 中使用。

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

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

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

As H.B. pointed out, a MarkupExtension is only intended to be used within XAML.

What makes Binding unique is that it actually derives from MarkupExtension which is what makes it possible to use the extension syntax {Binding ...} or the full markup <Binding>...</Binding> and use it in code.

However, you can always try creating an intermediary object (something akin to BindingOperations) that knows how to use your custom markup extension and apply it to a target DependencyObject.

To do this, I believe you would need to make use of the XamlSetMarkupExtensionAttribute (for .NET 4) or the IReceiveMarkupExtension interface (for .NET 3.x). I am not entirely sure how to make use of the attribute and/or interface, but it might point you in the right direction.

莫言歌 2024-12-12 17:27:02

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

因为我意识到有时有必要使用代码中的标记扩展我一直试图以这样的方式来写它们。

How do I set a custom MarkupExtension from code?

If you can modify it, then simply extract the logic into separate SomeMethod which can be called alone and/or from ProvideValue.

Then instead of

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

you just call it

customExtension.SomeMethod(textBox, TextBox.TextProperty);

Often we are creating custom property extensions (used like this in xaml):

<TextBox Text="{local:SomeExtension ...}" />

This can be written like this:

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
    }
}

Since I realized there is sometimes a necessity to use markup extensions from code I am always trying to write them in such a way.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文