WPF基础之Binding解析(一)

WPF基础之Binding解析(一)_第1张图片
,你好

写在前面的话

在我的另一的文集WPF Control Development中写了控件开发选型指南一文,笔者以为WPF Control Development文集中的文章作为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的两种等效的用法:



我们再来看下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);
    }

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

   

这样我们就可以看到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的理解。谢谢大家。


WPF基础之Binding解析(一)_第2张图片
我的微信

你可能感兴趣的:(WPF基础之Binding解析(一))