如何:用 DataGrid 控制元件實現驗證
阿新 • • 發佈:2018-12-12
利用 DataGrid 控制元件,您既可以在單元格級別也可以在行級別執行驗證。 通過單元格級別驗證,將可在使用者更新值時驗證繫結資料物件的個別屬性。 通過行級別驗證,將可在使用者提交對行的更改時驗證整個資料物件。 您也可以為驗證錯誤提供自定義的可視反饋,或使用 DataGrid 控制元件提供的預設可視反饋。 以下過程介紹如何將驗證規則應用於 DataGrid 繫結,並自定義可視反饋。 驗證個別單元格值 對用於列的繫結指定一條或多條驗證規則。 這與在簡單控制元件中驗證資料類似,如資料繫結概述中所述。 下面的示例顯示一個 DataGrid 控制元件,其中包含繫結到業務物件的不同屬性的四個列。 其中三個列通過將 ValidatesOnExceptions 屬性設定為 true 來指定ExceptionValidationRule。 XAML <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”(課程 ID)列中輸入非整數時,單元格周圍將出現一個紅色邊框。 可以按照下面過程中所述的方式更改此預設驗證反饋。 自定義單元格驗證反饋 將列的 EditingElementStyle 屬性設定為適合於列的編輯控制元件的樣式。 由於編輯控制元件是在執行時建立的,因此您無法像處理簡單控制元件一樣使用Validation.ErrorTemplate 附加屬性。 下面的示例通過新增由具有驗證規則的三個列共享的錯誤樣式來更新前面的示例。 當用戶輸入無效的值時,樣式會更改單元格背景顏色,並新增一個工具提示。 請注意使用觸發器來確定是否存在驗證錯誤的做法。 這是必要的,因為當前沒有專門用於單元格的錯誤模板。 XAML <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 屬性值。 C# VB 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,以使驗證只會在更新了繫結資料物件之後進行。 XAML <DataGrid.RowValidationRules> <local:CourseValidationRule ValidationStep="UpdatedValue"/> </DataGrid.RowValidationRules> 當用戶指定早於開始日期的結束日期時,行標題中將出現一個紅色感嘆號 (!)。 可以按照下面過程中所述的方式更改此預設驗證反饋。 自定義行驗證反饋 設定 DataGrid.RowValidationErrorTemplate 屬性。 利用此屬性,您可以為個別 DataGrid 控制元件自定義行驗證反饋。 通過使用隱式行樣式來設定DataGridRow.ValidationErrorTemplate 屬性,您也可以影響多個控制元件。 下面的示例將預設行驗證反饋替換為更直觀的指示符。 當用戶輸入無效的值時,行標題中將出現一個帶有白色感嘆號的紅色圓圈。 對於行驗證錯誤和單元格驗證錯誤,都將發生此情況。 關聯的錯誤訊息顯示在工具提示中。 XAML <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”(課程 ID)列中,輸入一個非整數值。 在“End Date”(結束日期)列中,輸入一個早於開始日期的日期。 刪除“Course ID”(課程 ID)、“Start Date”(開始日期)或“End Date”(結束日期)中的值。 若要撤消無效的單元格值,請將游標重新放在單元格中,並按 Esc 鍵。 若要在當前單元格處於編輯模式中時撤消整行的更改,請按 Esc 鍵兩次。 發生驗證錯誤時,將滑鼠指標移到行標題中的指示符上即可看到關聯的錯誤訊息。 VB 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 C# 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; } } } } XAML <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> --------------------- 本文來自 metal1 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/metal1/article/details/41212103?utm_source=copy