Silverlight3系列(七)数据绑定 Data Binding 3 数据类型转换 Data Converter
7 数据转换
在普通的情况下,数据从后台到前台显示,没有任何变化。看起来是符合逻辑的,但是有可能不是你想要的效果,数据源的数据可能是的低级别的(这里的低级别是说数据比较原始,或者说是数据库可以理解的,不是最终用户可以理解的形式),你不想让他直接显示在界面上。例如:你可能会将数字变成用户可以看懂的形式、或者是想让日期显示成长格式的字符串。如果是这样的话,你需要将数据转换成正确的显示形式。如果是双向绑定,你也需要将用户提供的数据转换成数据库可以存储的形式。
很幸运的是,Silverlight允许你创建一个值转换类来完成。这个值转换类负责将数据库的值转换成可以显示的值,如果是双向绑定,还可以将用户输入的值转换成数据库可以存储的值。
在数据绑定中,值转换是很常用的。你可以在下列的情况中使用它们:
1)将数据格式化成string。例如:将数字转换成string,这是最常用的方式,但不是唯一的功能。
2)创建一个特殊的Silverlight类型。例如:你将读取的一些二进制数据创建成一个BitmapImage对象,以便可以将它绑定到一个Image控件。
3)有条件的改变一个绑定数据的一些属性。例如:通过值转换类改变一个控件的背景色,或者是高亮显示其中的一部分。
7.1 使用值转换格式换字符串
值转换对于需要从数字显示为字符串的时候,是一个很好的工具。例如:你的一个商品的单价属性,在数据库中你可能使用decimal存储,但是显示的时候你需要显示为3.9900,或者你还需要显示成一个钱的符号,就好像¥49.99。
你可以通过下面的步骤创建一个值转换类。
1)创建一个实现IValueConverter接口(接口在System.Windows.Data空间下面)的类,将这个类放在你的Silverlight项目中,而不是webservice项目中。
2)实现Convert()方法,将原始值转换为可以显示的值。
3)实现ConvertBack()方法,反过来,将显示的值,转换为原始的值。
上图是一个转换的示意图。
在decima数值l到currency货币的转换中,你可以使用Decimal.ToString()方法来完成,你需要设置转换结果的形式“C”
string currentyText=decimalPrice.ToString("C");
方法中的环境设置使用了当前的运行环境语言文件,如果简体中文是当前的语言环境的话,就会显示¥符号。如果你想换成另外一个语言显示形式,你就需要声明语言环境,例如你想显示美元形式,你需要这么做
CultureInfo culture=new CultureInfo("en-US");
string currentyText=decimalPrice.ToString("C",culture);
下面是几种常用的字符串格式形式。
Type |
FormatString |
Example |
Currency |
C |
$1,234.50 |
Scientific 科学计数法 |
E |
1234.50E+004 |
Percentage |
P |
45.6% |
Fixed Decimal |
F? |
小数点后的几位,F3会格式为123.400,F0会格式为123 |
Short Date |
d |
M/d/yyyy For example:10/30/2009 |
Long Date |
D |
dddd,MMMM dd,yyyy For example:Monday,January 30,2005 |
Long Date and Short Time |
f |
dddd,MMMM dd,yyyy HH:mm aa For example:Monday,January 30,2005 10:00 AM
|
Long Date and Long Time |
F |
ddd,MMMM dd,yyyy HH:mm:ss aa For example:Monday,January 30,2005 10:00:26 AM |
ISO Sortable Standard |
s |
yyyy-MM-dd HH:mm:ss For example:2005-01-30 10:02:18 |
Month and Day |
M |
MMMM dd For example:January 30 |
General |
G |
M/d/yyyy HH:mm:ss aa(依赖于本地设置) For example:10/30/2005 10:00:23 AM |
从显示格式转换成数值,可能需要一点小技巧。double类型的Parse()和TryParse()方法是一种选择,但是通常他们不能处理带有货币符号的字符串。解决的办法是,使用重载的Parse和TryParse方法,在方法中添加一个System.Globalization.NumberStyles值。如果你使用System.Globalization.NumberStyles.Any,你可以成功的去除货币符号,如果它存在。
下面是一个价格转换的例子。
代码
public class PriceConverter:IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double price = (double)value;
return price.ToString("C", culture);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string price = value.ToString();
double result;
if (double.TryParse(price, System.Globalization.NumberStyles.Any, culture, out result) == true)
{
return result;
}
return value;
}
#endregion
如果要使用这个转换,你需要在声明一个命名空间,下面假定你的值转换类处于Silverlight.ValueConverter命名空间下面。
xmlns:local="clr-namespace:Silverlight.ValueConverter"
你需要将上面的这个属性添加到顶级节点<UserControl>中,现在你需要在页面的资源集合中创建一个PriceConverter类,
<Usercontrol.Resources>
<local:PriceConverter x:Key="PriceConverter" ></local:PriceConverter>
</Usercontrol.Resources>
然后你可以绑定一个静态资源
<TextBox Margin="5" Grid.Row="0" Grid.Column="0"
Text="{Binding UnitCost, Mode=TwoWay, Converter={StaticResource PriceConverter}}"></TextBox>
7.2 使用值转换创建对象
值转换独立于数据的存储方式和数据的展示方式。例如:你有一个图片以二进制的形式存储在数据库中,你可以把它转换成System.Windows.Media.Imaging.BitmapImage对象。当然了,这么设计也许不是最合适的。
你可能需要很灵活的创建更多的类型,你的数据类库可能被Silverlight和Windows Form(需要转换成System.Drawing.Bitmap)都有调用。在这种情况下,你需要将原始的二进制转换成BitmapImage类型。
从二进制转换为一张图片,你首先要创建一个BitmapImage对象,同时将二进制读到MemoryStram中。然后,调用BitmapImage.SetSource()方法,将流中的数据传输给BitmapImage对象。
产品表中的可能没有存储二进制的图片信息,可能存储的是和产品有关的图片的路径。这种情况下,你就更有理由延缓创建图片对象。第一,图片可能没有访问权限,依赖于应用运行在哪里;第二,没有必要分配额外的内存资源,除非你要显示它。
ProductImage字段存储的是图片的文件名,不是图片的完整路径。这样给你一个灵活的配置,你可以从任何地方获取图片。值转换环类有任务将存储的文件名结合网站,返回图片的地址。网站的根URI使用一个自定义属性RootUri存储,默认是网站的Uri,下面是完整代码。
{
private string _rootUri;
public string RootUri
{
get { return this._rootUri; }
set { this._rootUri = value; }
}
public ImagePathConverter()
{
string uri = "http://baidu.com/";
_rootUri = uri.Remove(uri.LastIndexOf("/"),
uri.Length - uri.LastIndexOf('/'));
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string imagePath = RootUri + "/" + (string)value;
return new System.Windows.Media.Imaging.BitmapImage(new Uri(imagePath));
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//图片是不能编辑的,这里就没有必要支持反向转换
throw new NotImplementedException();
}
#endregion
}
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namaspace:Silverlight.ValueConverter"
Width="400" Height="300">
<UserControl.Resources >
<local:ImagePathConverter x:Key="ImagePathConverter"></local:ImagePathConverter>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions >
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Image Margin="5" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left"
Source="{Binding ProductImagePath, Mode=TwoWay, Converter={StaticResource ImagePathConverter}}"></Image>
</Grid>
</UserControl>
你可以改进这里例子,首先,在创建一个不存在图片的BitmapImage的时候你可以抛异常,在你绑定数据的时候可以收到。或者你可以在ImagePathConverter类中加一个属性,来配置这个行为。添加一个bool类型的属性SuppressExceptions。如果设置为true,你可以捕获一个异常,然后再Convert方法中返回一个空字符串,或者添加一个默认的图片,如果有异常就显示这个默认的图片。
7.3 应用有条件的格式化
有一些很有意思的转换在表现层中设计不到。相反,你打算使用一些数据规则来控制显示效果。
例如:你想要根据值的大小变化背景颜色,你就可以声明下面这样一个类
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
namespace Silverlight.ValueConverter
{
public class PriceToBackgroundConverter:IValueConverter
{
public double MinimumPriceToHighlight
{ set; get; }
public Brush HighlightBrush
{ set; get; }
public Brush DefaultBrush { set; get; }
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double price = (double)value;
if (price >= MinimumPriceToHighlight)
return HighlightBrush;
else
return DefaultBrush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:Silverlight.ValueConverter"
Width="400" Height="300">
<UserControl.Resources >
<loc:PriceToBackgroundConverter x:Key="PriceToBackgroundConverter"
DefaultBrush="{x:null}" HighlightBrush="Orange" MinimumPriceToHighlight="50"></loc:PriceToBackgroundConverter>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions >
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Border Background="{Binding UnitCost, Converter={StaticResource PriceToBackgroundConverter}}" ></Border>
</Grid>
</UserControl>
<Border Background="{Binding UnitCost, Converter={StaticResource PriceToBackgroundConverter}, ConverterParameter=50}" ></Border>
如果想上面这样传递了ConverterParameter参数的话,可以在Convert方法中使用
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double price = (double)value;
if (price >= double.Parse (parameter .ToString ()))
return HighlightBrush;
else
return DefaultBrush;
}