방법: DataGrid 컨트롤을 사용하여 유효성 검사 구현
이 DataGrid 컨트롤을 사용하면 셀과 행 수준 모두에서 유효성 검사를 수행할 수 있습니다. 셀 수준 유효성 검사를 사용하면 사용자가 값을 업데이트할 때 바인딩된 데이터 개체의 개별 속성 유효성을 검사합니다. 행 수준 유효성 검사를 사용하면 사용자가 행에 변경 사항을 커밋할 때 전체 데이터 개체의 유효성을 검사합니다. 유효성 검사 오류에 대해 사용자 지정된 시각적 피드백을 제공하거나 DataGrid 컨트롤에서 제공하는 기본 시각적 피드백을 사용할 수도 있습니다.
다음 절차에서는 DataGrid 바인딩에 유효성 검사 규칙을 적용하고 시각적 피드백을 사용자 지정하는 방법을 설명합니다.
개별 셀 값의 유효성 검사
열과 함께 사용되는 바인딩에 유효성 검사 규칙을 하나 이상 지정합니다. 이는 데이터 바인딩 개요의 설명대로 단순한 컨트롤에서 데이터 유효성을 검사하는 것과 유사합니다.
다음 예제에서는 비즈니스 개체의 다른 속성에 바인딩된 열이 4개 있는 DataGrid 컨트롤을 보여줍니다. 열 3개는 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>
사용자가 잘못된 값(예: 과정 ID 열의 정수가 아닌 값)을 입력하면 셀 주위에 빨간색 테두리가 나타납니다. 다음 절차의 설명대로 이 기본 유효성 검사 피드백을 변경할 수 있습니다.
셀 유효성 검사 피드백 사용자 지정
열의 EditingElementStyle 속성을 열의 편집 컨트롤에 적합한 스타일로 설정합니다. 편집 컨트롤은 런타임에 생성되므로 단순한 컨트롤을 사용하는 경우와 같이 Validation.ErrorTemplate 연결된 속성을 사용할 수 없습니다.
다음 예제에서는 유효성 검사 규칙을 사용하여 열 3개에서 공유하는 오류 스타일을 추가해 앞선 예를 업데이트합니다. 사용자가 잘못된 값을 입력하면 스타일이 셀 배경색을 변경하고 도구 설명을 추가합니다. 트리거를 사용하여 유효성 검사 오류가 있는지 여부를 확인합니다. 현재 셀에 대한 전용 오류 템플릿이 없으므로 필요합니다.
<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."); } else { 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.") Else 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
{
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;
}
}
}
}
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
<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>
<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>
참고 항목
.NET Desktop feedback