WPF DataGrid erreurs de validation pas de compensation

j'ai Donc un WPF DataGrid, qui est lié à un ObservableCollection. La collection a une validation sur ses membres, à travers IDataErrorInfo. Si je modifie une cellule de manière à être invalide, et puis tab loin de lui avant de frapper entrée, puis revenir et le rendre valide, la cellule cessera de montrer invalide, cependant, le "!"à la tête de la ligne sera toujours là, et le ToolTip fera référence à la valeur précédente, non valide.

33
demandé sur Dave Clemmer 2011-02-24 03:27:04

14 réponses

ne pas utiliser Mode=TwoWayDataGridTextColumns résout une version du problème, mais il semble que ce problème peut surgir de nulle part pour d'autres raisons.

(celui qui a une bonne explication de pourquoi pas à l'aide de Mode=TwoWay résout cela en premier lieu est probablement proche d'une solution à ce problème)

La même chose m'est juste arrivé avec un DataGridComboBoxColumn donc j'ai essayé de creuser un peu plus profond.

Le problème n'est pas la Binding dans le Control qui affiche le ErrorTemplate à l'intérieur DataGridHeaderBorder. C'est la liaison de son VisibilityValidation.HasError pour l'ancêtre DataGridRow (exactement comme cela devrait être le cas) et cette partie fonctionne.

Visibility="{Binding (Validation.HasError),
                     Converter={StaticResource bool2VisibilityConverter},
                     RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"/>

Le problème est que l'erreur de validation n'est pas effacé de la DataGridRow une fois qu'il est résolu. Dans ma version du problème, le DataGridRow commencé avec 0 erreurs. Quand j'ai entré une valeur invalide, il y a eu 1 erreur donc, jusqu'à présent tout va bien. Mais quand j'ai résolu l'erreur il a sauté jusqu'à 3 erreurs, toutes identiques.

Ici, j'ai essayé de le résoudre avec un DataTrigger set ValidationErrorTemplate{x:Null} si Validation.Errors.Count n'était pas 1. Cela a bien fonctionné pour la première itération mais une fois que j'ai effacé l'erreur pour la deuxième fois il était de retour. Il n'y avait plus 3 erreurs, il y en avait 7! Après quelques itérations supplémentaires, il était plus de 10.

j'ai aussi essayé de supprimer les erreurs manuellement en faisant UpdateSource et UpdateTarget sur le BindingExpressions mais pas de dés. Validation.ClearInvalid n'ont pas d'effet non plus. Et en regardant le code source dans la boîte à outils n'est pas moi, n'importe où :)

donc je n'ai pas de bonnes solutions à cela mais j'ai pensé que je devrais poster mes conclusions de toute façon..

ma seule solution pour l'instant est de cacher le ErrorTemplate dans le DataGridRowHeader

<DataGrid ...>
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Setter Property="ValidationErrorTemplate" Value="{x:Null}"/>
        </Style>
    </DataGrid.RowStyle>
    <!-- ... -->
</DataGrid>
21
répondu Fredrik Hedblad 2011-09-17 02:13:25

j'ai trouvé la meilleure réponse qui a fonctionné pour moi. Juste effacer votre DataGridRowValidationErrorTemplate.

  1. En Code

    YourGrid.RowValidationErrorTemplate = new ControlTemplate();
    
  2. Dans Le Code Xaml

    <DataGrid.RowValidationErrorTemplate>
        <ControlTemplate>
        </ControlTemplate>
    </DataGrid.RowValidationErrorTemplate>`
    
  3. alors créez votre propre modèle D'erreur de validation de ligne.

    Si votre élément de données est INotifyPropertyChanged

    ((INotifyPropertyChanged)i).PropertyChanged += this.i_PropertyChanged;`
    

    private void i_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        this.Dispatcher.BeginInvoke(new Action(() =>
        {
            var row = this.ItemContainerGenerator.ContainerFromItem(sender) as DataGridRow;
            if (row == null)
                return;
    
            var Errs = IsValid(row);
    
            if (Errs.Count == 0) row.Header = null;
            else
            {
                // Creatr error template
                var gg = new Grid { ToolTip = "Error Tooltip" };
    
                var els = new Ellipse { Fill = new SolidColorBrush(Colors.Red), Width = row.FontSize, Height = row.FontSize };
    
                var tb = new TextBlock
                {
                    Text = "!",
                    Foreground = new SolidColorBrush(Colors.White),
                    HorizontalAlignment = HorizontalAlignment.Center,
                    FontWeight = FontWeights.Bold
                };
    
                gg.Children.Add(els);
                gg.Children.Add(tb);
    
                row.Header = gg;
            }
        }),
         System.Windows.Threading.DispatcherPriority.ApplicationIdle);
    }
    
  4. écrire votre propre méthode Isvalide, la façon dont vous comme

