WPF数据转换

在基本绑定中,信息从源到目标的传递过程中没有任何变化。这看起来是符合逻辑的,但我们并不总是希望出现这种行为。通常,数据源使用的是低级表达方式,我们可能不希望直接在用户界面使用这种低级表达方式。WPF提供了两个工具,来进行数据转换:

字符串格式化

通过设置 Binding.StringFormat 属性对文本形式的数据进行转换------例如包含日期和数字的字符串。

值转换器

该功能更强大,使用该功能可以将任意类型的源数据转换为任意类型的对象表示,然后可以传递到关联的控件。

使用StringFormat属性

cs 复制代码
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Path=Price, StringFormat=ConvertDirectly:{0:C}}"></TextBlock>
<TextBox   Grid.Row="0" Grid.Column="1" Text="{Binding Path=Price, StringFormat={}{0:C}}"></TextBox>
<TextBox   Grid.Row="2" Grid.Column="1" Text="{Binding Path=OrderDate, StringFormat={}{0:s}}"></TextBox>

可以看到后面两个StringFormat属性以花括号 {} 开头,完整值是 {}{0:C},而不是 {0:C},第一个则只有 {0:C},这是因为在StringFormat 值以花括号开头时需要 {} 转义序列。

使用值转换器

为创建子转换器需要执行以下四个步骤:

1、创建一个实现IValueConverter接口的类

2、为该类声明添加ValueConversion特性,并指定目标数据类型

3、实现Convert()方法,该方法将数据从原来的格式转换为显示的格式

4、实现ConvertBack()方法,该方法执行反向变换,将值从显示格式转换为原格式

cs 复制代码
[ValueConversion(typeof(decimal), typeof(string))]
public class PriceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        decimal price = (decimal)value;
        return price.ToString("C", culture);
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string price = (string)value;
        if (decimal.TryParse(price, System.Globalization.NumberStyles.Any, culture, out decimal result))
        {
            return result;
        }
        return value;
    }
}

要使用这个转换器,可以将其添加到页面的资源下,然后使用 Binding.Converter 指定。

cs 复制代码
<Window>
    <Window.Resources>
        <local:PriceConverter x:Key="priceConverter"/>
        <local:PriceToBackgroundConverter x:Key="priceToBackgroundConverter" MinimumPriceToHighlight="100" DefaultBrush="{x:Null}" HighlightBrush="Orange"/>
        <local:ImagePathConverter x:Key="imagePathConverter"/>
        <local:MultiValueConverter x:Key="multiValueConverter"/>
    </Window.Resources>
    <TextBox Text="{Binding Path=Price, Converter={StaticResource priceConverter}}"></TextBox>
</Window>

多重绑定

可以将多个字段绑定到同一个输出控件,可以通过 StringFormat 或 MultiBinding.Converter 来格式化数据。多重绑定的值转换器需要实现的接口是 IMultiValueConverter,与 IValueConverter 接口比较类似,只是转换函数的第一个参数改成了数组形式。

cs 复制代码
        <TextBlock Grid.Row="4" Grid.Column="0">
            <TextBlock.Text>
                <MultiBinding StringFormat="{}{0}, {1}, {2}">
                    <Binding Path="Price"/>
                    <Binding Path="OrderDate"/>
                    <Binding Path="Volume"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
        <TextBlock Grid.Row="5" Grid.Column="0">
            <TextBlock.Text>
                <MultiBinding Converter="{StaticResource multiValueConverter}">
                    <Binding Path="Price"/>
                    <Binding Path="Volume"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
cs 复制代码
public class MultiValueConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        decimal price = (decimal)values[0];
        int volume = (int)values[1];
        return (price * volume).ToString("C");
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

完整代码如下:

MainWindow.xaml

