国内做网站网站风险大吗,asp网站栏目如何修改,上海微信网站建设价格,最新版app下载安装在使用WPF进行编程的过程中#xff0c;我们常常需要使用XAML的标记扩展#xff1a;{Binding}#xff0c;{x:Null}等等。那么为什么WPF提供了XAML标记扩展这一功能#xff0c;我们又如何创建自定义的标记扩展呢。这就是本文将要讨论的内容。 一#xff0e;从标记扩展的分析… 在使用WPF进行编程的过程中我们常常需要使用XAML的标记扩展{Binding}{x:Null}等等。那么为什么WPF提供了XAML标记扩展这一功能我们又如何创建自定义的标记扩展呢。这就是本文将要讨论的内容。 一从标记扩展的分析说起 在WPF中软件开发人员需要以类似于XML的格式编写XAML。如下面代码所示 1 Window …2 StackPanel …3 TextBlock …/4 /StackPanel5 /Window 但是在实际开发过程中我们却常常需要使用标记扩展如对绑定的使用 1 Window …2 StackPanel3 TextBlock Text{Binding src:DataSource.Description}/4 /StackPanel5 /Window 您会好奇为什么提供这种特殊的语法其实这是因为XAML本身无法完成某些特定的功能所导致的。如果需要深刻地了解产生该问题的原因我们就需要从XAML编译器是如何对XAML进行解析的讲起。 无论XAML的最终表示形式是怎样编译器在处理XAML文件时所得到的都是一个个字符串。一个XML元素的开始常常表示类型实例而以属性Attribute或子元素所表示的XML组成则是在对该类型实例的属性进行设置。在分析对XML属性Attribute进行赋值的字符串时XAML处理器会根据字符串的内容决定自身的分析逻辑。 对于普通的属性赋值字符串XAML处理器会根据属性的类型决定是否需要执行对字符串的转化。如果属性的类型不是字符串那么XAML处理器会调用相应的转化逻辑如对于枚举类型的属性XAML处理器将通过Enum的Parse方法得到相应类型的数值。而对于自定义类型XAML会根据该自定义类型声明或属性声明上所标明的TypeConverter将字符串转换为该自定义类型。 也就是说可以被XAML编译器正确解释的自定义类型需要满足如下条件属性的类型需要是值类型具有默认构造函数的类型或者标明了专用类型转换器的类型即标明了特性TypeConverterAttribute。 如果一个类型不能提供满足上面条件的实线那该怎么办呢解决问题的方法就是使用XAML标记扩展。XAML编译器会按照如下方式分析XAML标记扩展如果XAML处理器遇到一个大括号或者遇到一个从MarkupExtension派生的对象元素时那么XAML编译器将按照标记扩展分析该字符串直至遇到表示结束的花括号。首先编译器会根据字符串决定标记扩展所对应的MarkupExtension类派生类。接下来编译器将按照下面的规则对扩展标记字符串进行处理1) 逗号代表各个标记的分隔符。2) 如果分隔的标记没有任何等号赋值那么它将被视为构造函数的参数。这些参数需要与构造函数的参数个数匹配。如果两个构造函数的参数个数相同那么XAML编译器将无法分析。该行为没有定义。3) 如果每个标记都包含等号那么XAML处理器将首先调用默认构造函数并对这些属性进行赋值。4) 如果标记扩展同时使用了构造函数参数以及属性赋值那么XAML处理器内部将调用对应的构造函数并对属性进行赋值。最后编译器会在应用程序加载时调用该类型的ProvideValue()函数用来定义该标记应该返回哪个对象。该函数调用会传入有关当前上下文的信息以允许ProvideValue()函数根据该上下文创建相应的对象。 如果标记扩展之间存在着嵌套那么XAML编译器将首先计算标记扩展的最内层如下面示例将首先计算x:Static 1 Setter PropertyBackground Value{DynamicResource {x:Static SystemColors.Control}}/ 可以看到XAML编译器对属性赋值进行分析的方式主要会根据其是否是标记扩展而分为使用转化或调用标记扩展的ProvideValue()函数两种。这两种方法之间的最大不同在于ProvideValue()函数可以根据上下文提供更复杂的实例创建或引用逻辑。另外标记扩展允许软件开发人员在XAML中使用带有一个参数的非默认构造函数。这也是标记扩展的一个优点。 二WPF中的标记扩展 在开始讲解之前您最好得到WPF的实现代码。虽然说本文会提供必要的代码片断但能从全局层面上分析可能会给您更多的收获。在“从Dispatcher.PushFrame()说起”一文中我们已经介绍了如何获得.net的源码而在“资源下载”一文中我们也提供了这些源码的下载地址。 首先来看看比较典型的标记扩展{x:Type}的实现 1 [MarkupExtensionReturnType(typeof(System.Type)), 2 TypeConverter(typeof(TypeExtensionConverter))] 3 public class TypeExtension : MarkupExtension 4 { 5 …… 6 public TypeExtension(System.Type type) 7 { 8 …… 9 this._type type;10 }11 12 public override object ProvideValue(IServiceProvider serviceProvider)13 {14 if (this._type null)15 {16 ……17 IXamlTypeResolver service serviceProvider.GetService(18 typeof(IXamlTypeResolver)) as IXamlTypeResolver;19 ……20 this._type service.Resolve(this._typeName);21 ……22 }23 return this._type;24 }25 26 [ConstructorArgument(type), DefaultValue((string) null)]27 public System.Type Type28 {29 get { return this._type; }30 set31 {32 ……33 this._type value;34 this._typeName null;35 }36 }37 ……38 } 首先来看看最重要的组成ProvideValue()函数。该函数首先会通过GetService()函数得到IXamlTypeResolver服务。该服务所提供的Resolve()函数会根据TypeName属性所记录的字符串解析出TypeName属性所指定的Type实例对象。 标记扩展{x:Type}的实现所展示的ProvideValue()函数实现是标记扩展实现中的典型实现。通过GetService()函数所可能得到的常用服务有IProvideValueTarget服务以知晓标记扩展所在的目标元素和属性IUriContext即可获得当前上下文中的基准UriIXamlTypeResolver用来将XAML元素名称解析为.net类型实例最典型的例子就是x:Type标记扩展。 同时上面所展示的代码使用了三个特性ConstructorArgument、TypeConverter以及MarkupExtensionReturnType。接下来我们就来看看这三个特性各自的功能。 首先就是ConstructorArgument特性。该特性用来提示XAML编译器标记扩展中所标示的构造函数参数实际上与哪个属性相对应。通过该特性所关联的属性则必须是一个可读写的属性。 那么问题接踵而至ConstructorArgument特性是使用在类型为Type的属性之上而XAML编译器所输入的则是字符串类型。为了解决这种类型上的不匹配标记扩展TypeExtension使用了另一个特性TypeConverter提示XAML编译器使用类型转换器类型TypeExtensionConverter处理标记扩展声明中所标示的字符串类型参数。 最后一个要提及的特性就是MarkupExtensionReturnType。该特性用来标明ProvideValue()函数所返回的类型。 三自定义标记扩展 现在我们就来开始编写自定义标记扩展。自定义标记扩展常常从MarkupExtension派生并重写该类的ProvideValue()函数。在本节中我们就以延迟绑定为例演示如何创建一个自定义绑定。 想象下面一种情况在一个程序的XAML中声明的绑定会在程序启动时加载并请求绑定源属性的值。对该源属性值的求解将会导致其它功能被加载。试想一下如果Ribbon所罗列的所有功能都会在程序启动时被加载那么程序的启动性能将变得非常差。 这也就是延迟绑定所需要解决的问题。只有在程序界面变为可见时绑定才会被添加到界面元素中并对其进行求解。 可能您的第一反应是创建一个自定义绑定以解决该问题。的确BindingBase类提供了虚函数CreateBindingExpressionOverride()以供自定义绑定实现者提供自定义功能。但是本文不采用该方法其原因有二该函数所提供的灵活性较差该函数具有较强的语义特征。其用于创建BindingExpression类型实例而并不适用于延迟绑定的实现。 因此使LazyBinding派生自MarkupExtension并重写它的ProvideValue()函数可能是一个更好的选择。下面就是实现LazyBinding的代码 1 [MarkupExtensionReturnType(typeof(object))] 2 public class LazyBindingExtension : MarkupExtension 3 { 4 public LazyBindingExtension() 5 { } 6 7 public LazyBindingExtension(string path) 8 { 9 Path new PropertyPath(path);10 }11 12 public override object ProvideValue(IServiceProvider serviceProvider)13 {14 IProvideValueTarget service serviceProvider.GetService15 (typeof(IProvideValueTarget)) as IProvideValueTarget;16 if (service null)17 return null;18 19 mTarget service.TargetObject as FrameworkElement;20 mProperty service.TargetProperty as DependencyProperty;21 if (mTarget ! null mProperty ! null)22 {23 // 侦听IsVisible属性的更改以在界面元素显示时通过OnIsVisibleChanged24 // 函数添加绑定25 mTarget.IsVisibleChanged OnIsVisibleChanged;26 return null;27 }28 else29 {30 Binding binding CreateBinding();31 return binding.ProvideValue(serviceProvider);32 }33 }34 35 private void OnIsVisibleChanged(object sender, 36 DependencyPropertyChangedEventArgs e)37 {38 // 添加绑定39 Binding binding CreateBinding();40 BindingOperations.SetBinding(mTarget, mProperty, binding);41 }42 43 private Binding CreateBinding() // 创建绑定类型实例44 {45 Binding binding new Binding(Path.Path);46 if (Source ! null)47 binding.Source Source;48 if (RelativeSource ! null)49 binding.RelativeSource RelativeSource;50 if (ElementName ! null)51 binding.ElementName ElementName;52 binding.Converter Converter;53 binding.ConverterParameter ConverterParameter;54 return binding;55 }56 57 #region Fields58 private FrameworkElement mTarget null;59 private DependencyProperty mProperty null;60 #endregion61 62 #region Properties63 public object Source…64 public RelativeSource RelativeSource…65 public string ElementName…66 public PropertyPath Path…67 public IValueConverter Converter…68 public object ConverterParameter…69 #endregion70 } 在这里LazyBinding仅仅探测IsVisibileChanged事件以在UI元素显示时动态添加绑定。在该类的真正实现中以何种方式完成延迟功能则需要您根据需求决定。 在XAML中软件开发人员可以像普通绑定一样使用它。但需要注意的一个问题就是MarkupExtension的嵌套使用。如果您按照下面的方法使用LazyBinding 1 TextBlock Text{local:LazyBinding ElementNamemMainWindow, PathSource, Converter{StaticResource testConverter}}/ 那么编译器会在编译时报错。从网络上的讨论来看这是一个Bug但是无论在VS2008还是VS2010中其都没有得到修正。如果我是错误的请通知我。 作为一个变通的方法我们可以在程序中通过XML元素的方法完成对LazyBinding的使用 1 TextBlock2 TextBlock.Text3 local:LazyBinding ElementNamemMainWindow PathSource Converter{StaticResource testConverter}/4 /TextBlock.Text5 /TextBlock 四命名空间管理 其实这本不属于与标志扩展关联密切的话题。只是由于WPF中的众多标记扩展都使用了x:作为前缀并且其在编写类库中非常常见因此在本文中我们将以一小部分篇幅完成对该功能的介绍。 在开发WPF程序时XAML一般包含两个xmlns声明 1 xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation2 xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml 第一个声明用来指定WPF命名空间为默认命名空间而第二个声明用来指定x:前缀对应XAML命名空间。这两个声明的关系是XAML是实现标准用来定义为实现兼容而要实现的元素而WPF是将XAML作为语言而使用的实现。如x:Type等就是标准的标记扩展而StaticResource则是WPF的特定扩展。因此有些派生自MarkupExtension类的标记扩展实际上是XAML的语言规范的一部分。它们通常使用x:前缀。 软件开发人员可以通过XmlnsDefinitionAttribute特性将多个CLR命名空间映射到单个XML命名空间。为了达到该目的软件开发人员仅需要将该特性声明置于AssemblyInfo中即可并标以assembly范围。该特性可重复使用以将多个CLR命名空间映射到一个XML命名空间。 需注意的是如果使用该映射命名空间的XAML文件与该特性处于同一项目中那么该特性声明的XML命名空间将不包含同一项目中的类型。这是因为编译时原程序集已清空而其中的类型无法在编译时解析的缘故。我并没有找到在官方文档中对该问题的说明因此如果您找到解释该行为的文档请告知。 转载请注明原文地址http://www.cnblogs.com/loveis715/archive/2012/02/06/2340669.html 商业转载请事先与我联系silverfox715sina.com 转载于:https://www.cnblogs.com/loveis715/archive/2012/02/06/2340669.html