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.
14 réponses
ne pas utiliser Mode=TwoWay
DataGridTextColumns
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 Visibility
Validation.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>
j'ai trouvé la meilleure réponse qui a fonctionné pour moi. Juste effacer votre DataGrid
RowValidationErrorTemplate
.
En Code
YourGrid.RowValidationErrorTemplate = new ControlTemplate();
Dans Le Code Xaml
<DataGrid.RowValidationErrorTemplate> <ControlTemplate> </ControlTemplate> </DataGrid.RowValidationErrorTemplate>`
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); }
écrire votre propre méthode Isvalide, la façon dont vous comme
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.)
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.
essayez d'enlever le Mode=TwoWay
pour chacun des DataGridTextColumns
de chacun des éléments de liaison.
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.
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.
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.
ma solution était simplement de supprimer la propriété UpdateSourceTrigger= "LostFocus" de la déclaration contraignante dans chaque datagridcolumn.
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
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>
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
Mon scénario était comme ceci:
- modèles d'instruments
IDataErrorInfo
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>
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.
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;
}