WPF DataGrid 验证错误未清除
Posted
技术标签:
【中文标题】WPF DataGrid 验证错误未清除【英文标题】:WPF DataGrid validation errors not clearing 【发布时间】:2011-07-03 05:05:07 【问题描述】:所以我有一个 WPF DataGrid
,它绑定到 ObservableCollection
。该集合通过IDataErrorInfo
对其成员进行了验证。如果我以一种无效的方式编辑一个单元格,然后在按 Enter 之前将其移开,然后返回并使其有效,则该单元格将停止显示无效,但是,“!”行首仍然存在,ToolTip
将引用之前的无效值。
【问题讨论】:
【参考方案1】:不使用Mode=TwoWay
代替DataGridTextColumns
解决了一个版本的问题,但似乎这个问题也可能由于其他原因突然出现。
(任何对为什么不使用 Mode=TwoWay
有很好的解释的人都可能首先解决这个问题)
DataGridComboBoxColumn
也发生了同样的事情,所以我试图深入挖掘。
问题不在于Control
中的Binding
在DataGridHeaderBorder
中显示ErrorTemplate
。它正在将其Visibility
绑定到Validation.HasError
的祖先DataGridRow
(正如它应该做的那样)并且该部分正在工作。
Visibility="Binding (Validation.HasError),
Converter=StaticResource bool2VisibilityConverter,
RelativeSource=RelativeSource AncestorType=x:Type DataGridRow"/>
问题是验证错误在解决后不会从DataGridRow
中清除。在我的问题版本中,DataGridRow
开始时出现 0 个错误。当我输入一个无效值时,它得到了 1 个错误,到目前为止一切都很好。但是当我解决这个错误时,它跳到了 3 个错误,所有这些都是一样的。
在这里我尝试使用 DataTrigger
解决它它回来了。它不再有 3 个错误,而是有 7 个!经过几次迭代后,它超过了 10。
我还尝试通过在 BindingExpressions
上执行 UpdateSource
和 UpdateTarget
手动清除错误,但没有骰子。 Validation.ClearInvalid
也没有任何效果。并且查看工具包中的源代码并没有让我有任何收获:)
所以我对此没有任何好的解决方案,但我想我还是应该发布我的发现..
到目前为止,我唯一的“解决方法”是将ErrorTemplate
隐藏在DataGridRowHeader
中
<DataGrid ...>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ValidationErrorTemplate" Value="x:Null"/>
</Style>
</DataGrid.RowStyle>
<!-- ... -->
</DataGrid>
【讨论】:
如果我不使用Mode=TwoWay
,则文本单元格不再可编辑,DataGridTextColumn.EditingElementStyle
将永远不会应用。
DataGridTextColumns
,您的第一个解决方案非常适合我!我也没有像@bitbonk 提到的任何编辑问题。所以谢谢你,即使我仍然不明白为什么会这样:D【参考方案2】:
我找到了这个问题的根本原因。这与BindingExpressionBase
s 如何失去对BindingGroup
的引用有关,因为只有BindingExpression
负责删除其ValidationErrors
。
在这种 DataGrid 验证的情况下,它有多个可能会丢失引用的来源:
明确地,当DataGridCell.BuildVisualTree()
为DataGridCell
重建可视化树时,属于此单元格的BindingGroup
的所有旧BindingExpressions
都将被删除,然后其Content
属性更改为新价值
明确地,当DataGridCell
的Content
属性发生更改(通过DataGridCell.BuildVisualTree()
或其他方式)时,将为旧属性值上的所有绑定调用BindingExpressionBase.Detach()
方法,这也删除了引用在任何ValidationError
有机会被删除之前,转到BindingGroup
隐含地,因为大多数对BindingExpressionBase
的引用实际上都是WeakReference
s,即使上述所有情况都不会导致引用被删除,但是当查找TargetElement
的BindingExpressionBase
时,有可能底层的WeakReference
返回null
并且属性访问器再次调用损坏的Detach()
方法
根据上述发现,现在也很清楚为什么不使用Mode=TwoWay
代替DataGridTextColumn
有时可以解决问题。 DataGridTextColumn
将变为只读,因此 DataGridCell
的 Content
属性永远不会更改。
为此,我使用附加的DependencyProperty
编写了一个解决方法。
using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Controls.Primitives;
namespace Utilities
public static class DataGridExtension
/// <summary>
/// Identifies the FixBindingGroupValidationErrorsFor attached property.
/// </summary>
public static readonly DependencyProperty FixBindingGroupValidationErrorsForProperty =
DependencyProperty.RegisterAttached("FixBindingGroupValidationErrorsFor", typeof(DependencyObject), typeof(DataGridExtension),
new PropertyMetadata(null, new PropertyChangedCallback(OnFixBindingGroupValidationErrorsForChanged)));
/// <summary>
/// Gets the value of the FixBindingGroupValidationErrorsFor property
/// </summary>
public static DependencyObject GetFixBindingGroupValidationErrorsFor(DependencyObject obj)
return (DependencyObject)obj.GetValue(FixBindingGroupValidationErrorsForProperty);
/// <summary>
/// Sets the value of the FixBindingGroupValidationErrorsFor property
/// </summary>
public static void SetFixBindingGroupValidationErrorsFor(DependencyObject obj, DependencyObject value)
obj.SetValue(FixBindingGroupValidationErrorsForProperty, value);
/// <summary>
/// Handles property changed event for the FixBindingGroupValidationErrorsFor property.
/// </summary>
private static void OnFixBindingGroupValidationErrorsForChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
DependencyObject oldobj = (DependencyObject)e.OldValue;
if (oldobj != null)
BindingGroup group = FindBindingGroup(d); //if d!=DataGridCell, use (DependencyObject)e.NewValue
var leftOverErrors = group.ValidationErrors != null ?
Validation.GetErrors(group.Owner).Except(group.ValidationErrors).ToArray() : Validation.GetErrors(group.Owner).ToArray();
foreach (var error in leftOverErrors)
//HINT: BindingExpressionBase.Detach() removes the reference to BindingGroup, before ValidationErrors are removed.
if (error.BindingInError is BindingExpressionBase binding && (binding.Target == null ||
TreeHelper.IsDescendantOf(binding.Target, oldobj)) && binding.BindingGroup == null &&
(binding.ValidationErrors == null || binding.ValidationErrors.Count == 0 || !binding.ValidationErrors.Contains(error)))
typeof(Validation).GetMethod("RemoveValidationError", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] error, group.Owner, group.NotifyOnValidationError);
private static BindingGroup FindBindingGroup(DependencyObject obj)
do
if (obj is FrameworkElement fe)
return fe.BindingGroup;
if (obj is FrameworkContentElement fce)
return fce.BindingGroup;
obj = LogicalTreeHelper.GetParent(obj);
while (obj != null);
return null;
private static class TreeHelper
private static DependencyObject GetParent(DependencyObject element, bool recurseIntoPopup)
if (recurseIntoPopup)
// Case 126732 : To correctly detect parent of a popup we must do that exception case
Popup popup = element as Popup;
if ((popup != null) && (popup.PlacementTarget != null))
return popup.PlacementTarget;
Visual visual = element as Visual;
DependencyObject parent = (visual == null) ? null : VisualTreeHelper.GetParent(visual);
if (parent == null)
// No Visual parent. Check in the logical tree.
parent = LogicalTreeHelper.GetParent(element);
if (parent == null)
FrameworkElement fe = element as FrameworkElement;
if (fe != null)
parent = fe.TemplatedParent;
else
FrameworkContentElement fce = element as FrameworkContentElement;
if (fce != null)
parent = fce.TemplatedParent;
return parent;
public static bool IsDescendantOf(DependencyObject element, DependencyObject parent)
return TreeHelper.IsDescendantOf(element, parent, true);
public static bool IsDescendantOf(DependencyObject element, DependencyObject parent, bool recurseIntoPopup)
while (element != null)
if (element == parent)
return true;
element = TreeHelper.GetParent(element, recurseIntoPopup);
return false;
然后将此属性通过绑定附加到DataGridCell
的Content
属性。
<Window ...
xmlns:utils="clr-namespace:Utilities">
...
<DataGrid ...>
<DataGrid.CellStyle>
<Style BasedOn="StaticResource x:Type DataGridCell" TargetType="x:Type DataGridCell">
<Setter Property="utils:DataGridExtension.FixBindingGroupValidationErrorsFor" Value="Binding Content, RelativeSource=RelativeSource Self" />
</Style>
</DataGrid.CellStyle>
</DataGrid>
...
</Window>
【讨论】:
不知道如何将它集成到我的项目中也许有人可以解释一下吗? 必须使用什么“使用”?使用 System.Reflection;?林克? 这似乎对我有用。我有一个具有验证的DataGridComboboxColumn,它未在选择有效值时清除错误。添加此代码已解决此问题。 更正 - 这仅适用于 DataGridComboBoxColumn 当其属性绑定是唯一具有验证的属性时。如果我包含一个属性绑定到同一对象上另一个属性的属性绑定的 DataGridTextColumn,则会出现相同错误消息的多个实例。 我创建了这个解决方法来解决另一个answer 中描述的问题的根本原因,它实际上适用于每个DataGridColumn
类型和每个绑定场景。如果它对您不起作用,那么它可能是由其他原因引起的。如果它适用于绑定对象的一个属性而不适用于另一个属性,您应该检查您的验证逻辑,它实际上会创建重复的ValidationErrors
。我看过一些IDataErrorInfo
的在线示例,它们使用缓存来处理错误,然后忘记重置缓存。【参考方案3】:
我找到了最适合我的答案。只需清除您的DataGrid
的RowValidationErrorTemplate
。
在代码中
YourGrid.RowValidationErrorTemplate = new ControlTemplate();
在 Xaml 中
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>`
然后制作自己的行验证错误模板。
如果您的数据项是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);
按照自己喜欢的方式编写自己的 IsValid 方法
【讨论】:
【参考方案4】:我有同样的问题,RowHeader 错误模板不会消失。我正在使用 INotifyDataErrorInfo。跟进 Fredrik Hedblad 的研究,我做了一个解决方法;我已修改 DataGridRowHeader 模板以使用 MultiBinding 来实现 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>
这依赖于具有“HasErrors”属性和更改通知的绑定对象。在我的项目中,我通过在项 EndEdit 事件中为 HasErrors 引发 PropertyChanged 来确保更新 HasErrors 属性。
【讨论】:
【参考方案5】:我的解决方案是实现自定义行验证反馈,类似于 自定义行验证反馈部分下的 this page。然后行错误会适当地消失。
(我还在DataGrid
定义中添加了RowHeaderWidth="20"
,以避免第一次出现感叹号时表格向右移动。)
【讨论】:
【参考方案6】:尝试从每个 Binding 元素中删除每个 DataGridTextColumns
的 Mode=TwoWay
。
【讨论】:
那么我应该如何通过 DataGrid 行和单元格进行编辑?【参考方案7】:如果您看到越来越多的类似于 Meleak 的错误,我很想知道您的错误集合是如何填充的。在问题的 Meleaks 版本中,他在解决无效数据后看到三个错误(以及更多错误)。
在我的数据验证代码中,我删除了特定错误的先前实例,然后在每次数据更改时重新添加。作为参考,这里有一个示例:
验证管道
#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
正在验证的属性
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
因此,当属性 Changed 处理程序运行时,如果 ValidationErrors 字典中存在错误,则将其删除,然后检查值,如果不符合要求,则将错误添加到字典中。这有助于确保该实体验证错误字典中仅存在任何错误的一个实例。
【讨论】:
集合中的所有错误完全相同,一旦DataGridCell
的内容出现验证错误,它们就会在内部由DataGrid
填充到DataGridRow
。不幸的是,你不能对这个集合做任何事情,只能看看它。我的版本中的错误来自 viewmodels IDataErrorInfo
实现。要深入了解问题所在,您可能需要访问源代码(而不是工具包中的源代码,因为它们在这方面的工作方式不同)。反射器可能会起作用..
嗯,是的,我之前没有在 ViewModel 上实现 IDataErrorInfo。我对我的数据对象执行此操作,以便它们可以报告自己的验证状态(从长远来看,这并不重要)。那么 DataGrid 填充了这些错误,它们是从 DataGridCell 传递给它的吗?我没有听说过这个(我绝不是专家)。这是普通的 .NET DataGrid 吗?【参考方案8】:
我的解决方法是不使用 Validation.Errors,而是使用 DataGridRow.Item 属性。如果您的 DataGrid 绑定到实现 IDataErrorInfo 接口的业务对象,那么您可以添加 IsNotValid 属性(或 IsValid),并确保 Error 属性返回与该对象关联的所有错误。然后自定义 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>
同样在 DataGridRow 样式中自定义 ValidationErrorTemplate,使其显示来自 DataGridRow.Item.Error proeprty 的错误消息。
【讨论】:
【参考方案9】:最初的问题是 2011 年的,Datagrids 验证系统仍然存在问题,无法使用。我花了 2 天时间试图找到一个解决方案来完成以下工作:
我的模型项实现了 INotifyDataErrorInfo 和 INotifyPropertyChanged 它们位于绑定到 DataGrid 的 BindingList 中 DataGrid 应该显示来自用户输入的验证错误以及来自不同来源的模型更改 RowValidation-Error-mark 应该显示任何单元格是否存在验证错误,否则隐藏,无论用户当前是在编辑、提交还是对行不执行任何操作 无效单元格应显示带有错误文本的工具提示 没有错误或故障实现此行为的唯一方法是放弃 RowValidation 和 CellValidation,而使用 RowHeader 和样式。我从网络上的各种来源复制了以下代码。我还不能对此进行广泛的测试,但乍一看它看起来很有希望。
在 DataGrids XAML 中:
<DataGrid ... local:DataGridProps.ShowCellErrorBorder="False">
<DataGrid.Resources>
<local:DataGridValidationConverter x:Key="DataGridValidationConverter" />
<Style TargetType="TextBlock" x:Key="errTemplate">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="Binding Path=(Validation.Errors)[0].ErrorContent
RelativeSource=x:Static RelativeSource.Self, "/>
<Setter Property="Background" Value="LightSalmon"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<Grid Margin="0,-2,0,-2"
Visibility="Binding Path=DataContext.HasErrors,
RelativeSource=RelativeSource Mode=FindAncestor,
AncestorType=x:Type DataGridRow,
Converter=StaticResource DataGridValidationConverter,
FallbackValue=Hidden">
<Ellipse StrokeThickness="0" Fill="Red"
Width="Binding Path=FontSize,
RelativeSource=RelativeSource Mode=FindAncestor,
AncestorType=x:Type DataGridRow"
Height="Binding Path=FontSize,
RelativeSource=RelativeSource Mode=FindAncestor,
AncestorType=x:Type DataGridRow" />
<TextBlock Text="!" FontWeight="Bold" Foreground="White" HorizontalAlignment="Center"
FontSize="Binding Path=FontSize,
RelativeSource=RelativeSource Mode=FindAncestor,
AncestorType=x:Type DataGridRow" />
</Grid>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
<DataGrid.Columns>
<DataGridTextColumn Header="Vorname" ElementStyle="StaticResource errTemplate"
Binding="Binding Path=Vorname,
ValidatesOnNotifyDataErrors=True,
NotifyOnValidationError=True" />
...
</DataGrid.Columns>
</DataGrid>
DataGridValidationConverter:
public class DataGridValidationConverter : IValueConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
if ((bool)value)
return Visibility.Visible;
else
return Visibility.Hidden;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
throw new NotImplementedException();
DataGridProps:
public class DataGridProps
public static readonly DependencyProperty ShowCellErrorBorderProperty = DependencyProperty.RegisterAttached(
"ShowCellErrorBorder", typeof(bool), typeof(DataGridProps), new PropertyMetadata(true, ShowCellErrorBorderPropertyChangedCallback));
public static bool GetShowCellErrorBorder(DependencyObject element)
return (bool)element.GetValue(ShowCellErrorBorderProperty);
public static void SetShowCellErrorBorder(DependencyObject element, bool value)
element.SetValue(ShowCellErrorBorderProperty, value);
private static void ShowCellErrorBorderPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
if (GetShowCellErrorBorder(dependencyObject)) return;
var dg = dependencyObject as DataGrid;
if (null != dg)
dg.Loaded += (sender, args) =>
var scrollView = dg.Template.FindName("DG_ScrollViewer", dg) as ScrollViewer;
if (null == scrollView) return;
var scrollContent = scrollView.Template.FindName("PART_ScrollContentPresenter", scrollView) as ScrollContentPresenter;
if (null == scrollContent) return;
scrollContent.AdornerLayer.Visibility = Visibility.Hidden;
;
模型的实现:
public Model()
if (Vorname == null)
Vorname = "";
...
errorsByPropertyName = new Dictionary<string, List<string>>();
this.PropertyChanged += Model_PropertyChanged;
ForceRevalidation(null);
private string _vorname;
public string Vorname get => _vorname; set => SetField(ref _vorname, value);
...
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
private Dictionary<string, List<string>> errorsByPropertyName;
public void ForceRevalidation(string propertyName)
if (string.IsNullOrEmpty(propertyName))
foreach (PropertyInfo property in GetType().GetProperties())
ValidateProperty(property.Name);
else
ValidateProperty(propertyName);
protected virtual void OnErrorsChanged(string propertyName)
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
OnPropertyChanged(nameof(HasErrors));
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public bool HasErrors => errorsByPropertyName.Any();
public System.Collections.IEnumerable GetErrors(string propertyName)
if (propertyName == null)
propertyName = "";
return errorsByPropertyName.ContainsKey(propertyName) ? errorsByPropertyName[propertyName] : null;
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
ValidateProperty(e.PropertyName);
protected virtual void ValidateProperty(string propertyName)
if (propertyName == null)
propertyName = "";
ClearErrors(propertyName);
switch (propertyName)
case nameof(Vorname):
if (string.IsNullOrWhiteSpace(Vorname))
AddError(propertyName, propertyName + " is empty");
break;
...
default:
break;
protected void AddError(string propertyName, string error)
if (!errorsByPropertyName.ContainsKey(propertyName))
errorsByPropertyName[propertyName] = new List<string>();
if (!errorsByPropertyName[propertyName].Contains(error))
errorsByPropertyName[propertyName].Add(error);
OnErrorsChanged(propertyName);
protected void ClearErrors(string propertyName)
if (errorsByPropertyName.ContainsKey(propertyName))
errorsByPropertyName.Remove(propertyName);
OnErrorsChanged(propertyName);
我从我更大的 Model-Base-Class 中创建了这个最小的例子,希望我在这里得到了这方面的所有重要信息。
【讨论】:
【参考方案10】:我的解决方法是从每个 datagridcolumn 的绑定声明中简单地删除属性 UpdateSourceTrigger="LostFocus"。
【讨论】:
【参考方案11】:就我而言,当我们使用 DataGrid WPF3.5 版本时,它运行良好。我们升级到 4.0,然后它停止重置。在搜索 SO、google 等之后,我偶然发现了我的解决方案。 在 DataGridTextColumn 中的 Binding 上设置 UpdateSourceTrigger=PropertyChanged 为我修复了它。
我刚刚意识到红色感叹号在设置为正确值时并不清楚。
【讨论】:
【参考方案12】:在我的情况下,我必须从绑定定义中删除
UpdateSourceTrigger=PropertyChanged
对我来说,这两种定义都适用:
<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>
和
<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 是自定义类并在此处获取
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;
并且您的数据对象必须实现 IDataErrorInfo
【讨论】:
【参考方案13】:我没有使用IDataErrorInfo
或INotifyDataErrorInfo
,我的解决方案是将绑定从UpdateSourceTrigger="PropertyChanged"
更改为UpdateSourceTrigger="LostFocus"
这是唯一的
如果您在 DataGrid 列定义中使用 ValidationRules,并且需要在属性更改(在 UI 或属性中)时运行验证规则,请查看在您的 ValidationRule
上设置 ValidatesOnTargetUpdated="True"
XAML 示例:
<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>
【讨论】:
【参考方案14】:我的场景是这样的:
-
模型实现
IDataErrorInfo
基于 WPF DataGrid Practical Examples -Validation with IDataErrorInfo 的自定义行验证规则,它使用 IDataErrorInfo 结合了模型中的所有错误。
<DataGrid.RowValidationRules>
<local:RowDataInfoValidationRule ValidationStep="UpdatedValue" />
</DataGrid.RowValidationRules>
ValidatesOnDataErrors=True
, ValidatesOnExceptions=True
, NotifyOnValidationError=True
在绑定中(我开始使用)
这导致对我的验证引擎的多次访问并最终使我的DataGrid
处于不一致的状态(即使行有效,行标题上的错误通知)。
解决方案是从绑定中移除开关(第 3 点。)
我也建议阅读Clearing a DataGrid row validation error。
【讨论】:
【参考方案15】:我使用了这种技术,它取消了 RowValidationRules,而是在视图模型中使用属性验证。这需要静态变量和数据注释:
//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;
【讨论】:
以上是关于WPF DataGrid 验证错误未清除的主要内容,如果未能解决你的问题,请参考以下文章