写在前面的话
在我的另一的文集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的理解。谢谢大家。