这篇文章给大家介绍怎么在c#项目中自定义MarkupExtension,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
Markup Extension,顾名思义,就是对xaml的扩展,在XAML中,规定如果属性以{}开始及结束,就是Markup Extension,Markup Extension指的是继承于MarkupExtension的类,首先我们通过一张图来看看WPF中有哪些已知的Markup Extension。
看了这张图片之后是不是对这个MarkupExtension有一个常规的认识,你会发现这个在WPF中实在是太重要了,通过这个MarkupExtension我们能够实现绑定、资源等等一系列的操作,在介绍完这个之后,我们来看看,这个抽象的MarkupExtension基类到底是什么?里面包含些什么?怎么去使用它?
#region 程序集 WindowsBase.dll, v3.0.0.0// C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll#endregionusing System;namespace System.Windows.Markup{ // 摘要: // 为所有 XAML 标记扩展提供基类。 public abstract class MarkupExtension { // 摘要: // 初始化从 System.Windows.Markup.MarkupExtension 派生的类的新实例。 protected MarkupExtension(); // 摘要: // 在派生类中实现时,返回一个对象,此对象被设置为此标记扩展的目标属性的值。 // // 参数: // serviceProvider: // 可以为标记扩展提供服务的对象。 // // 返回结果: // 将在扩展应用到的属性上设置的对象值。 public abstract object ProvideValue(IServiceProvider serviceProvider); }}
其实看看里面的内容,仅仅提供了一个抽象的方法ProvideValue,我们在继承这个抽象类后需要去重载这个抽象方法,然后来实现自己的逻辑。
在对整个MarkupExtension介绍之后,我们可以对它进行一个总结,那就是:
XAML标记扩展语法格式:
<元素对象 对象属性=”{扩展标记 扩展标记属性 = 扩展属性值}” />
这个是不是很熟悉,如果还是不够直观的话,我们可以通过代码来进行说明:
<TextBox Text=”{Binding Path=ProductName}”/>
再来一个复杂一些的例子吧
<Popup IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" x:Name="SubMenuPopup" Focusable="false" AllowsTransparency="true" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}"/>
类似的这种我们在WPF中见到的是在是太多了,那么既然基类是一个抽象方法那么我们是不是可以通过重载这种方式来写自己的MarkupExtension呢?这个当然是可以的,我们可以通过下面的几个例子来进行相应的说明。
示例1:通过MarkupExtension绑定MenuItem的Icon属性。
我们知道,MenuItem的Icon属性可以通过下面的方式进行设置:
<MenuItem Header="New"> <MenuItem.Icon> <Image Source="data/cat.png"/> </MenuItem.Icon></MenuItem>
这个是MSDN介绍的常规方式,在这里我们可以通过三种不同的方式来达到这个目的,具体来看看是怎么实现的吧?
<Menu Grid.Column="0"> <MenuItem Header="文本"> <MenuItem Header="重做"> <MenuItem.Icon> <Image Stretch="Uniform" Source="{extension:ImageBinding Redo}"></Image> </MenuItem.Icon> </MenuItem> <MenuItem Header="撤销"> <MenuItem.Icon> <Image Stretch="Uniform" Source="{extension:ImageBinding Undo}"></Image> </MenuItem.Icon> </MenuItem> <MenuItem Header="保存所有"> <MenuItem.Icon> <Image Stretch="Uniform" Source="{Binding SaveAll,Converter={StaticResource SourceConverter}}"></Image> </MenuItem.Icon> </MenuItem> <MenuItem Header="测试"> <MenuItem.Icon> <Image Stretch="Uniform" Source="Resources/Images/Redo.png"></Image> </MenuItem.Icon> </MenuItem> </MenuItem> <MenuItem Header="编辑"></MenuItem> <MenuItem Header="视图"></MenuItem> <MenuItem Header="插件"></MenuItem> </Menu>
第一种方式就是我们今天重点介绍的通过继承MarkupExtension来实现同样的效果,我们来具体分析一下这个ImageBinding
public class ImageBindingExtension : System.Windows.Markup.MarkupExtension { public ImageBindingExtension(string path) : this() { Path = path; } public ImageBindingExtension() { } [ConstructorArgument("path")] public string Path { get; set; } public override object ProvideValue(IServiceProvider serviceProvider) { IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; if (target.TargetObject is Setter) { return new Binding(Path) { Converter = ImgaeSourceConverter.Default }; } else { Binding binding = new Binding(Path) { Converter = ImgaeSourceConverter.Default }; return binding.ProvideValue(serviceProvider); } } }
这里面我们定义的Path属性就是绑定到ViewModel中的一个特定的属性,这里我们通过重写ProvideValue方法,最终调用BindingBase的ProvideValue返回ImageSource对象,这里是通过一个转换器来实现源属性(字符串)到目标属性ImageSource的转换的,我们会发现,其实这种方法和直接绑定并设置转换器其实效果是一样的,只不过第一种方式更为直观,将所有的转换过程都放在了重写ProvideValue函数的过程中了,这个读者在后面可以对照demo去认真思考然后加以总结。
示例2:通过MarkupExtension绑定到ListBox的ItemsSource属性
这个稍微复杂一些,我们在Reflection这个MarkupExtension中加入了一些自定义的属性,这些属性能够控制后面返回的数据源的最终内容,其实这个也是非常好理解的,我们在定义RelativeSource这个MarkupExtension的时候,也是通过定义Mode、AncestorType、AncestorLevel等属性组合起来最终实现在视觉树上找到最终的元素。在代码里面也不复杂主要是通过反射来获取Button的属性、方法、事件、字段等等,这个具体的实现过程可以参考后面的代码。
public class ReflectionExtension : System.Windows.Markup.MarkupExtension { public Type CurrentType { get; set; } public bool IncludeMethods { get; set; } public bool IncludeFields { get; set; } public bool IncludeEvents { get; set; } public ReflectionExtension(Type currentType) { this.CurrentType = currentType; } public override object ProvideValue(IServiceProvider serviceProvider) { if (this.CurrentType == null) { throw new ArgumentException("Type argument is not specified"); } ObservableCollection<string> collection = new ObservableCollection<string>(); foreach (PropertyInfo p in this.CurrentType.GetProperties()) { collection.Add(string.Format("属性 : {0}", p.Name)); } if (this.IncludeMethods) { foreach (MethodInfo m in this.CurrentType.GetMethods()) { collection.Add(string.Format("方法 : {0} with {1} argument(s)", m.Name, m.GetParameters().Count())); } } if (this.IncludeFields) { foreach (FieldInfo f in this.CurrentType.GetFields()) { collection.Add(string.Format("字段 : {0}", f.Name)); } } if (this.IncludeEvents) { foreach (EventInfo e in this.CurrentType.GetEvents()) { collection.Add(string.Format("事件 : {0}", e.Name)); } } return collection; } }
关于怎么在c#项目中自定义MarkupExtension就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。