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("");
    }
}
相关推荐
麻花20136 小时前
C#之WPF的C1FlexGrid空间的行加载事件和列事件变更处理动态加载的枚举值
开发语言·c#·wpf
lcintj6 小时前
【WPF】Prism学习(九)
学习·wpf·prism
界面开发小八哥6 小时前
界面控件DevExpress WPF中文教程:网格视图数据布局的列和卡片字段
wpf·界面控件·devexpress·ui开发·用户界面
△曉風殘月〆6 小时前
如何在WPF中嵌入其它程序
wpf
Crazy Struggle7 小时前
功能齐全的 WPF 自定义控件资源库(收藏版)
.net·wpf·ui控件库
shepherd枸杞泡茶20 小时前
WPF动画
c#·.net·wpf
lcintj20 小时前
【WPF】Prism学习(十)
学习·wpf·prism
wyh要好好学习20 小时前
WPF数据加载时添加进度条
ui·wpf
code_shenbing20 小时前
跨平台WPF框架Avalonia教程 三
前端·microsoft·ui·c#·wpf·跨平台·界面设计
lcintj2 天前
【WPF】Prism学习(八)
学习·wpf·prism