3
répondu MSL 2016-08-03 09:46:21

ma solution a été de mettre en place un feedback personnalisé de validation de ligne, similaire à cette pagepersonnaliser de validation de ligne commentaires section. L'erreur de ligne disparaît alors de manière appropriée.



(J'ai aussi ajouté RowHeaderWidth="20"DataGrid définition, pour éviter que la table de déplacement vers la droite de la première fois que le point d'exclamation s'affiche.)

2
répondu Conrad 2013-03-15 15:08:37

j'ai le même problème avec le modèle D'erreur RowHeader qui ne disparaît pas. J'utilise INotifyDataErrorInfo. Suite à la recherche de Fredrik Hedblad, j'ai fait une solution de contournement; j'ai modifié le modèle DataGridRowHeader pour utiliser un multiclient pour la visibilité ValidationErrorTemplate:

  <Style x:Key="DataGridRowHeaderStyle" TargetType="{x:Type DataGridRowHeader}">
<!--<Setter Property="Background" Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly=Brushes:BrushesLibrary1,
             ResourceId=HeaderBrush}}"/>-->
<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="{x:Type DataGridRowHeader}">
      <Grid>
        <Microsoft_Windows_Themes:DataGridHeaderBorder BorderBrush="{TemplateBinding BorderBrush}"
                          BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"
                          IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}"
                            IsSelected="{TemplateBinding IsRowSelected}" Orientation="Horizontal"
                            Padding="{TemplateBinding Padding}" SeparatorBrush="{TemplateBinding SeparatorBrush}"
                            SeparatorVisibility="{TemplateBinding SeparatorVisibility}">
          <StackPanel Orientation="Horizontal">
            <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"
                                                                Width="15"/>
            <Control SnapsToDevicePixels="false"
                                       Template="{Binding ValidationErrorTemplate, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}">
              <Control.Visibility>
              <MultiBinding Converter="{StaticResource ValidationConverter}">
                <Binding Path="(Validation.HasError)" RelativeSource="{RelativeSource AncestorType={x:Type DataGridRow}}"/>
                <Binding Path="DataContext.HasErrors" RelativeSource="{RelativeSource AncestorType={x:Type DataGridRow}}"/>
              </MultiBinding>
              </Control.Visibility>
              <!-- Original binding below -->
              <!--Visibility="{Binding (Validation.HasError), Converter={StaticResource bool2VisibilityConverter},
                     RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}">-->
            </Control>
          </StackPanel>
        </Microsoft_Windows_Themes:DataGridHeaderBorder>
        <Thumb x:Name="PART_TopHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Top"/>
        <Thumb x:Name="PART_BottomHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Bottom"/>
      </Grid>
    </ControlTemplate>
  </Setter.Value>
</Setter>

cela dépend des objets reliés ayant une propriété" HasErrors " avec notification de changement. Dans mon projet j'ai veillé à ce que la propriété HasErrors soit mis à jour en levant le PropertyChanged pour HasErrors dans L'événement EndEdit item.

2
répondu ChangedDaily 2014-09-24 19:14:06

essayez d'enlever le Mode=TwoWay pour chacun des DataGridTextColumns de chacun des éléments de liaison.

1
répondu Priyank 2011-09-16 17:44:15

ma solution n'était pas D'utiliser la Validation.Erreurs, mais utilisez DataGridRow.Propriétés de l'élément. Si votre DataGrid est lié à des objets d'affaires qui implémentent L'interface IDataErrorInfo, alors vous pouvez ajouter la propriété IsNotValid (ou IsValid), et vous assurer que la propriété Error renvoie toutes les erreurs associées à l'objet. Puis personnalisez le style par défaut pour DataGridRowHeader:

<Style x:Key="{x:Type DataGridRowHeader}" TargetType="{x:Type DataGridRowHeader}">
    ...
    <Control SnapsToDevicePixels="false"
             Visibility="{Binding RelativeSource={RelativeSource 
                          AncestorType={x:Type DataGridRow}}, 
                          Path=Item.IsNotValid, Converter={StaticResource 
                          Bool2VisibilityConverter}}"
             Template="{Binding RelativeSource={RelativeSource 
                        AncestorType={x:Type DataGridRow}}, 
                        Path=ValidationErrorTemplate}" />

    ...
</Style>

aussi dans le style DataGridRow personnaliser Validationerrorteemplate, de sorte qu'il montre le message d'erreur de DataGridRow.Article.Erreur si elle.

1
répondu synergetic 2013-01-18 11:28:44

