• WPF绑定数据源时如何实现数据校验和类型转换?
  • 发布于 1个月前
  • 65 热度
    0 评论
在WPF开发中,Binding实现了数据在Source和Target之间的传递和流通,就像现实生活中的一条条道路,建立起了城镇与城镇之间的衔接,而数据校验和类型转换,就像高速公路之间的收费站和安检站。那在WPF开发中,如何实现数据的校验和类型转换呢?本文以一个简单的小例子,简述在WPF开发中,实现数据校验和类型转换的相关知识点,仅供学习分享使用,如有不足之处,还请指正。

在数据绑定Binding时,数据校验主要通过ValidationRules属性,数据转换主要通过Converter属性。

数据校验
在WPF开发中,校验数据的有效性,主要步骤如下:
1. 实现校验规则
Binding的ValidationRules是Collection<ValidationRule>类型,是一个校验规则列表,可以支持多重校验。而ValidationRule是一个抽象类,所以要实现业务规则校验,需要继承ValidationRule并实现抽象方法,如下所示:
ValidationRule的Validate方法返回一个ValidationResult对象,如果校验通过,则ValidationResult的IsValid为true,否则为false。RangeValidationRule主要校验用户输入数据的范围:
namespace WpfApp4.DataValidate
{
    public class RangeValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            double d = 0;
            if(double.TryParse(value.ToString(),out d))
            {
                if(d>=0 && d <= 100)
                {
                    return new ValidationResult(true,null);
                }
            }
            return new ValidationResult(false, "数据必须在0~100之间");
        }
    }
}
2. 设置规则
要想应用规则,首先需要引入规则对应的命名空间:xmlns:v="clr-namespace:WpfApp4.DataValidate"
然后设置Binding的ValidationRules规则,如下所示:
<TextBlock Text="年龄" VerticalAlignment="Center"></TextBlock>
<TextBox x:Name="tbAge" Width="120" Height="25" Margin="10" Validation.Error="tbAge_Error" VerticalContentAlignment="Center">
  <TextBox.Text>
    <Binding Path="Age" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
      <Binding.ValidationRules>
        <v:RangeValidationRule ValidatesOnTargetUpdated="True"></v:RangeValidationRule>
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>
<TextBlock VerticalAlignment="Center" Text="{Binding DataError}" Foreground="Red"></TextBlock>
关于校验规则,有以下几点需要注意:
默认情况下,认为Source的数据总是正确的,规则校验只有当Target更新时,才会生效,因为Target多为用户输入;如果当Source更新时也要生效,则需要配置ValidatesOnTargetUpdated为true。
默认情况下,校验规则返回的错误信息,是不会输出到UI的,如果要输出到UI,需要设置NotifyOnValidationError属性为true。

3. 输出校验信息
默认情况下,校验信息不会输出到UI,只是显示用户控件为红色边框,如果要输出校验信息,除了设置NotifyOnValidationError属性外,还要订阅Validation.Error事件,并在事件中处理要显示的错误信息,如下所示:
private void tbAge_Error(object sender, ValidationErrorEventArgs e)
{
  this.viewModel.ValidationErrorCommand.Execute(e);
}
其中上述方法中的命令为ViewModel中定义,如下所示:
public class MainWindowViewModel : ObservableObject
{
  private int age;
  public int Age
  {
    get { return age; }
    set { SetProperty(ref age , value); }
  }
  private string dataError;
  public string DataError
  {
    get { return dataError; }
    set { SetProperty(ref dataError , value); }
  }
 
 
  private TextBox textBox;
  private ICommand winLoadedCommand;
  public ICommand WinLoadedCommand
  {
    get {
      if (winLoadedCommand == null)
      {
        winLoadedCommand = new RelayCommand<object>(WinLoaded);
      }
      return winLoadedCommand; }
  }
 
  private void WinLoaded(object sender)
  {
    if (sender != null)
    {
      var win = sender as MainWindow;
      this.textBox = win.tbAge;
    }
  }
  private ICommand validationErrorCommand;
  public ICommand ValidationErrorCommand
  {
    get {
      if (validationErrorCommand == null)
      {
        validationErrorCommand = new RelayCommand<object>(DisplayValidationError);
      }
      return validationErrorCommand; }
  }
 
  private void DisplayValidationError(object obj)
  {
    if (Validation.GetErrors(this.textBox).Count > 0)
    {
      this.DataError= Validation.GetErrors(this.textBox)[0].ErrorContent.ToString();
    }
    else
    {
      this.DataError = string.Empty;
    }
  }
 
}
4. 数据校验示例演示
经过上述步骤,运行程序,如下所示:

数据类型转换
在数据绑定时,如果Source端的数据类型和Target端的数据类型不一致,就需要用到数据类型转换,如:true/false与显示/隐藏之间的转换等,要实现数据转换,需要用到Binding的Converter属性。步骤如下:
1. 定义转换器
Binding的Converter属性是IValueConverter类型,所以定义转换器需要实现IValueConverter接口,如下所示:
namespace WpfApp4.DataConverter
{
    public class BoolToVisilityConverter : IValueConverter
    {
        /// <summary>
        /// Source 到 Target
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var flag = bool.Parse(value.ToString());
            if(flag)
            {
                return Visibility.Visible;
            }
            else
            {
                return Visibility.Collapsed;
            }
        }
 
        /// <summary>
        /// Target到Source
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var visibility = Visibility.Collapsed;
            if (Enum.TryParse<Visibility>(value.ToString(), out visibility))
            {
                if (visibility == Visibility.Collapsed)
                {
                    return false;
                }
                else if(visibility == Visibility.Visible)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
 
            return false;
        }
    }
}
注意:IValueConverter接口共两个方法,其中Convert方法当Source到Target时调用,ConvertBack方法则是反方向调用。

2. 定义资源
将类型转换器定义为一种资源,要声明资源,首先引入命名空间xmlns:c="clr-namespace:WpfApp4.DataConverter",如下所示:
<Window.Resources>
  <c:BoolToVisilityConverter x:Key="boolToVisility"></c:BoolToVisilityConverter>
</Window.Resources>
3. 调用类型转换
在Binding时,调用资源Visibility="{Binding ElementName=chk01, Path=IsChecked, Converter={StaticResource boolToVisility}}"。通过复选框的选择与否,控制控件的显示与隐藏,如下所示:
<StackPanel Orientation="Horizontal" Grid.Row="0">
  <CheckBox Content="显示" x:Name="chk01"></CheckBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Row="1" Visibility="{Binding ElementName=chk01, Path=IsChecked, Converter={StaticResource boolToVisility}}">
  <TextBlock Text="年龄" VerticalAlignment="Center"></TextBlock>
  <TextBox x:Name="tbAge" Width="120" Height="25" Margin="10" Validation.Error="tbAge_Error" VerticalContentAlignment="Center">
    <TextBox.Text>
      <Binding Path="Age" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
        <Binding.ValidationRules>
          <v:RangeValidationRule ValidatesOnTargetUpdated="True"></v:RangeValidationRule>
        </Binding.ValidationRules>
      </Binding>
    </TextBox.Text>
  </TextBox>
  <TextBlock VerticalAlignment="Center" Text="{Binding DataError}" Foreground="Red"></TextBlock>
</StackPanel>

示例演示


用户评论