DataGrid 컨트롤을 사용하여 셀 수준과 행 수준 모두에서 유효성 검사를 수행할 수 있습니다. 셀 수준 유효성 검사에서는 사용자가 값을 업데이트할 때 바인딩된 데이터 개체에 대한 개별 속성의 유효성을 검사합니다. 행 수준 유효성 검사에서는 사용자가 행에 대한 변경 내용을 커밋할 때 전체 데이터 개체의 유효성을 검사합니다. 유효성 검사 오류에 대한 사용자 지정 시각적 피드백을 제공하거나, DataGrid 컨트롤에서 제공하는 기본 시각적 피드백을 사용할 수 있습니다.
다음 절차에서는 DataGrid 바인딩에 유효성 검사 규칙을 적용하고 시각적 피드백을 사용자 지정하는 방법에 대해 설명합니다.
개별 셀 값의 유효성을 검사하려면
열과 함께 사용되는 바인딩에 하나 이상의 유효성 검사 규칙을 지정합니다. 이 방법은 데이터 바인딩 개요에 설명된 대로 간단한 컨트롤에서 데이터의 유효성을 검사하는 방법과 비슷합니다.
다음 예제에서는 네 개의 열이 비즈니스 개체의 각기 다른 속성에 바인딩된 DataGrid 컨트롤을 보여 줍니다. 이 중 세 개의 열에서는 ValidatesOnExceptions 속성을 true로 설정하여 ExceptionValidationRule을 지정합니다.
<Grid> <Grid.Resources> <local:Courses x:Key="courses"/> </Grid.Resources> <DataGrid Name="dataGrid1" FontSize="20" ItemsSource="{StaticResource courses}" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Header="Course Name" Binding="{Binding Name, TargetNullValue=(enter a course name)}"/> <DataGridTextColumn Header="Course ID" Binding="{Binding Id, ValidatesOnExceptions=True}"/> <DataGridTextColumn Header="Start Date" Binding="{Binding StartDate, ValidatesOnExceptions=True, StringFormat=d}"/> <DataGridTextColumn Header="End Date" Binding="{Binding EndDate, ValidatesOnExceptions=True, StringFormat=d}"/> </DataGrid.Columns> </DataGrid> </Grid>
사용자가 Course ID 열에 정수가 아닌 값을 입력하는 등 올바르지 않은 값을 입력하면 셀 주위에 빨간색 테두리가 나타납니다. 다음 절차에 설명된 대로 이 기본 유효성 검사 피드백을 변경할 수 있습니다.
셀 유효성 검사 피드백을 사용자 지정하려면
열의 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 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.") Else Return ValidationResult.ValidResult End If End Function End Class
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."); } else { return ValidationResult.ValidResult; } } }
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"로 바꿉니다. |
유효성 검사를 테스트하려면 다음을 시도합니다.
Course ID 열에서 정수가 아닌 값을 입력합니다.
End Date 열에서 시작 날짜보다 이전인 날짜를 입력합니다.
Course ID, Start Date 또는 End Date에서 값을 삭제합니다.
올바르지 않은 셀 값을 취소하려면 커서를 다시 셀에 놓고 Esc 키를 누릅니다.
현재 셀이 편집 모드에 있을 때 전체 행의 변경 내용을 취소하려면 Esc 키를 두 번 누릅니다.
유효성 검사 오류가 발생하면 마우스 포인터를 행 머리글의 표시기 위로 이동하여 연결된 오류 메시지를 확인합니다.
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
Get
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
Get
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
Get
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
Get
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.")
Else
Return ValidationResult.ValidResult
End If
End Function
End Class
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
{
get
{
return _name;
}
set
{
if (_name == value) return;
_name = value;
OnPropertyChanged("Name");
}
}
private int _number;
public int Id
{
get
{
return _number;
}
set
{
if (_number == value) return;
_number = value;
OnPropertyChanged("Id");
}
}
private DateTime _startDate;
public DateTime StartDate
{
get
{
return _startDate;
}
set
{
if (_startDate == value) return;
_startDate = value;
OnPropertyChanged("StartDate");
}
}
private DateTime _endDate;
public DateTime EndDate
{
get
{
return _endDate;
}
set
{
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.");
}
else
{
return ValidationResult.ValidResult;
}
}
}
}
<Window x:Class="DataGridValidation.MainWindow"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataGridValidation"
Title="DataGrid Validation Example" Height="240" Width="600">
<Grid>
<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>