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
#endregion using 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中自定义你的绘制(三) 在WPF中自定义你的绘制(三) ...
- 在WPF中自定义你的绘制(四)
原文:在WPF中自定义你的绘制(四) 在WPF中自定义你的绘制(四) ...
- 在WPF中自定义你的绘制(一)
原文:在WPF中自定义你的绘制(一) 在WPF中自定义你的绘制(一) ...
- 在WPF中自定义你的绘制(二)
原文:在WPF中自定义你的绘制(二) 在WPF中自定义你的绘制(二) ...
- 在VS2005中设置WPF中自定义按钮的事件
原文:在VS2005中设置WPF中自定义按钮的事件 上篇讲了如何在Blend中绘制圆角矩形(http://blog.csdn.net/johnsuna/archive/2007/08/13/17407 ...
- WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探
原文:WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探 最近因为项目需要,开始学习如何使用WPF开发桌面程序.使用WPF一段时间之后,感 ...
- 示例:WPF中自定义MessageService应用DialogHost、Snackbar、NotifyIcon显示各种场景提示消息
原文:示例:WPF中自定义MessageService应用DialogHost.Snackbar.NotifyIcon显示各种场景提示消息 一.目的:不同交互场景需要提示不同的消息,不同的消息需要用不 ...
- 示例:WPF中自定义StoryBoarService在代码中封装StoryBoard、Animation用于简化动画编写
原文:示例:WPF中自定义StoryBoarService在代码中封装StoryBoard.Animation用于简化动画编写 一.目的:通过对StoryBoard和Animation的封装来简化动画 ...
随机推荐
- Leetcode:263
编写一个程序判断给定的数是否为丑数.丑数就是只包含质因数 2, 3, 5 的正整数.示例 1:输入: 6输出: true解释: 6 = 2 × 3示例 2:输入: 8输出: true解释: 8 = 2 ...
- bootstrap+Ajax+SSM(maven搭建)实现增删改查
https://www.jianshu.com/p/d76316b48e3e 源码: https://github.com/Ching-Lee/crud 功能点: 分页 数据校验 ajax Rest风 ...
- package-info.java的使用
一.引入 上文中,提到了注解类JyzTargetPackage可以定义为@Target(ElementType.PACKAGE),可是在被注解类里我无论怎么加,编译器都报错,于是引入了package- ...
- XSS高级利用
XSS会话劫持 (1)Cookie简介 Cookie是能够让网站服务器把少量文本数据存储到客户端的硬盘.内存,或者是从客户端的硬盘.内存读取数据的一种技术. Cookie是一段随HTTP请求.响应一起 ...
- P1171 售货员的难题--搜索(剪枝)
题目背景 数据有更改 题目描述 某乡有nn个村庄(1<n \le 201<n≤20),有一个售货员,他要到各个村庄去售货,各村庄之间的路程s(0<s<1000)s(0<s ...
- AI R-CNN目标检测算法
Region-CNN,简称R-CNN,是首次将深度学习应用于目标检测的算法. bounding box IOU 非极大值抑制 selective search 参考链接: https://blog.c ...
- bootstrapvalidator 用法
1.引入js.css 2.用法 API:http://bootstrapvalidator.votintsev.ru/api/ 参考博客:https://blog.csdn.net/u01393846 ...
- linux内存源码分析 - 内存回收(lru链表)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 概述 对于整个内存回收来说,lru链表是关键中的关键,实际上整个内存回收,做的事情就是处理lru链表的收缩,所以 ...
- BZOJ4361 isn 树状数组、DP、容斥
传送门 不考虑成为非降序列后停止的限制,那么答案显然是\(\sum\limits_{i=1}^N cnt_i \times (N-i)!\),其中\(cnt_i\)表示长度为\(i\)的非降序列数量 ...
- 【thinkPHP框架】Failed opening required 'header.php' include_path='.;c:\php5\pear 终级解决方案
ThinkPHP框架中的某一个php页面,想要去引入其他php页面,结果反复报错,无解. 各种百度,有说是文件权限不够的,也有说配置不对的,反正一个都没有解决这个问题,依然无解. 最终,找到了办法,似 ...