none
WPF DataGrid - RowValidationRules Problem mit neuer Zeile und ValidationStep=CommittedValue RRS feed

  • Frage

  • Hallo,

    ich habe folgendes Problem bei der Validierung einer neuen Tabellenzeile, die in einem DataGrid angezeigt wird:

    1. Das DataGrid ist an eine CollectionViewSource bedunden, deren Source eine DefaultView einer DataTable ist
    2. Der Bediener gibt Werte in eine neue Zeile ein und setzt den Cursor dann auf eine andere Zeile
    3. Innerhalb des RowChanged - Ereignisses der Tabelle überprüfe ich die Eingabe und setze für eine Spalte mit DataRow.SetColumnError einen Fehler
    4. Da dies nicht reicht (warum auch immer?), um im DataGrid den Fehler anzuzeigen, besitzt das DataGrid eine RowValidationRule mit ValidationStep = "CommitedValue". (Wenn DataRow.HasErrors true liefert setze ich ValidationResult auf einen Fehlertext)
    5. Der Fehler wird im RowHeader nun auch angezeigt, aber leider sind alle Spalten dieser Zeile des DataGrid nicht mehr editierbar!!!

    Hat jemand eine Idee?

    Oder ist vielleicht mein Validierungskonzept völlig falsch?

    Danke für Antworten!

    Mittwoch, 14. November 2012 11:48

