代码改变世界

Silverlight3系列(七)数据绑定 Data Binding 3 数据类型转换 Data Converter

2010-01-29 12:01  Virus-BeautyCode  阅读(2049)  评论(1编辑  收藏  举报

 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数值lcurrency货币的转换中,你可以使用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.400F0会格式为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()方法是一种选择,但是通常他们不能处理带有货币符号的字符串。解决的办法是,使用重载的ParseTryParse方法,在方法中添加一个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,下面是完整代码。

  

代码
    public partial  class ImagePathConverter:IValueConverter 
    {
        
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
    }

 

 

代码
<UserControl x:Class="Silverlight.ValueConverter.ImagePathConverterPage"
    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;
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
        { 
setget; }
        
public Brush HighlightBrush
        { 
setget; }
        
public Brush DefaultBrush { setget; }


        
#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
    }
}

 

 

代码
<UserControl x:Class="Silverlight.ValueConverter.ImagePathConverterPage"
    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;
        }