将列的
EditingElementStyle
属性设置为适合列的编辑控件的样式。 由于编辑控件在运行时创建,因此不能像对简单控件一样使用
Validation.ErrorTemplate
附加属性。
以下示例添加了使用验证规则的三列共享的错误样式,从而更新了上一个示例。 当用户输入无效值时,样式将更改单元格背景色并添加工具提示。 请注意,使用触发器来确定是否存在验证错误。 此步骤是必需的,因为当前没有针对单元格的专用错误模板。
<DataGrid.Resources>
<Style x:Key="errorStyle" TargetType="{x:Type TextBox}">
<Setter Property="Padding" Value="-2"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Course Name"
Binding="{Binding Name, TargetNullValue=(enter a course name)}"/>
<DataGridTextColumn Header="Course ID"
EditingElementStyle="{StaticResource errorStyle}"
Binding="{Binding Id, ValidatesOnExceptions=True}"/>
<DataGridTextColumn Header="Start Date"
EditingElementStyle="{StaticResource errorStyle}"
Binding="{Binding StartDate, ValidatesOnExceptions=True,
StringFormat=d}"/>
<DataGridTextColumn Header="End Date"
EditingElementStyle="{StaticResource errorStyle}"
Binding="{Binding EndDate, ValidatesOnExceptions=True,
StringFormat=d}"/>
</DataGrid.Columns>
可以通过替换列使用的 CellStyle 来实现更广泛的自定义。
验证单个行中的多个值
实现一个检查绑定数据对象的多个属性的 ValidationRule 子类。 在 Validate 方法实现中,将 value
参数值强制转换为 BindingGroup 实例。 然后,可以通过 Items 属性访问数据对象。
以下示例演示了验证 Course
对象的 StartDate
属性值是否早于其 EndDate
属性值的过程。
public class CourseValidationRule : ValidationRule
public override ValidationResult Validate(object value,
System.Globalization.CultureInfo cultureInfo)
Course course = (value as BindingGroup).Items[0] as Course;
if (course.StartDate > course.EndDate)
return new ValidationResult(false,
"Start Date must be earlier than End Date.");
return ValidationResult.ValidResult;
Public Class CourseValidationRule
Inherits ValidationRule
Public Overrides Function Validate(ByVal value As Object, _
ByVal cultureInfo As System.Globalization.CultureInfo) _
As ValidationResult
Dim course As Course = _
CType(CType(value, BindingGroup).Items(0), Course)
If course.StartDate > course.EndDate Then
Return New ValidationResult(False, _
"Start Date must be earlier than End Date.")
Return ValidationResult.ValidResult
End If
End Function
End Class
将验证规则添加到 DataGrid.RowValidationRules 集合。 可以通过 RowValidationRules 属性直接访问 BindingGroup 实例的 ValidationRules 属性,该实例对控件使用的所有绑定进行分组。
以下示例在 XAML 中设置 RowValidationRules 属性。 ValidationStep 属性设置为 UpdatedValue,以便仅在更新绑定数据对象后进行验证。
<DataGrid.RowValidationRules>
<local:CourseValidationRule ValidationStep="UpdatedValue"/>
</DataGrid.RowValidationRules>
若用户指定的结束日期早于开始日期,行标题中将显示一个红色感叹号 (!)。 可以更改此默认验证反馈,如以下过程中所述。
自定义行验证反馈
设置 DataGrid.RowValidationErrorTemplate 属性。 使用此属性可以自定义各个 DataGrid 控件的行验证反馈。 还可以使用隐式行样式设置 DataGridRow.ValidationErrorTemplate 属性来影响多个控件。
以下示例将默认行验证反馈替换为更明显的标记。 若用户输入无效值,行标题中将显示带有白色感叹号的红色圆圈。 行和单元格验证错误时都将发生这种情况。 关联的错误消息将显示在工具提示中。
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
<Grid Margin="0,-2,0,-2"
ToolTip="{Binding RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type DataGridRow}},
Path=(Validation.Errors)[0].ErrorContent}">
<Ellipse StrokeThickness="0" Fill="Red"
Width="{TemplateBinding FontSize}"
Height="{TemplateBinding FontSize}" />
<TextBlock Text="!" FontSize="{TemplateBinding FontSize}"
FontWeight="Bold" Foreground="White"
HorizontalAlignment="Center" />
</Grid>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>
以下示例完整演示了单元格验证和行验证。 Course
类提供了示例数据对象,以实现 IEditableObject 来支持事务。 DataGrid 控件可与 IEditableObject 交互,使用户可以通过按 ESC 来还原更改。
如果使用的是 Visual Basic,则在 MainWindow.xaml 的第一行中,将 x:Class="DataGridValidation.MainWindow"
替换为 x:Class="MainWindow"
。
若要测试验证,请尝试以下操作:
在“课程 ID”列中输入一个非整数值。
在“结束日期”列中输入一个早于开始日期的日期。
删除“课程 ID”、“开始日期”或“结束日期”中的值。
若要撤消无效的单元格值,请将光标放回到单元格中,然后按 ESC 键。
若要在当前单元格处于编辑模式时撤消整个行的更改,请按 ESC 键两次。
发生验证错误时,将鼠标指针移到行标题中的标记上,以查看关联的错误消息。
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace DataGridValidation
public partial class MainWindow : Window
public MainWindow()
InitializeComponent();
dataGrid1.InitializingNewItem += (sender, e) =>
Course newCourse = e.NewItem as Course;
newCourse.StartDate = newCourse.EndDate = DateTime.Today;
public class Courses : ObservableCollection<Course>
public Courses()
this.Add(new Course
Name = "Learning WPF",
Id = 1001,
StartDate = new DateTime(2010, 1, 11),
EndDate = new DateTime(2010, 1, 22)
this.Add(new Course
Name = "Learning Silverlight",
Id = 1002,
StartDate = new DateTime(2010, 1, 25),
EndDate = new DateTime(2010, 2, 5)
this.Add(new Course
Name = "Learning Expression Blend",
Id = 1003,
StartDate = new DateTime(2010, 2, 8),
EndDate = new DateTime(2010, 2, 19)
this.Add(new Course
Name = "Learning LINQ",
Id = 1004,
StartDate = new DateTime(2010, 2, 22),
EndDate = new DateTime(2010, 3, 5)
public class Course : IEditableObject, INotifyPropertyChanged
private string _name;
public string Name
return _name;
if (_name == value) return;
_name = value;
OnPropertyChanged("Name");
private int _number;
public int Id
return _number;
if (_number == value) return;
_number = value;
OnPropertyChanged("Id");
private DateTime _startDate;
public DateTime StartDate
return _startDate;
if (_startDate == value) return;
_startDate = value;
OnPropertyChanged("StartDate");
private DateTime _endDate;
public DateTime EndDate
return _endDate;
if (_endDate == value) return;
_endDate = value;
OnPropertyChanged("EndDate");
#region IEditableObject
private Course backupCopy;
private bool inEdit;
public void BeginEdit()
if (inEdit) return;
inEdit = true;
backupCopy = this.MemberwiseClone() as Course;
public void CancelEdit()
if (!inEdit) return;
inEdit = false;
this.Name = backupCopy.Name;
this.Id = backupCopy.Id;
this.StartDate = backupCopy.StartDate;
this.EndDate = backupCopy.EndDate;
public void EndEdit()
if (!inEdit) return;
inEdit = false;
backupCopy = null;
#endregion
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
#endregion
public class CourseValidationRule : ValidationRule
public override ValidationResult Validate(object value,
System.Globalization.CultureInfo cultureInfo)
Course course = (value as BindingGroup).Items[0] as Course;
if (course.StartDate > course.EndDate)
return new ValidationResult(false,
"Start Date must be earlier than End Date.");
return ValidationResult.ValidResult;
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Public Class MainWindow
Private Sub dataGrid1_InitializingNewItem(ByVal sender As System.Object, _
ByVal e As System.Windows.Controls.InitializingNewItemEventArgs) _
Handles dataGrid1.InitializingNewItem
Dim newCourse As Course = CType(e.NewItem, Course)
newCourse.StartDate = DateTime.Today
newCourse.EndDate = DateTime.Today
End Sub
End Class
Public Class Courses
Inherits ObservableCollection(Of Course)
Sub New()
Me.Add(New Course With { _
.Name = "Learning WPF", _
.Id = 1001, _
.StartDate = New DateTime(2010, 1, 11), _
.EndDate = New DateTime(2010, 1, 22) _
Me.Add(New Course With { _
.Name = "Learning Silverlight", _
.Id = 1002, _
.StartDate = New DateTime(2010, 1, 25), _
.EndDate = New DateTime(2010, 2, 5) _
Me.Add(New Course With { _
.Name = "Learning Expression Blend", _
.Id = 1003, _
.StartDate = New DateTime(2010, 2, 8), _
.EndDate = New DateTime(2010, 2, 19) _
Me.Add(New Course With { _
.Name = "Learning LINQ", _
.Id = 1004, _
.StartDate = New DateTime(2010, 2, 22), _
.EndDate = New DateTime(2010, 3, 5) _
End Sub
End Class
Public Class Course
Implements IEditableObject, INotifyPropertyChanged
Private _name As String
Public Property Name As String
Return _name
End Get
Set(ByVal value As String)
If _name = value Then Return
_name = value
OnPropertyChanged("Name")
End Set
End Property
Private _number As Integer
Public Property Id As Integer
Return _number
End Get
Set(ByVal value As Integer)
If _number = value Then Return
_number = value
OnPropertyChanged("Id")
End Set
End Property
Private _startDate As DateTime
Public Property StartDate As DateTime
Return _startDate
End Get
Set(ByVal value As DateTime)
If _startDate = value Then Return
_startDate = value
OnPropertyChanged("StartDate")
End Set
End Property
Private _endDate As DateTime
Public Property EndDate As DateTime
Return _endDate
End Get
Set(ByVal value As DateTime)
If _endDate = value Then Return
_endDate = value
OnPropertyChanged("EndDate")
End Set
End Property
#Region "IEditableObject"
Private backupCopy As Course
Private inEdit As Boolean
Public Sub BeginEdit() Implements IEditableObject.BeginEdit
If inEdit Then Return
inEdit = True
backupCopy = CType(Me.MemberwiseClone(), Course)
End Sub
Public Sub CancelEdit() Implements IEditableObject.CancelEdit
If Not inEdit Then Return
inEdit = False
Me.Name = backupCopy.Name
Me.Id = backupCopy.Id
Me.StartDate = backupCopy.StartDate
Me.EndDate = backupCopy.EndDate
End Sub
Public Sub EndEdit() Implements IEditableObject.EndEdit
If Not inEdit Then Return
inEdit = False
backupCopy = Nothing
End Sub
#End Region
#Region "INotifyPropertyChanged"
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, _
New PropertyChangedEventArgs(propertyName))
End Sub
#End Region
End Class
Public Class CourseValidationRule
Inherits ValidationRule
Public Overrides Function Validate(ByVal value As Object, _
ByVal cultureInfo As System.Globalization.CultureInfo) _
As ValidationResult
Dim course As Course = _
CType(CType(value, BindingGroup).Items(0), Course)
If course.StartDate > course.EndDate Then
Return New ValidationResult(False, _
"Start Date must be earlier than End Date.")
Return ValidationResult.ValidResult
End If
End Function
End Class
<Window x:Class="DataGridValidation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataGridValidation"
Title="DataGrid Validation Example" Height="240" Width="600">
<Grid.Resources>
<local:Courses x:Key="courses"/>
</Grid.Resources>
<DataGrid Name="dataGrid1" FontSize="20" RowHeaderWidth="27"
ItemsSource="{StaticResource courses}"
AutoGenerateColumns="False">
<DataGrid.Resources>
<Style x:Key="errorStyle" TargetType="{x:Type TextBox}">
<Setter Property="Padding" Value="-2"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Course Name"
Binding="{Binding Name, TargetNullValue=(enter a course name)}"/>
<DataGridTextColumn Header="Course ID"
EditingElementStyle="{StaticResource errorStyle}"
Binding="{Binding Id, ValidatesOnExceptions=True}"/>
<DataGridTextColumn Header="Start Date"
EditingElementStyle="{StaticResource errorStyle}"
Binding="{Binding StartDate, ValidatesOnExceptions=True,
StringFormat=d}"/>
<DataGridTextColumn Header="End Date"
EditingElementStyle="{StaticResource errorStyle}"
Binding="{Binding EndDate, ValidatesOnExceptions=True,
StringFormat=d}"/>
</DataGrid.Columns>
<DataGrid.RowValidationRules>
<local:CourseValidationRule ValidationStep="UpdatedValue"/>
</DataGrid.RowValidationRules>
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
<Grid Margin="0,-2,0,-2"
ToolTip="{Binding RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type DataGridRow}},
Path=(Validation.Errors)[0].ErrorContent}">
<Ellipse StrokeThickness="0" Fill="Red"
Width="{TemplateBinding FontSize}"
Height="{TemplateBinding FontSize}" />
<TextBlock Text="!" FontSize="{TemplateBinding FontSize}"
FontWeight="Bold" Foreground="White"
HorizontalAlignment="Center" />
</Grid>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>
</DataGrid>
</Grid>
</Window>
DataGrid
DataGrid
实现绑定验证
在自定义对象上实现验证逻辑