WPF中自定义MarkupExtension
在介绍这一篇文章之前,我们首先来回顾一下WPF中的一些基础的概念,首先当然是XAML了,XAML全称是Extensible Application Markup Language (可扩展应用程序标记语言),是专门用于WPF技术中的UI设计语言,通过使用XAML语言,我们能够快速设计软件界面,同时能够通过绑定这种机制能够很好地实现界面和实现逻辑之间的解耦,这个就是MVVM模式的核心了,那么今天我们介绍的MarkupExtension和XAML之间又有哪些的关系呢?
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;}}
今天就如何自定义MarkupExtension做了一个简单的介绍,最重要的是能够通过这种方式来实现自己的合理绑定的目的,同时通过这种合理的扩展方式也能够让我们的代码更加灵活多变,最后附上整个测试用的DEMO,希望有需要的点击进行下载,这篇文章只是一个抛砖引玉的作用,希望读完之后能引发自己更多的共鸣,最终代码越写越好。
WPF中自定义MarkupExtension相关推荐
- 在WPF中自定义你的绘制(二)
在WPF中自定义你的绘制(二) 原文:在WPF中自定义你的绘制(二) 在WPF中自定义你的绘制(二) ...
- WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探
原文:WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探 最近因为项目需要,开始学习如何使用WPF开发桌面程序.使用WPF一段时间之后,感觉WPF的开发思 ...
- html自定义指针,如何自定义鼠标指针 怎样在wpf中自定义鼠标指针
如何在ppt中自定义鼠标指针的形状? 魔兽世界怎么自定义鼠标指针 在系统 控制面板 里面鼠标属性 指针里面改就可以了 下面有个浏览 可以自定义换图案 怎样在网页中设定一整套自定义的鼠标指针? 麻烦了 ...
- WPF中自定义窗体标题栏
最新文章:Virson's Blog 这个例子是在看<深入浅出WPF>第5章控件与布局的Canvas控件时,对书上的例子做了一下小扩展:在此记下,以备后用: XAML代码: 1 <W ...
- 【转】在WPF中自定义控件
周银辉的开发博客(WPF) 在WPF中自定义控件(1) 一, 不一定需要自定义控件 在使用WPF以前,动辄使用自定义控件几乎成了惯性思维,比如需要一个带图片的按钮,但在WPF中此类任务却不需要如此大费 ...
- WPF中改进自定义Command一些想法
Command来源于Command命令模式,Command模式它封装的是命令,把命令发出者的责任和命令执行者的责任分开,直白的说为了调用与具体实现解耦.关于理论俺向来是不擅长的,而且各位达人的文章也已 ...
- wpf中xaml的类型转换器与标记扩展
wpf中xaml的类型转换器与标记扩展 原文:wpf中xaml的类型转换器与标记扩展 这篇来讲wpf控件属性的类型转换器 类型转换器 类型转换器在asp.net控件中已经有使用过了,由于wpf的界面是 ...
- WPF中对三维模型的控制
原文:WPF中对三维模型的控制 (以下选自南开大学出版社出版的<WPF和Silverlight教程>) 3Dmax中的建模模型可以导出为obj文件格式,将此文件导入WPF项目中,由WPF完 ...
- WPF绘制自定义窗口
原文:WPF绘制自定义窗口 WPF是制作界面的一大利器,下面就用WPF模拟一下360的软件管理界面,360软件管理界面如下: 界面不难,主要有如下几个要素: 窗体的圆角 自定义标题栏及按钮 自定义状态 ...
- 了解 WPF 中的路由事件和命令
目录 路由事件概述 WPF 元素树 事件路由 路由事件和组合 附加事件 路由命令概述 操作中的路由命令 命令路由 定义命令 命令插入 路由命令的局限 避免命令出错 超越路由命令 路由处理程序示例 要想 ...
最新文章
- python close_wait_线上大量CLOSE_WAIT原因深入分析
- C语言程序练习- L1-040 最佳情侣身高差 (10分)
- 后台服务系统之Dubbo协议
- MZOJ 1134: 二叉苹果树
- HTML+CSS+JS实现 ❤️three 3D铅笔绘图工具特效❤️
- CCF	2017-3-2 学生排队
- The system is running in low-graphics mode
- js验证银行卡号 luhn校验规则
- 兜兜转转,回到原点,Hello Mr.my yesterday
- 单片机代码真值怎么取反c语言,手把手教你学单片机的C语言程序设计(八)运算符与表达式(续).pdf...
- arm开发板 qt5.6.0 qt使用文泉驿字体
- 给小米手机安装google框架
- 高级架构师备考经验分享
- VUE启动报错:Error: The project seems to require yarn but it‘s not installed
- 分库分表会带来读扩散问题?怎么解决?
- Cocos2D:塔防游戏制作之旅(十)
- Linux-USB学习 -- USB枚举过程
- 2021安全范儿高校挑战赛ByteCTF线上赛部分Writeup
- 服务器安装macos虚拟机,windows服务器装macos虚拟机系统
- 数组,向量和矩阵以及空间的维数