65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF Toolkit 的 DataGrid 中的验证

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2011年10月23日

CPOL

2分钟阅读

viewsIcon

62484

downloadIcon

3174

描述在 WPF 工具包的 DataGrid 中呈现数据时的验证。

screenshot.jpg

引言

本文介绍了一种验证数据的方法,该方法在 CodePlex WPF 工具包的 DataGrid 控件 (http://wpf.codeplex.com/) 中展示。验证使用 IDataError 接口完成,并在 .NET Framework 3.5 及以上版本中运行。

背景

最近,我需要在网格控件中验证数据。 我不想购买第三方组件,但决定使用 WPF 工具包的 DataGrid

在阅读了 Colin Eberhardt 撰写的优秀文章 (https://codeproject.org.cn/KB/WPF/WPFDataGridExamples.aspx) 之后,我意识到在使用 .NET Framework 3.5 时遇到了一些问题,尤其是在程序化添加数据而不是用户更改数据时。

我不得不实现一个可以通过解压缩 Zip 文件部署的小工具。 因此,我不希望用户为了解决我的验证问题而需要安装 .NET Framework 4.0。

最后,我按照下面描述的方式进行了数据验证,该方法适用于 .NET Framework 3.5。

Using the Code

该应用程序使用 MVVM 模式。 如果您还没有听说过它,我建议您阅读 Josh Smith 的优秀文章 (http://joshsmithonwpf.wordpress.com/)。

进行验证的类称为 <PersonVM>。这个类包含以下属性

  • 字符串 FirstName(名)
  • 字符串 LastName(姓)
  • 布尔值 HasJob (指示这个人是否有工作或失业)
  • 字符串 JobName(工作名称)

应该验证两件事

  1. 名字和姓氏应该只包含 A-Za-z 字符和空格。
  2. 不应该允许一个人设置 HasJob 标志,但 JobName 为空,反之亦然。

第一个规则可以通过验证每个名称属性来检查。 要检查第二个规则,仅仅验证单个属性是不够的,需要验证多个属性。此验证在整个 Person 对象上进行。

验证在以下 PersonVM 索引器中实现(<IDataError> 接口所需)

public string this[string columnName]
{
  get
  {
    // apply property level validation rules
    if (columnName == "FirstName")
    {
      if (String.IsNullOrEmpty(this.FirstName))
        return "First Name needs to be filled";
        
      if (!MyNameEx.Match(this.FirstName).Success)
        return "First Name may only contain characters or spaces";
    }
    
    if (columnName == "LastName")
    {
      if (String.IsNullOrEmpty(this.LastName))
        return "Last Name needs to be filled";
        
      if (!MyNameEx.Match(this.LastName).Success)
        return "Last Name may only contain characters or spaces";
    }
    
    // Method ValidateJob applies object level validation. In this example the consistency 
    // of the properties HasJob and JobName is checked on object level. An error is 
    // announced for JobName only. Otherwise the red error border would be presented for 
    // the JobName and the HasJob field and the error correction would be inconvenient 
    // for the user - try it out by uncommenting following line
    // if (columnName == "JobName" || columnName == "HasJob")
    if (columnName == "JobName")
    {
      return ValidateJob();
    }
    
    return "";
  }
}

private string ValidateJob()
{
  if (!this.HasJob && !String.IsNullOrEmpty(this.JobName))
  {
    return "Job Name is given, but Job Flag is not set!";
  }
  if (this.HasJob && String.IsNullOrEmpty(this.JobName))
  {
    return "Job Name is not given, but Job Flag is set!";
  }
  return "";
}

IDataError 的第二种方法不是单独为 PersonVM 编写的,可以放入一个基类或辅助类中。

public string Error
{
  get
  {
    StringBuilder error = new StringBuilder();
 
    // iterate over all of the properties
    // of this object - aggregating any validation errors
    PropertyDescriptorCollection props = TypeDescriptor.GetProperties(this);
    foreach (PropertyDescriptor prop in props)
    {
      String propertyError = this[prop.Name];
      if (propertyError != string.Empty)
      {
        error.Append((error.Length != 0 ? ", " : "") + propertyError);
      }
    }
 
    return error.Length == 0 ? null : error.ToString();
  }
}

多个字段的验证需要由一个字段(在我的示例中是 JobName)来声明,因为用户不能一步编辑多个字段来更正多字段错误。 因此,当 HasJob 复选框更新时,有必要模拟 JobName 已被更改。 此通知在 HasJob 的设置器中完成

public Boolean HasJob
{
  get
  {
    return myHasJob;
  }
  set
  {
    myHasJob = value;
    NotifyPropertyChanged("JobName");
    NotifyErrorChanged();
  }
}

下面显示了 DataGrid 的相应 XAML 代码

<Window x:Class="ValidationInWpfDatagrid.View.MainWindow" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition/>
      <RowDefinition Height="20"/>
    </Grid.RowDefinitions>
    <dg:DataGrid  Name="myDataGrid" AutoGenerateColumns="False" 
    ItemsSource="{Binding PersonList}" CanUserAddRows="True">
 
      <dg:DataGrid.Resources>
        <Style TargetType="{x:Type dg:DataGridCell}">
          <Setter Property="TextBlock.ToolTip" 
                    Value="{Binding Error}" />
        </Style>
      </dg:DataGrid.Resources>
 
      <dg:DataGrid.Columns>
        <dg:DataGridTextColumn Header="FirstName" 
           Binding="{Binding Path=FirstName, ValidatesOnDataErrors=True}" 
           Width="*" />
        <dg:DataGridTextColumn Header="LastName" 
           Binding="{Binding Path=LastName, ValidatesOnDataErrors=True}" 
           Width="*" />
        <dg:DataGridCheckBoxColumn Header="Job Flag" 
           Binding="{Binding Path=HasJob, ValidatesOnDataErrors=True}" 
           Width="*" />
        <dg:DataGridTextColumn Header="Job's Name" 
           Binding="{Binding Path=JobName, ValidatesOnDataErrors=True}" 
           Width="*" />
      </dg:DataGrid.Columns>
 
    </dg:DataGrid>
    <Button Grid.Row="1" Name="myBtnAddPerson" 
       Content="Add Person" Command="{Binding AddPersonCommand}" />
  </Grid>
</Window> 

玩得开心!

历史

  • 2011 年 10 月 23 日:第一次修订。
  • 2011 年 11 月 5 日:更正了对象级验证。
© . All rights reserved.