在WPF程序中,数据绑定是非常常用的手段。伴随着数据绑定,我们通常还需要编写一些Converter。而编写Converter是一件非常枯燥的事情,并且大量的converter不容易组织和维护。

今天在网上发现了一篇文章SwitchConverter – A "switch statement" for XAML,它可以通过XAML的方式编写一些类似switch-case方式的converter,十分简洁明了。例如,对如如下的数据绑定转换:

可以直接在XAML中通过如下方式写converter:

<Grid>
<Grid.Resources>
<e:SwitchConverter x:Key="WeatherIcons">
<e:SwitchCase When="Sunny" Then="Sunny.png" />
<e:SwitchCase When="Cloudy" Then="Cloudy.png" />
<e:SwitchCase When="Rain" Then="Rain.png" />
<e:SwitchCase When="Snow" Then="Snow.png" />
</e:SwitchConverter>
</Grid.Resources>
<Image Source="{Binding Condition, Converter={StaticResource WeatherIcons}}" />
</Grid>

原文已经附上了代码的工程,但由于担心哪天方校长抖威风而导致该文章失效,这里将其转录了下来,一共三个文件:

SwitchCase.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Windows;
using System.Windows.Markup; namespace SwitchConverterDemo
{ /// <summary>
/// An individual case in the switch statement.
/// </summary>
[ContentProperty( "Then" )]
public sealed class SwitchCase : DependencyObject
{ #region Constructors /// <summary>
/// Initializes a new instance of the <see cref="T:SwitchCase"/> class.
/// </summary>
public SwitchCase( )
{ } #endregion #region Properties /// <summary>
/// Dependency property for the <see cref="P:When"/> property.
/// </summary>
public static readonly DependencyProperty WhenProperty = DependencyProperty.Register( "When", typeof( object ), typeof( SwitchCase ), new PropertyMetadata( default( object ) ) ); /// <summary>
/// The value to match against the input value.
/// </summary>
public object When
{
get
{
return (object)GetValue( WhenProperty );
}
set
{
SetValue( WhenProperty, value );
}
} /// <summary>
/// Dependency property for the <see cref="P:Then"/> property.
/// </summary>
public static readonly DependencyProperty ThenProperty = DependencyProperty.Register( "Then", typeof( object ), typeof( SwitchCase ), new PropertyMetadata( default( object ) ) ); /// <summary>
/// The output value to use if the current case matches.
/// </summary>
public object Then
{
get
{
return (object)GetValue( ThenProperty );
}
set
{
SetValue( ThenProperty, value );
}
} #endregion } // class } // namespace

SwitchCaseCollection.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Linq; namespace SwitchConverterDemo
{ /// <summary>
/// A collection of switch cases.
/// </summary>
public sealed class SwitchCaseCollection : Collection<SwitchCase>
{ #region Constructors /// <summary>
/// Initializes a new instance of the <see cref="T:SwitchCaseCollection"/> class.
/// </summary>
internal SwitchCaseCollection( )
{ } #endregion #region Methods /// <summary>
/// Adds a new case to the collection.
/// </summary>
/// <param name="when">The value to compare against the input.</param>
/// <param name="then">The output value to use if the case matches.</param>
public void Add( object when, object then )
{
Add(
new SwitchCase {
When = when,
Then = then
}
);
} #endregion } // class } // namespace

SwitchConverter.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup; namespace SwitchConverterDemo
{ /// <summary>
/// Produces an output value based upon a collection of case statements.
/// </summary>
[ContentProperty( "Cases" )]
public class SwitchConverter : IValueConverter
{ #region Constructors /// <summary>
/// Initializes a new instance of the <see cref="T:SwitchConverter"/> class.
/// </summary>
public SwitchConverter( )
: this( new SwitchCaseCollection( ) )
{
} /// <summary>
/// Initializes a new instance of the <see cref="T:SwitchConverter"/> class.
/// </summary>
/// <param name="cases">The case collection.</param>
internal SwitchConverter( SwitchCaseCollection cases )
{ Contract.Requires( cases != null ); Cases = cases;
StringComparison = StringComparison.OrdinalIgnoreCase; } #endregion #region Properties /// <summary>
/// Holds a collection of switch cases that determine which output
/// value will be produced for a given input value.
/// </summary>
public SwitchCaseCollection Cases
{
get;
private set;
} /// <summary>
/// Specifies the type of comparison performed when comparing the input
/// value against a case.
/// </summary>
public StringComparison StringComparison
{
get;
set;
} /// <summary>
/// An optional value that will be output if none of the cases match.
/// </summary>
public object Else
{
get;
set;
} #endregion #region Methods /// <summary>
/// Converts a value.
/// </summary>
/// <param name="value">The value produced by the binding source.</param>
/// <param name="targetType">The type of the binding target property.</param>
/// <param name="parameter">The converter parameter to use.</param>
/// <param name="culture">The culture to use in the converter.</param>
/// <returns>A converted value. If the method returns null, the valid null value is used.</returns>
public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
{ if ( value == null ) { // Special case for null
// Null input can only equal null, no convert necessary return Cases.FirstOrDefault( x => x.When == null ) ?? Else; } foreach ( var c in Cases.Where( x => x.When != null ) ) { // Special case for string to string comparison
if ( value is string && c.When is string ) {
if ( String.Equals( (string)value, (string)c.When, StringComparison ) ) {
return c.Then;
}
} object when = c.When; // Normalize the types using IConvertible if possible
if ( TryConvert( culture, value, ref when ) ) {
if ( value.Equals( when ) ) {
return c.Then;
}
} } return Else; } /// <summary>
/// Converts a value.
/// </summary>
/// <param name="value">The value that is produced by the binding target.</param>
/// <param name="targetType">The type to convert to.</param>
/// <param name="parameter">The converter parameter to use.</param>
/// <param name="culture">The culture to use in the converter.</param>
/// <returns>A converted value. If the method returns null, the valid null value is used.</returns>
public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
{
throw new NotSupportedException( );
} /// <summary>
/// Attempts to use the IConvertible interface to convert <paramref name="value2"/> into a type
/// compatible with <paramref name="value1"/>.
/// </summary>
/// <param name="culture">The culture.</param>
/// <param name="value1">The input value.</param>
/// <param name="value2">The case value.</param>
/// <returns>True if conversion was performed, otherwise false.</returns>
private static bool TryConvert( CultureInfo culture, object value1, ref object value2 )
{ Type type1 = value1.GetType( );
Type type2 = value2.GetType( ); if ( type1 == type2 ) {
return true;
} if ( type1.IsEnum ) {
value2 = Enum.Parse( type1, value2.ToString( ), true );
return true;
} var convertible1 = value1 as IConvertible;
var convertible2 = value2 as IConvertible; if ( convertible1 != null && convertible2 != null ) {
value2 = System.Convert.ChangeType( value2, type1, culture );
return true;
} return false; } #endregion } // class } // namespace

这种绑定的方式非常简洁有效,但也有限制,只能处理简单的switch-case形式的关联,并且不能有转换逻辑。不过已经可以替换很大一部分Converter了(非常典型的应用就是这种枚举到图片的转换)。

另外,网上也有一些开源库,实现了一些常见的通用Converter。例如:http://wpfconverters.codeplex.com/。在自己编写Converter之前,不妨先使用这些通用的Converter。

一种用XAML写Data Converter的方式的更多相关文章

  1. WPF的DataGrid的某个列绑定数据的三种方法(Binding、Converter、DataTrigger)

    最近在使用WPF的时候,遇到某个列的值需要根据内容不同进行转换显示的需求.尝试了一下,大概有三种方式可以实现: 1.传统的Binding方法,后台构造好数据,绑定就行. 2.转换器方法(Convert ...

  2. Xaml引用图片路径的方式

    最近写代码的时候遇到过好几次引用某个路径下图片资源的情况,思索了一下,便将自己所知的在xaml里引用图片资源的方法写成了个小Demo,并完成了这篇博文.希望罗列出的这些方式能够对大家有所帮助. Xam ...

  3. js replace 全局替换 以表单的方式提交参数 判断是否为ie浏览器 将jquery.qqFace.js表情转换成微信的字符码 手机端省市区联动 新字体引用本地运行可以获得,放到服务器上报404 C#提取html中的汉字 MVC几种找不到资源的解决方式 使用Windows服务定时去执行一个方法的三种方式

    js replace 全局替换   js 的replace 默认替换只替换第一个匹配的字符,如果字符串有超过两个以上的对应字符就无法进行替换,这时候就要进行一点操作,进行全部替换. <scrip ...

  4. Java中几种office文档转pdf的方式

    最近公司要做office的文档,搜集了几种office文档转pdf的方式,简单的做下总结 我主要尝试了三种方式:openoffice,aspose,jacob 对他们进行了大文件,小文件,在linux ...

  5. Android两种为ViewPager+Fragment添加Tab的方式

    在Android开发中ViewPager的使用是非常广泛的,而它不仅仅能够实现简单的开始引导页,还可以结合Fragment并添加Tab作为选项卡或为显示大批量页面实现强大的顺畅滑动 下面介绍两种为Vi ...

  6. 简单总结几种常见web攻击手段及其防御方式

    web攻击手段有几种,本文简单介绍几种常见的攻击手段及其防御方式 XSS(跨站脚本攻击) CSRF(跨站请求伪造) SQL注入 DDOS XSS 概念 全称是跨站脚本攻击(Cross Site Scr ...

  7. 简单地总结几种常见web攻击手段及其防御方式

    web攻击手段有几种,本文简单介绍几种常见的攻击手段及其防御方式 XSS(跨站脚本攻击) CSRF(跨站请求伪造) SQL注入 DDOS XSS 概念 全称是跨站脚本攻击(Cross Site Scr ...

  8. jQuery.data() 的实现方式,jQuery16018518865841457738的由来,jQuery后边一串数字的由来

    原文地址: http://xxing22657-yahoo-com-cn.iteye.com/blog/1042440 jQuery.data() 的实现方式 jQuery.data() 的作用是为普 ...

  9. 几种常见web攻击手段及其防御方式

    XSS(跨站脚本攻击) CSRF(跨站请求伪造) SQL注入 DDOS web安全系列目录 总结几种常见web攻击手段极其防御方式 总结几种常见的安全算法 XSS 概念 全称是跨站脚本攻击(Cross ...

随机推荐

  1. 事件Qevent的接受和忽略 和重定义 事件过滤器(转)

    转载来源:http://blog.csdn.net/seanyxie/article/details/5821970 事件处理流程:某个事件发生------>exec()循环会接收到这个事件-- ...

  2. tomcat启动后服务访问404

      .  解决办法: 在tomcat文件中有个work文件夹.其中,tomcat属于admin用户,work属于 admin用户 ,启动服务由admin用户启动. 但是发现work文件下的目录权限属于 ...

  3. NIO--1

    1.为什么不直接用jdk NIO(1) API繁杂(2) 原始NIO可靠性不是很高.可靠性包括:断开重连,网络闪断,半包读写,失败缓存(3) NIO 的epoll BUG会导致多路复用器Selecto ...

  4. 【Luogu】P4284概率充电器(概率树形DP)

    题目链接 这题好神啊…… 设f[i]为i没电的概率,初始化$f[i]=1-q[i]$ 之后x的电有三个来源: 1.x自己有电 2.x的儿子给它传来了电 3.x的父亲给它传来了电 对于2和3操作分别做一 ...

  5. easyui中tab页中js脚本无法加载的问题及解决方法

    我发现tab页中<script src="xxx.js">方式加载的脚本没有生效,firebug看请求也没有请求相应的脚本文件. 单独在浏览器中打开tab页中的页面js ...

  6. 【CZY选讲·Yjq的棺材】

    题目描述 Yjq想要将一个长为宽为的矩形棺材(棺材表面绝对光滑,所以棺材可以任意的滑动)拖过一个L型墓道. 如图所示,L型墓道两个走廊的宽度分别是和,呈90°,并且走廊的长度远大于. 现在Hja ...

  7. AtCoder Regular Contest 093 E: Bichrome Spanning Tree(生成树)

    Bichrome Spanning Tree 题意: 给出一个n个点,m条边的无向连通图,现在要给每条边染色,可以染成黑色或者白色. 现在要求在染色完毕后,找出一个至少包含一条黑边和一条白边的最小生成 ...

  8. 如何用cookie保存用户的登录的密码和用户名

    思路:绘制一个简单的登录界面的Servlet并要在此页面中读取保存密码和用户名的cookie--->在登录处理界面的servlet中把用户名和密码保存到cookie中 //登录界面的Servle ...

  9. 转:用VMProtect和ASProtect 的SDK加密应用程序

    最近想用VMProtect和ASProtect 的SDK加密一个程序,结果搞了半天没搞成,网上没看到在VC中如何使用VMProtect的SDK加密,于是琢磨了一下,总算成功了,最后有一点点心得,与大家 ...

  10. 使用rssh创建一个安全的文件服务器

    使用rssh创建一个安全的文件服务器 目前有这样一个需求,公司需要一台linux服务器作为文件服务器,但是基于安全性考虑,我不想使用ftp或者samba,但又必须允许用户上传文件.怎么办呢? 因为是l ...