dans mon cas, ça marchait très bien quand nous utilisions le DataGrid WPF3.Version 5. Nous sommes passés à 4.0, puis il a arrêté de se réinitialiser. Après des recherches sur SO, google etc, je suis tombé sur ma solution. Réglage UpdateSourceTrigger=PropertyChanged sur la reliure dans la DataGridTextColumn l'a fixé pour moi.

je viens de réaliser que le point d'exclamation rouge n'est pas clair sur le paramètre à une valeur correcte.

1
répondu Nii Osei-Adjemang II 2013-05-29 08:16:46

Si vous voyez un nombre croissant d'erreurs semblables à Meleak, je serais intéressé de savoir comment votre collection des erreurs se rempli. Dans la version de Meleaks du problème, il voit trois erreurs (et plus) après avoir résolu les données invalides.

dans mon code de Validation des données, je supprime l'instance précédente d'une erreur particulière puis je rajoute chaque fois que les données changent. Pour référence, voici un exemple:

La Plomberie De Validation

#Region " Validation workers "

    Private m_validationErrors As New Dictionary(Of String, String)
    Private Sub AddError(ByVal ColName As String, ByVal Msg As String)
        If Not m_validationErrors.ContainsKey(ColName) Then
            m_validationErrors.Add(ColName, Msg)

        End If
    End Sub
    Private Sub RemoveError(ByVal ColName As String)
        If m_validationErrors.ContainsKey(ColName) Then
            m_validationErrors.Remove(ColName)
        End If
    End Sub


    Public ReadOnly Property [Error]() As String Implements System.ComponentModel.IDataErrorInfo.Error
        Get
            If m_validationErrors.Count > 0 Then
                Return "Shipment data is invalid"
            Else
                Return Nothing
            End If
        End Get
    End Property

    Default Public ReadOnly Property Item(ByVal columnName As String) As String Implements System.ComponentModel.IDataErrorInfo.Item
        Get
            If m_validationErrors.ContainsKey(columnName) Then
                Return m_validationErrors(columnName).ToString
            Else
                Return Nothing
            End If
        End Get
    End Property

#End Region

Un Propriété En Cours De Validation

    Private Sub OnZIPChanged()
        Me.RemoveError("ZIP")
        If _ZIP Is Nothing OrElse _ZIP.Trim = "" Then
            Me.AddError("ZIP", "Please enter a ZIP Code")
        Else
            Select Case _ZIP.Length
                Case 5

                Case 10

                Case Else
                    Me.AddError("ZIP", "Please enter a ZIP Code")
            End Select
        End If
        OnPropertyChanged("CanShip")
    End Sub

Ainsi, lorsque la propriété changée handler est exécutée, si une erreur existe dans le dictionnaire ValidationErrors, elle est supprimée, alors la valeur est cochée, et si elle ne correspond pas aux exigences, une erreur est ajoutée au dictionnaire. Cela permet de s'assurer qu'une seule instance d'une erreur est présente dans le dictionnaire d'erreurs de validation des entités.

0
répondu CodeWarrior 2011-09-22 14:39:20

ma solution était simplement de supprimer la propriété UpdateSourceTrigger= "LostFocus" de la déclaration contraignante dans chaque datagridcolumn.

0
répondu Eduardo Brites 2012-05-15 10:54:15

Dans mon cas, j'ai dû supprimer de la définition de liaison

UpdateSourceTrigger=PropertyChanged

Pour moi, il fonctionne avec ces deux définitions:

<DataGridTextColumn                                         
Header="Time, min" 
x:Name="uiDataGridTextColumnTime"
Width="Auto"                                            
CellStyle="{StaticResource ResourceKey=DataGridCellText}"                                            
IsReadOnly="False">
<DataGridTextColumn.Binding>
    <Binding Path="fTime" StringFormat="{}{0:0.00}">
        <Binding.ValidationRules>
            <Validation:CellDataInfoValidationRule ValidationStep="UpdatedValue"/>
        </Binding.ValidationRules>
    </Binding>
</DataGridTextColumn.Binding>

Et

<DataGridTextColumn                                         
Header="Time, min" 
x:Name="uiDataGridTextColumnTime"
Width="Auto"                                            
CellStyle="{StaticResource ResourceKey=DataGridCellText}"     
Binding="{Binding fTime, StringFormat={}\{0:0.00\}, ValidatesOnDataErrors=True}" 
IsReadOnly="False">

Validation:CellDataInfoValidationRule est classe personnalisée et recevez ici

