为了账号安全,请及时绑定邮箱和手机立即绑定

WPF基础之Binding解析(一)

标签:
Java

webp

简书,你好

写在前面的话

集中的文章作为WPF的高级主题,需要读者理解WPF框架中的一些概念和特性,因此,笔者又开了一个专题来讲一下WPF中几个基本特性。笔者作为曾经的java爱好者,在本系列讲解中,会透过源码来解析WPF各个知识点的奥秘。有人会问,这和学习没有学习过java有什么关系呢?我想学习过java的人应该不会这样问,因为在学习java的过程中,我们经常听到的一句话就是源码面前了无秘密,我们要撕掉代码的伪装,看到特性或者主题的本质。

本文概要

提到WPF的几大特性,读者可能认为我会先讲解依赖属性,但我认为当读者进入WPF的大门时,首先应该看到的是标记语言XMAL(Extensible Application Markup Language),这种语言主要的特性就体现在可扩展上。因此我在WPF众多主题中首先选择了Binding类来分解解析。
可能读者还是不明白我首先选择这个主题的原因,那么请看下面两个类的继承关系:

  public class Binding : BindingBase
{
......
}
 public abstract class BindingBase : MarkupExtension
{
......
}

当读者看到第二个类的继承关系的时候,应该就明白我选择的原因了,对!因为Binding类间接继承了MarkupExtension,它是我们使用XAML的基础,微软给该抽象类的定义是:

为可以由 .NET Framework XAML 服务及其他 XAML 读取器和 XAML 编写器支持的 XAML 标记扩展实现提供基类。

从上述文字中我们可以看出MarkupExtension是多么的重要,所以,在讲Binding之前,我们按照继承关系先来梳理一下MarkupExtension

言归正传,看看MarkupExtension给我们带来了什么?

看了MarkupExtension类后,读者斗志昂扬的情志瞬间一落千丈,因为MarkupExtension除了一个受保护的无参构造函数外,就给我们提供了一个方法,那就让我们看一下这个方法有何神通吧,先来看看此方法的声明:

       //
        // 摘要:
        //     当在派生类中实现时,返回用作此标记扩展的目标属性值的对象。
        //
        // 参数:
        //   serviceProvider:
        //     可为标记扩展提供服务的服务提供程序帮助程序。
        //
        // 返回结果:
        //     要在应用了扩展的属性上设置的对象值。
        public abstract object ProvideValue(IServiceProvider serviceProvider);

serviceProvider参数是为该类及其子类扩展标记提供解析服务的接口,它的目的通过该扩展标记将一种输入转化为另一种类型的输出,即文档中写的目标属性的值(实例)。

举个例子

我们通过MarkupExtension的另一个简单的子类StaticExtension来说明一下这个方法的魅力。先来看一下StaticExtension的两种等效的用法:

<Button Foreground="{x:Static SystemColors.ActiveCaptionBrush}"></Button>
<Button>
            <Button.Foreground>
                <x:Static Member="SystemColors.ActiveCaptionBrush"></x:Static>
            </Button.Foreground>
        </Button>

我们再来看下StaticExtension类的声明:

    public class StaticExtension : MarkupExtension
    {  
        public StaticExtension();   
        public StaticExtension(string member);        public string Member { get; set; }
     
        [DefaultValue(null)]        public Type MemberType { get; set; }        public override object ProvideValue(IServiceProvider serviceProvider);
    }

其实第一种方式也可以写成这种:

   <Button Foreground="{x:Static  Member=SystemColors.ActiveCaptionBrush}"></Button>

这样我们就可以看到SystemColors.ActiveCaptionBrush是作为一个字符串传给了该类的Member属性,然后通过ProvideValue方法将该字符串解析成目标属性Foreground的实例。这是一个非常简单的过程,一下是ProvideValue方法的源码:

public override object ProvideValue(IServiceProvider serviceProvider)
        {            if (Member == null)
            {                throw new InvalidOperationException(SR.Get(SRID.MarkupExtensionStaticMember));
            }
 
            object value;            if (MemberType != null)
            {
                value = SystemResourceKey.GetSystemResourceKey(MemberType.Name + "." + Member);                if (value != null)
                {                    return value;
                }
            }            else
            {
                value = SystemResourceKey.GetSystemResourceKey(Member);                if (value != null)
                {                    return value;
                } 
                // Validate the _member
 
                int dotIndex = Member.IndexOf('.');                if (dotIndex < 0)
                {                    throw new ArgumentException(SR.Get(SRID.MarkupExtensionBadStatic, Member));
                } 
                // Pull out the type substring (this will include any XML prefix, e.g. "av:Button")
 
                string typeString = Member.Substring(0, dotIndex);                if (typeString == string.Empty)
                {                    throw new ArgumentException(SR.Get(SRID.MarkupExtensionBadStatic, Member));
                } 
                // Get the IXamlTypeResolver from the service provider
 
                if (serviceProvider == null)
                {                    throw new ArgumentNullException("serviceProvider");
                }
 
                IXamlTypeResolver xamlTypeResolver = serviceProvider
.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver;                if (xamlTypeResolver == null)
                {                    throw new ArgumentException(SR.Get(SRID.MarkupExtensionNoContext, GetType().Name, "IXamlTypeResolver"));
                } 
                // Use the type resolver to get a Type instance
 
                MemberType = xamlTypeResolver.Resolve(typeString); 
                // Get the member name substring
 
                Member = Member.Substring(dotIndex + 1, Member.Length - dotIndex - 1);
            }
 
            value = CommandConverter.GetKnownControlCommand(MemberType, Member);            if (value != null)
            {                return value;
            } 
            return base.ProvideValue(serviceProvider);
        }

从源代码中我们可以清楚的看到Member的解析过程,以及MemberType是怎么被使用的。

通告

本系列下一篇文章,将讲述怎么自定义MarkupExtension,这样能加深我们对MarkupExtension的理解。谢谢大家。



作者:杨凯本尊
链接:https://www.jianshu.com/p/5ebcd4d65ce2


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消