WPF Toolkit 的 DataGrid 中的验证
描述在 WPF 工具包的 DataGrid 中呈现数据时的验证。
引言
本文介绍了一种验证数据的方法,该方法在 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(工作名称)
应该验证两件事
- 名字和姓氏应该只包含 A-Za-z 字符和空格。
- 不应该允许一个人设置
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 日:更正了对象级验证。