cs 复制代码
<Window x:Class="TestDataConverter.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestDataConverter"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <local:PriceConverter x:Key="priceConverter"/>
        <local:PriceToBackgroundConverter x:Key="priceToBackgroundConverter" MinimumPriceToHighlight="100" DefaultBrush="{x:Null}" HighlightBrush="Orange"/>
        <local:ImagePathConverter x:Key="imagePathConverter"/>
        <local:MultiValueConverter x:Key="multiValueConverter"/>
    </Window.Resources>
    <Grid Name="myGrid" Background="{Binding Path=Price, Converter={StaticResource priceToBackgroundConverter}}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Path=Price, StringFormat=ConvertDirectly:{0:C}}"></TextBlock>
        <TextBox   Grid.Row="0" Grid.Column="1" Text="{Binding Path=Price, StringFormat={}{0:C}}"></TextBox>

        <TextBlock Grid.Row="1" Grid.Column="0">ConvertWithConverter:</TextBlock>
        <TextBox   Grid.Row="1" Grid.Column="1" Text="{Binding Path=Price, Converter={StaticResource priceConverter}}"></TextBox>

        <TextBlock Grid.Row="2" Grid.Column="0">ConvertDateTime:</TextBlock>
        <TextBox   Grid.Row="2" Grid.Column="1" Text="{Binding Path=OrderDate, StringFormat={}{0:s}}"></TextBox>

        <TextBlock Grid.Row="3" Grid.Column="0">ConvertImagePath:</TextBlock>
        <Image   Grid.Row="3" Grid.Column="1" Stretch="None" HorizontalAlignment="Left" Source="{Binding Path=Image, Converter={StaticResource imagePathConverter}}"></Image>

        <TextBlock Grid.Row="4" Grid.Column="0">
            <TextBlock.Text>
                <MultiBinding StringFormat="{}{0}, {1}, {2}">
                    <Binding Path="Price"/>
                    <Binding Path="OrderDate"/>
                    <Binding Path="Volume"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
        <TextBlock Grid.Row="5" Grid.Column="0">
            <TextBlock.Text>
                <MultiBinding Converter="{StaticResource multiValueConverter}">
                    <Binding Path="Price"/>
                    <Binding Path="Volume"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
    </Grid>
</Window>

MainWindow.xaml.cs

cs 复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace TestDataConverter;

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    protected virtual bool SetProperty<T>(ref T member, T value, [CallerMemberName] string? propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(member, value))
        {
            return false;
        }
        member = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

public class Order : ViewModelBase
{
    public decimal price = 0;
    public decimal Price { get => price; set => SetProperty(ref price, value); }
    public int volume = 0;
    public int Volume { get => volume; set => SetProperty(ref volume, value); }

    public DateTime orderDate = DateTime.Now;
    public DateTime OrderDate { get => orderDate; set => SetProperty(ref orderDate, value); }

    public string image = string.Empty;
    public string Image { get => image; set => SetProperty(ref image, value); }
}
[ValueConversion(typeof(decimal), typeof(string))]
public class PriceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        decimal price = (decimal)value;
        return price.ToString("C", culture);
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string price = (string)value;
        if (decimal.TryParse(price, System.Globalization.NumberStyles.Any, culture, out decimal result))
        {
            return result;
        }
        return value;
    }
}
[ValueConversion(typeof(decimal), typeof(Brush))]
public class PriceToBackgroundConverter : IValueConverter
{
    public decimal MinimumPriceToHighlight { get; set; }
    public Brush HighlightBrush { get; set; }
    public Brush DefaultBrush { get; set; }

    public object Convert(object value, Type targetType, object parameter,
      System.Globalization.CultureInfo culture)
    {
        decimal price = (decimal)value;
        if (price >= MinimumPriceToHighlight)
            return HighlightBrush;
        else
            return DefaultBrush;
    }
    public object ConvertBack(object value, Type targetType, object parameter,
      System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}
public class ImagePathConverter : IValueConverter
{
    private string imageDirectory = Directory.GetCurrentDirectory();
    public string ImageDirectory
    {
        get { return imageDirectory; }
        set { imageDirectory = value; }
    }
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string imagePath = Path.Combine(ImageDirectory, (string)value);
        return new BitmapImage(new Uri(imagePath));
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException("The method or operation is not implemented.");
    }
}
public class MultiValueConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        decimal price = (decimal)values[0];
        int volume = (int)values[1];
        return (price * volume).ToString("C");
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        myGrid.DataContext = Order;

        Order.Price = 100;
        Order.Volume = 10;
        Order.OrderDate = DateTime.Now;
        Order.Image = "image1.gif";
    }
    public Order Order = new Order();

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Console.WriteLine("");
    }
}
相关推荐
甜甜不吃芥末21 小时前
WPF依赖属性详解
wpf
Hat_man_1 天前
WPF制作图片闪烁的自定义控件
wpf
晚安苏州2 天前
WPF Binding 绑定
wpf·wpf binding·wpf 绑定
wangnaisheng2 天前
【WPF】RenderTargetBitmap的使用
wpf
dotent·3 天前
WPF 完美解决改变指示灯的颜色
wpf
orangapple4 天前
WPF 用Vlc.DotNet.Wpf实现视频播放、停止、暂停功能
wpf·音视频
ysdysyn4 天前
wpf mvvm 数据绑定数据(按钮文字表头都可以),根据长度进行换行,并把换行的文字居中
c#·wpf·mvvm
orangapple4 天前
WPF 使用LibVLCSharp.WPF实现视频播放、停止、暂停功能
wpf
晚安苏州4 天前
WPF ControlTemplate 控件模板
wpf
晚安苏州4 天前
WPF 布局控件
wpf