Alle Antworten

  • Hallo petkuh,

    Schau Dir mal an folgenden Links:

    Implementieren von Validierung mit dem DataGrid-Steuerelement.

    Data Validation

    Gruß,

    Ionut


    Donnerstag, 15. November 2012 13:21
    Moderator
  • Hallo Ionut,

    danke für Deine Antwort.

    Mein Problem ist allerdings eher die Validierung im DataGrid über die IDataErrorInfo-Schnittstelle der DataRowView! Meine Source ist eine DataTable (bzw. die default DataRowView).

    Ich möchte im DataGrid die Zelle markieren, die den Fehler enthält, also setzte ich bei der Spaltenbindung "ValidatesOnDataErrors=True".

    In meiner RowValidationRule (ich habe "ValidationStep=UpdatedValue" gesetzt) lösche ich zunächst alle Fehler über "DataRow.ClearErrors()", setze dann über "DataRow.SetColumnError()" für die Spalte einen Fehler und verlasse die ValidationRule mit "return ValidationResult(false, "Fehler");". Das funktionert auch, ich sehe im RowHeader des DataGrids meinen Fehler (über einen Style für DataGridRow.ValidationErrorTemplate), die Zelle ist rot umrandet und die Eingabe in all anderen Zeilen ist blockiert.

    Wenn ich jetzt aber einen korrekten Wert in die Zelle eingebe und die Zeile verlasse, ist die Eingabe in alle anderen Zeilen immer noch blockiert.

    Die RowValidationRule wurde durchlaufen, die Fehler zurückgesetzt, die ValidationRule wurde mit "ValidationResult.ValidResult" verlassen. Die Fehler werden jetzt auch nicht mehr visualisiert ( der rote Rahmen ist weg).

    Die Blockade der anderen Zeilen wird erst aufgehoben, wenn ich den Cursor wieder in die Zeile mit den Fehler setze und diese erneut verlasse.

    Nebenbei: Das RowChanged Ereignis der Tabelle wird áuch erst nach dem zweiten verlassen der Zeile ausgelöst.

    Ich arbeite mit .NET Framework 4.0

    Kannst Du nochmal helfen?

    Donnerstag, 15. November 2012 14:21
  • Hallo petkuh,

    Ich denke Du hast dasselbe Problem wie hier:

    WPF DataGrid Validation fails to update

    Extracting erroneous validated cell from inside DataGrid

    Ich hoffe diesen Links kann Dir weiterhelfen.

    Gruß,

    Ionut

    Donnerstag, 15. November 2012 14:34
    Moderator
  • Hallo Ionut,

    ich habe mir die Links angesehen, leider komme ich immer noch nicht klar. Eigentlich möchte ich nichts anderes als die Benutzereingabe validieren und im Fehlerfall die Zelle markieren, die den Fehler verursacht hat. Ist das vielleicht mit .Net 4.0 nicht möglich? Gibt es deshalb die INotifyDataErrorInfo Schnittstelle in .Net 4.5?

    Ich habe mal ein Beispiel angefügt. Das Fenster zeigt zwei DataGrids mit unterschiedlichen Tabellen. Im linken validiere ich in der RowValidationRule und setze DataRow.SetColumnError, im rechten validiere ich im RowChanged-Event der Tabelle.

    Nachdem ich links eine ungültige Zahl eingebe (z.B. 100), wird die Zelle korrekt markiert. Wenn ich dann eine gültige Zahl eingebe, wird die Markierung korrekt entfernt, aber die anderen Zeilen sind weiterhin blockiert (und das RowChanged-Event der Tabelle wurde auch nicht ausgelöst).

    Nachdem ich rechts  eine ungültige Zahl eingebe, wird die Zelle nicht markiert. Dies geschieht erst, wenn ich den Cursor nochmals in die Zelle setze. Gebe ich eine gültige Zahl ein, wird die Zelle markiert! Die Markierung verschwindet erst, wenn ich einmal in den EditMode der Zelle gewechselt bin.

    Eigentlich müßten doch viele vor diesem Problem stehen? Was mache ich falsch?

    Danke für Deine Mühe.

    Hier der Code:

    MainWindow.xaml:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
      <Window.Resources>
        <local:MyViewModel x:Key="myViewModel" />
        <CollectionViewSource x:Key="dataTableLeftViewSource" Source="{Binding Path=dataTableLeft, Source={StaticResource myViewModel}}" />
        <CollectionViewSource x:Key="dataTableRightViewSource" Source="{Binding Path=dataTableRight, Source={StaticResource myViewModel}}" />

        <Style x:Key="errorStyleTextBlock" TargetType="{x:Type TextBlock}">
          <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
              <Setter Property="Background" Value="Tomato"/>
              <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
          </Style.Triggers>
        </Style>

        <Style TargetType="DataGridRow">
          <Setter Property="ValidationErrorTemplate" >
            <Setter.Value>
              <ControlTemplate>
                <Grid Margin="0,-2,0,-2" Background="Red" HorizontalAlignment="Stretch"
                    ToolTip="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Path=(Validation.Errors)[0].ErrorContent}">
                  <Rectangle Fill="Red" Width="20"/>
                </Grid>
              </ControlTemplate>
            </Setter.Value>
          </Setter>
        </Style>
      </Window.Resources>
     
      <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="20">

        <DataGrid DataContext="{StaticResource dataTableLeftViewSource}" ItemsSource="{Binding}" AutoGenerateColumns="False" FontSize="14" HorizontalAlignment="Center" RowHeaderWidth="20">
          <DataGrid.RowValidationRules>
            <local:TableLeftValidationRule ValidationStep="UpdatedValue"/>
          </DataGrid.RowValidationRules>
          <DataGrid.Columns>
            <DataGridTextColumn Header="Zahl kleiner 50" Binding="{Binding Path=Zahl, ValidatesOnDataErrors=True}" ElementStyle="{StaticResource errorStyleTextBlock}" />
          </DataGrid.Columns>
        </DataGrid>

        <DataGrid DataContext="{StaticResource dataTableRightViewSource}" ItemsSource="{Binding}" AutoGenerateColumns="False" FontSize="14" HorizontalAlignment="Center" RowHeaderWidth="20" Margin="20,0,0,0">
          <DataGrid.RowValidationRules>
            <local:TableRightValidationRule ValidationStep="CommittedValue"/>
          </DataGrid.RowValidationRules>
          <DataGrid.Columns>
            <DataGridTextColumn Header="Zahl kleiner 50" Binding="{Binding Path=Zahl, ValidatesOnDataErrors=True}" ElementStyle="{StaticResource errorStyleTextBlock}" />
          </DataGrid.Columns>
        </DataGrid>
       
      </StackPanel>
    </Window>

    MainWindow.xaml.cs:

    using System;
    using System.Data;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;

    namespace WpfApplication1
    {
      /// <summary>
      /// Interaktionslogik für MainWindow.xaml
      /// </summary>
      public partial class MainWindow : Window
      {
        MyViewModel vm;

        public MainWindow()
        {
          InitializeComponent();

          vm = new MyViewModel();

          this.DataContext = vm;
        }
      }

      class MyViewModel
      {
        public DataTable dataTableLeft { get; set; }
        public DataTable dataTableRight { get; set; }

        public MyViewModel()
        {
          dataTableLeft = new DataTable();
          dataTableLeft.Columns.Add("Zahl", typeof(System.Int32));

          DataRow r;

          r = dataTableLeft.NewRow();
          r["Zahl"] = 10;
          dataTableLeft.Rows.Add(r);

          r = dataTableLeft.NewRow();
          r["Zahl"] = 20;
          dataTableLeft.Rows.Add(r);

          r = dataTableLeft.NewRow();
          r["Zahl"] = 30;
          dataTableLeft.Rows.Add(r);

          dataTableLeft.AcceptChanges();

          dataTableRight = dataTableLeft.Copy();
          dataTableRight.RowChanged += new DataRowChangeEventHandler(dataTableRight_RowChanged);


        }

        void dataTableRight_RowChanged(object sender, DataRowChangeEventArgs e)
        {
          e.Row.ClearErrors();

          if (Convert.ToInt32(e.Row["Zahl"]) > 50)
          {
            e.Row.SetColumnError("Zahl", "Bitte kleiner 50 (row)");
          }
        }
      }

      public class TableLeftValidationRule : ValidationRule
      {
        public override ValidationResult Validate(object value,
            System.Globalization.CultureInfo cultureInfo)
        {
          if (value is BindingGroup)
          {
            if ((value as BindingGroup).Items.Count > 0)
            {
              System.Data.DataRowView rv = ((value as BindingGroup).Items[0] as System.Data.DataRowView);
              DataRow row = rv.Row;

              row.ClearErrors();

              if ( Convert.ToInt32(row["Zahl"]) > 50)
              {
                row.SetColumnError(row.Table.Columns["Zahl"], "Bitte kleiner 50 (row)");
                return new ValidationResult(false, "Bitte kleiner 50");
              }
            }
          }

          return ValidationResult.ValidResult;
        }
      }

      public class TableRightValidationRule : ValidationRule
      {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
          BindingGroup group = (BindingGroup)value;

          foreach (var item in group.Items)
          {
            DataRowView rowView = item as DataRowView;

            if (rowView != null)
            {
              if (rowView.Row.HasErrors)
              {
                return new ValidationResult(false, rowView.Row.GetColumnError("Zahl"));
              }
            }
          }

          return ValidationResult.ValidResult;
        }
      }
    }

    Freitag, 16. November 2012 09:43