public class CellDataInfoValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        // obtain the bound business object
        BindingExpression expression = value as BindingExpression;
        IDataErrorInfo info = expression.DataItem as IDataErrorInfo;

        // determine the binding path
        string boundProperty = expression.ParentBinding.Path.Path;

        // obtain any errors relating to this bound property
        string error = info[boundProperty];
        if (!string.IsNullOrEmpty(error))
        {
            return new ValidationResult(false, error);
        }

        return ValidationResult.ValidResult;
    }
}

Et objet de données doit mettre en œuvre IDataErrorInfo

0
répondu Evalds Urtans 2013-09-06 11:35:10

Je n'utilise pas IDataErrorInfo ou INotifyDataErrorInfo et ma solution était de changer mes reliures de UpdateSourceTrigger="PropertyChanged"UpdateSourceTrigger="LostFocus" C'était la seule chose que

si vous utilisez ValidationRules dans votre définition de colonne de DataGrid et que vous avez besoin des règles de validation pour exécuter lorsque la propriété change (dans L'interface utilisateur ou la propriété), regardez ValidatesOnTargetUpdated="True" de votre ValidationRule

XAML exemple:

<DataGridTextColumn Header="Name"
    CellStyle="{StaticResource DGCellStyle}"
    ElementStyle="{StaticResource DGTextColValidationStyle}"
    EditingElementStyle="{StaticResource DGTextColEditValidationStyle}">
    <DataGridTextColumn.Binding>
        <Binding Path="Name" UpdateSourceTrigger="LostFocus">
            <Binding.ValidationRules>
                <ValidationResource:YourValidationRule ValidationStep="UpdatedValue" ValidatesOnTargetUpdated="True" />
            </Binding.ValidationRules>
        </Binding>
    </DataGridTextColumn.Binding>
</DataGridTextColumn>
0
répondu MoMo 2013-12-06 19:46:35

Ok, après quelques travaux une modification de la solution de synergetic a fonctionné pour moi, voici comment je l'ai implémenté:

    <Style x:Key="{x:Type DataGridRowHeader}" TargetType="{x:Type DataGridRowHeader}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Control SnapsToDevicePixels="true"
                 Visibility="{Binding RelativeSource={RelativeSource 
                              AncestorType={x:Type DataGridRow}}, 
                              Path=Item.HasErrors, Converter={StaticResource 
                              BooleanToVisibilityConverter }}"
                 Template="{Binding RelativeSource={RelativeSource 
                            AncestorType={x:Type DataGridRow}}, 
                            Path=ValidationErrorTemplate}" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

N'oubliez pas de faire référence au BooleanToVisibilityConverter:

    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>

Juste le style de ligne n'a pas (encore) l'air aussi bon que celui par défaut, je suis en train de travailler sur elle.

Edit: Plz aider ici

0
répondu Houdini 2015-08-24 12:57:04

Mon scénario était comme ceci:

  1. modèles d'instrumentsIDataErrorInfo
  2. règle de Validation de ligne personnalisée basée sur WPF DataGrid exemples pratiques-Validation with IDataErrorInfo, qui combinait toutes les erreurs du modèle en utilisant IDataErrorInfo.

    <DataGrid.RowValidationRules>
        <local:RowDataInfoValidationRule ValidationStep="UpdatedValue" />
    </DataGrid.RowValidationRules>
    
  3. ValidatesOnDataErrors=True, ValidatesOnExceptions=True,NotifyOnValidationError=True à l'intérieur de la liaison (j'ai commencé avec)

cela a causé plusieurs accès à mon Moteur de validation et éventuellement de gauche ma DataGrid dans un état incohérent (notification D'erreur sur l'en-tête de ligne même si la ligne est valide).

la solution a consisté à enlever les interrupteurs de la liaison (point 3.)

je suggère la lecture par le biais de effacer une ligne de grille de données erreur de validation trop.

0
répondu surfen 2016-08-03 10:21:39

j'ai utilisé cette technique qui supprime RowValidationRules et utilise plutôt les validations de propriétés dans un viewmodel. Cela nécessite des variables statiques et les annotations de données :

//uses Prism.MVVM for BindableBase and INotifyDataErrorInfo

private static int _xxStartNo;
private static int _xxEndNo;

// in property getter/setter
private int _startNo;
[CustomValidation(typeof(YourModel), "ValidateStartNoRange")]
public int StartNo
{
    get
       {
          _xxStartNo=_startNo;
          return _startNo;
       }
    set
       {
         .......... 
         ValidateProperty("StartNo") 
       }
}
.......

public static ValidationResult ValidateStartNoRange(int number)
{
   if(number > _xxEndNo) 
   {
       return ValidationResult("Start No must be less than End No.";
   }
   return ValidationResult.Success;
}       
0
répondu Nandy 2016-10-05 13:26:37