Freigeben über


Gewusst wie: Implementieren von Validierung mit dem DataGrid-Steuerelement

Das DataGrid-Steuerelement ermöglicht eine Validierung auf Zellen- sowie auf Zeilenebene. Bei der Validierung auf Zellenebene werden einzelne Eigenschaften eines gebundenen Datenobjekts überprüft, wenn ein Benutzer einen Wert aktualisiert. Bei der Validierung auf Zeilenebene werden ganze Datenobjekte überprüft, wenn ein Benutzer einen Commit für Änderungen an einer Zeile ausführt. Sie können auch angepasstes visuelles Feedback für Validierungsfehler bereitstellen oder das standardmäßige visuelle Feedback des DataGrid-Steuerelements verwenden.

In den folgenden Prozeduren wird das Anwenden von Validierungsregeln auf DataGrid-Bindungen sowie das Anpassen des visuellen Feedbacks erläutert.

So überprüfen Sie einzelne Zellenwerte

  • Geben Sie für die Bindung einer Spalte mindestens eine Validierungsregel an. Dies ist vergleichbar mit dem Überprüfen von Daten in einfachen Steuerelementen (siehe Übersicht über Datenbindung).

    Im folgenden Beispiel wird ein DataGrid-Steuerelement mit vier Spalten veranschaulicht, die an unterschiedliche Eigenschaften eines Geschäftsobjekts gebunden sind. In drei der Spalten wird die ExceptionValidationRule angegeben, indem die ValidatesOnExceptions-Eigenschaft auf true festgelegt wird.

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

    Gibt ein Benutzer nun einen ungültigen Wert ein (beispielsweise einen nicht ganzzahligen Wert in der Spalte für die Kurs-ID), wird die Zelle mit einem roten Rahmen versehen. Dieses Standardvalidierungsfeedback kann wie in der folgenden Prozedur beschrieben geändert werden.

So passen Sie das Zellenvalidierungsfeedback an

  • Legen Sie die EditingElementStyle-Eigenschaft der Spalte auf einen Stil fest, der für das Bearbeitungssteuerelement der Spalte geeignet ist. Da die Bearbeitungssteuerelemente zur Laufzeit erstellt werden, kann die angefügte Validation.ErrorTemplate-Eigenschaft nicht wie sonst bei einfachen Steuerelementen verwendet werden.

    Im folgenden Beispiel wird das vorherige Beispiel aktualisiert. Hierzu wird ein gemeinsamer Fehlerstil für die drei Spalten mit Validierungsregeln hinzugefügt. Gibt ein Benutzer nun einen ungültigen Wert ein, ändert sich die Hintergrundfarbe der Zelle, und eine QuickInfo wird hinzugefügt. Beachten Sie, dass hier ein Trigger verwendet wird, um zu bestimmen, ob ein Validierungsfehler vorliegt. Dies ist erforderlich, da momentan keine dedizierte Fehlervorlage für Zellen vorhanden ist.

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

    Ersetzen Sie zum Implementieren umfangreicherer Anpassungen den von der Spalte verwendeten CellStyle.

So überprüfen Sie mehrere Werte in einer einzelnen Zeile

  1. Implementieren Sie eine ValidationRule-Unterklasse, von der mehrere Eigenschaften des gebundenen Datenobjekts überprüft werden. Wandeln Sie in der Implementierung der Validate-Methode den value-Parameterwert zu einer BindingGroup-Instanz um. Anschließend können Sie über die Items-Eigenschaft auf das Datenobjekt zugreifen.

    Im folgenden Beispiel wird dieser Prozess veranschaulicht, indem überprüft wird, ob der StartDate-Eigenschaftswert für ein Course-Objekt vor dem EndDate-Eigenschaftswert des Objekts liegt.

    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;
            }
        }
    }
    
  2. Fügen Sie die Validierungsregel der DataGrid.RowValidationRules-Auflistung hinzu. Die RowValidationRules-Eigenschaft bietet direkten Zugriff auf die ValidationRules-Eigenschaft einer BindingGroup-Instanz, von der alle von diesem Steuerelement verwendeten Bindungen gruppiert werden.

    Im folgenden Beispiel wird die RowValidationRules-Eigenschaft in XAML festgelegt. Die ValidationStep-Eigenschaft wird auf UpdatedValue festgelegt, damit die Validierung nur nach der Aktualisierung des gebundenen Datenobjekts erfolgt.

    <DataGrid.RowValidationRules>
      <local:CourseValidationRule ValidationStep="UpdatedValue"/>
    </DataGrid.RowValidationRules>
    

    Wird von einem Benutzer ein Enddatum angegeben, das vor dem Startdatum liegt, wird im Zeilenkopf ein rotes Ausrufezeichen angezeigt. Dieses Standardvalidierungsfeedback kann wie in der folgenden Prozedur beschrieben geändert werden.

So passen Sie das Zeilenvalidierungsfeedback an

  • Legen Sie die DataGrid.RowValidationErrorTemplate-Eigenschaft fest. Diese Eigenschaft ermöglicht das Anpassen des Zeilenvalidierungsfeedbacks für einzelne DataGrid-Steuerelemente. Sie können auch mehrere Steuerelemente anpassen, indem Sie die DataGridRow.ValidationErrorTemplate-Eigenschaft mithilfe eines impliziten Zeilenstils festlegen.

    Im folgenden Beispiel wird das standardmäßige Zeilenvalidierungsfeedback durch einen besser sichtbaren Indikator ersetzt. Gibt ein Benutzer einen ungültigen Wert ein, wird im Zeilenheader ein roter Kreis mit einem weißen Ausrufezeichen angezeigt. Dies gilt sowohl für Zeilen- als auch für Zellenvalidierungsfehler. Die entsprechende Fehlermeldung wird in einer QuickInfo angezeigt.

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

Beispiel

Beim folgenden Beispiel handelt es sich um eine vollständige Demonstration der Zellen- und Zeilenvalidierung. Von der Course-Klasse wird ein Beispieldatenobjekt bereitgestellt, von dem IEditableObject implementiert wird, um Transaktionen zu unterstützen. Das DataGrid-Steuerelement interagiert mit IEditableObject, um den Benutzern das Rückgängigmachen von Änderungen durch Drücken von ESC zu ermöglichen.

HinweisHinweis

Ersetzen Sie bei Verwendung von Visual Basic in der ersten Zeile von "MainWindow.xaml" den Code x:Class="DataGridValidation.MainWindow" durch x:Class="MainWindow".

Gehen Sie zum Testen der Validierung folgendermaßen vor:

  • Geben Sie in der Spalte für die Kurs-ID einen nicht ganzzahligen Wert ein.

  • Geben Sie ein Enddatum ein, das vor dem Startdatum liegt.

  • Löschen Sie den Wert für Kurs-ID, Startdatum oder Enddatum.

  • Platzieren Sie zum Rückgängigmachen eines ungültigen Zellenwerts den Cursor in der entsprechenden Zelle, und drücken Sie ESC.

  • Drücken Sie zweimal ESC, während sich die aktuelle Zelle im Bearbeitungsmodus befindet, um die Änderungen für eine ganze Zeile rückgängig zu machen.

  • Bewegen Sie bei Auftreten eines Validierungsfehlers den Mauszeiger über den Indikator im Zeilenheader, um die entsprechende Fehlermeldung anzuzeigen.

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>

Siehe auch

Aufgaben

Gewusst wie: Implementieren der Bindungsvalidierung

Gewusst wie: Implementieren von Validierungslogik für benutzerdefinierte Objekte

Referenz

DataGrid

Weitere Ressourcen

DataGrid

Datenbindung (WPF)