共用方式為


如何:使用 DataGrid 控制項實作驗證

控制項 DataGrid 可讓您在資料格和資料列層級上執行驗證。 使用資料格層級驗證,您可以在使用者更新值時驗證系結資料物件的個別屬性。 使用資料列層級驗證時,當使用者認可資料列的變更時,您可以驗證整個資料物件。 您也可以為驗證錯誤提供自訂的視覺回饋,或使用控制項提供的預設視覺回饋 DataGrid

下列程式說明如何將驗證規則套用至 DataGrid 系結並自訂視覺回饋。

驗證個別儲存格值

  • 在與資料行搭配使用的系結上指定一或多個驗證規則。 這類似于驗證簡單控制項中的資料,如資料系結概觀 中所述

    下列範例顯示一個控制項,其中四個 DataGrid 資料行系結至商務物件的不同屬性。 其中三個數據行藉由將 屬性設定為 trueValidatesOnExceptions 指定 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>
    

    當使用者輸入不正確值時(例如課程識別碼資料行中的非整數),儲存格周圍會出現紅色框線。 您可以變更此預設驗證意見反應,如下列程式所述。

自訂儲存格驗證意見反應

  • 將資料行的屬性 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 實作更廣泛的自訂。

驗證單一資料列中的多個值

  1. 實作子 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
    
  2. 將驗證規則新增至 DataGrid.RowValidationRules 集合。 屬性 RowValidationRules 可讓您直接存取 ValidationRules 實例的 BindingGroup 屬性,以將 控制項使用的所有系結分組。

    下列範例會在 RowValidationRules XAML 中設定 屬性。 屬性 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"

若要測試驗證,請嘗試下列動作:

  • 在 [課程識別碼] 資料行中,輸入非整數值。

  • 在 [結束日期] 資料行中,輸入早于開始日期的日期。

  • 刪除課程識別碼、開始日期或結束日期中的值。

  • 若要復原不正確儲存格值,請將游標放回儲存格中,然後按 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>

另請參閱