简书,你好
写在前面的话
集中的文章作为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
共同学习,写下你的评论
评论加载中...
作者其他优质文章