1. 程式人生 > >如何:用 DataGrid 控制元件實現驗證

如何:用 DataGrid 控制元件實現驗證

利用 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