C# 从代码中设置自定义标记扩展名

C# 从代码中设置自定义标记扩展名,c#,wpf,xaml,markup,markup-extensions,C#,Wpf,Xaml,Markup,Markup Extensions,如何从代码设置自定义标记扩展名 您可以从Xaml轻松设置if。绑定和动态资源也是如此 <TextBox FontSize="{Binding MyFontSize}" Style="{DynamicResource MyStyle}" Text="{markup:CustomMarkup}"/> 动态资源:使用SetResourceReference textBox.SetResourceReference(TextBox.StylePrope

如何从代码设置自定义
标记扩展名

您可以从Xaml轻松设置if。
绑定
动态资源
也是如此

<TextBox FontSize="{Binding MyFontSize}"
         Style="{DynamicResource MyStyle}"
         Text="{markup:CustomMarkup}"/>
  • 动态资源:使用SetResourceReference

    textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle");
    
  • 自定义标记:如何从代码设置自定义
    MarkupExtension
    ?我是否应该调用
    ProvideValue
    ,在这种情况下,我如何获得
    IServiceProvider
    *

    CustomMarkupExtension customExtension = new CustomMarkupExtension();
    textBox.Text = customExtension.ProvideValue(??);
    
  • 令人惊讶的是,我在这个问题上发现的很少,那么,这能做到吗


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

    问题是您无法从
    绑定中派生
    并重写
    ProvideValue
    ,因为它是密封的。您必须执行类似的操作:。但问题是,当您将
    绑定
    返回给
    设置程序
    时,会得到一个异常,但在
    样式
    之外,它工作正常

    我在几个地方读到过,如果
    TargetObject
    是一个
    Setter
    ,您应该返回
    MarkupExtension
    本身,以允许它在应用于实际的
    框架元素后重新评估,这是有意义的

    • (在评论中)

    但是,这仅在
    TargetProperty
    类型为
    对象时有效,否则将返回异常。如果您查看
    BindingBase
    的源代码,您会发现它确实做到了这一点,但框架似乎有一些秘密成分使其工作。

    我认为没有代码等价物,这些服务只能通过XAML提供。发件人:

    MarkupExtension只有一个虚拟方法ProvideValue。InputServiceProvider参数是当XAML处理器调用标记扩展时,服务如何与实现通信


    可能会对这个问题有所帮助。我记得他们展示了一些可能有用的代码示例。

    正如H.B.所指出的,
    MarkupExtension
    仅用于XAML中

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

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


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

    作为替代方案,它是在代码中生成的,但不一定像XAML那样优雅:

            var markup = new CustomMarkup();
            markup.ProvideValue(new Target(textBox, TextBox.TextProperty));
    
    目标的实施简单地说是:

    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()

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

    如何从代码中设置自定义标记扩展名

    如果可以修改它,那么只需将逻辑提取到单独的
    SomeMethod
    ,可以单独调用和/或从
    ProvideValue
    调用

    而不是

    textBox.Text = customExtension.ProvideValue(??);
    
    你管它叫什么

    customExtension.SomeMethod(textBox, TextBox.TextProperty);
    

    我们通常会创建自定义属性扩展(在xaml中这样使用):


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

    嘿,H.B.是的,我也读过这篇文章,但我希望仍然有一种方法。这是一个非常坏的消息,而定制
    MarkupExtensions
    似乎是一个半实用的概念。如果TargetProperty不是
    object
    类型,则它们不能用于
    样式
    Setter
    ,因此我希望通过在附加的行为中应用它来解决此问题,但计划已经完成。不管怎样,谢谢你的帮助answer@Meleak:好的,正如名称所说的MarkupExtensions扩展了标记,它们并不是真正用于代码中。顺便说一句,我无法重现在Setter中使用MarkupExtensions的任何问题。我同意,这不仅仅是名称所暗示的,只是仍然希望有一种方法:)我在评论中不是很清楚,问题是当您向
    Setter
    提供
    绑定
    值时。你已经回答了这个问题,我接受你的回答。在我的问题中添加了一些细节,说明了我为什么要这样做。在这个特定主题上,我没有找到任何东西,但是+1是一个有趣的链接:)我不确定你所说的“使绑定独一无二”是什么意思,它源自
    MarkupExtension
    ,我的自定义
    MarkupExtension
    ,我看不出区别。对于你答案的第二部分,你似乎在正确的轨道上,很高兴知道+1我试图传达的是,
    绑定
    没有遵循创建
    MyClass
    并附带
    MyClassExtension
    的常规,后者通常会在标记中提供类型为
    MyClass
    的值。我仍然不确定是否遵循,但任何
    标记扩展
    都可以像
    {markup:Custom}那样设置
    。不管怎样,没关系。我通过使用
    多重绑定解决了我的问题。是的,我认识这个人
    
    customExtension.SomeMethod(textBox, TextBox.TextProperty);
    
    <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
        }
    }