WPF DataGrid验证错误未清除
因此我有一个WPFWPF DataGrid验证错误未清除,wpf,validation,datagrid,wpfdatagrid,idataerrorinfo,Wpf,Validation,Datagrid,Wpfdatagrid,Idataerrorinfo,因此我有一个WPFDataGrid,它绑定到一个可观测集合。集合通过IDataErrorInfo对其成员进行验证。如果我以无效的方式编辑一个单元格,然后在按enter键之前将其从选项卡上移开,然后返回并使其有效,则该单元格将停止显示无效,但行首的“!”仍将存在,并且工具提示将引用上一个无效值 尝试从每个绑定元素中删除每个DataGridTextColumns的Mode=TwoWay。对于DataGridTextColumns不使用Mode=TwoWay解决了问题的一个版本,但似乎这个问题也可能出
DataGrid
,它绑定到一个可观测集合
。集合通过IDataErrorInfo
对其成员进行验证。如果我以无效的方式编辑一个单元格,然后在按enter键之前将其从选项卡上移开,然后返回并使其有效,则该单元格将停止显示无效,但行首的“!”仍将存在,并且工具提示将引用上一个无效值 尝试从每个绑定元素中删除每个DataGridTextColumns
的Mode=TwoWay
。对于DataGridTextColumns
不使用Mode=TwoWay
解决了问题的一个版本,但似乎这个问题也可能出于其他原因突然出现
(对于为什么不使用Mode=TwoWay
首先解决这个问题,任何人都有一个很好的解释,很可能接近于解决这个问题的方法)
同样的事情发生在我身上的是一个datagridcomboxcolumn
,所以我试着深入挖掘
问题不在于控件中的绑定
,该控件在DataGridHeaderOrder
中显示ErrorTemplate
。它正在将其可见性
绑定到验证。对于祖先DataGridRow
(与它应该做的完全一样),hasrerror
,并且该部分正在工作
Visibility="{Binding (Validation.HasError),
Converter={StaticResource bool2VisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"/>
问题在于,验证错误一旦解决,就不会从DataGridRow
中清除。在我的问题版本中,DataGridRow
一开始就有0个错误。当我输入一个无效值时,它得到了1个错误,到目前为止还不错。但当我解决错误时,它跳到了3个错误,所有这些都是相同的
在这里,我尝试使用一个DataTrigger
来解决它,该触发器将ValidationErrorTemplate
设置为{x:Null}
如果Validation.Errors.Count
不是1。在第一次迭代中效果很好,但当我第二次清除错误时,它又回来了。它不再有3个错误了,它有7个!经过几次迭代之后,它已经超过了10
我还试图通过在BindingExpressions
上执行UpdateSource
和UpdateTarget
手动清除错误,但没有骰子<代码>验证。ClearInvalid
也没有任何效果。在工具包中查看源代码时,我什么也看不到:)
所以我没有什么好的解决办法,但我想我还是应该发布我的发现
到目前为止,我唯一的“解决方法”是将ErrorTemplate
隐藏在DataGridRowHeader
<DataGrid ...>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ValidationErrorTemplate" Value="{x:Null}"/>
</Style>
</DataGrid.RowStyle>
<!-- ... -->
</DataGrid>
如果您看到越来越多类似于Meleak的错误,我很想知道您的错误集合是如何填充的。在Meleak版本的问题中,他在解决无效数据后看到了三个错误(以及更多)
在我的数据验证代码中,我删除了一个特定错误的前一个实例,然后在每次数据更改时重新添加。以下是一个示例,供参考:
验证管道
正在验证的属性
因此,当运行属性更改处理程序时,如果ValidationErrors字典中存在错误,则会将其删除,然后检查该值,如果该值与要求不匹配,则会将错误添加到字典中。这有助于确保实体验证错误字典中只存在一个错误实例。我的场景如下:
模型实现了IDataErrorInfo
基于的自定义行验证规则,该规则使用IDataErrorInfo组合了模型中的所有错误
<DataGrid.RowValidationRules>
<local:RowDataInfoValidationRule ValidationStep="UpdatedValue" />
</DataGrid.RowValidationRules>
validatesOnDaerRors=True
,validatesOnceptions=True
,NotifyOnValidationError=True
在绑定中(我从它开始)
这导致对我的验证引擎的多次访问,并最终使我的DataGrid
处于不一致状态(即使行有效,行标题上也会出现错误通知)
解决方案是从绑定中移除开关(第3点)
我建议也通读一遍。我的解决方法是从每个datagridcolumn中的绑定声明中删除属性UpdateSourceTrigger=“LostFocus”。我的解决方法不是使用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 ProeProperty的错误消息。我的解决方案是实现自定义行验证反馈,类似于自定义行验证反馈部分下的。行错误将相应地消失。
(我还将RowHeaderWidth=“20”
添加到了DataGrid
定义中,以避免表在感叹号第一次出现时向右移动。)在我的例子中,当我们使用DataGrid WPF3.5版本时,它运行良好。我们升级到4.0,然后它停止重置。在搜索了SO、google等之后,我偶然发现了我的解决方案。
在DataGridTextColumn中的绑定上设置UpdateSourceTrigger=PropertyChanged,为我修复了它
我刚刚意识到,将红色感叹号设置为正确的值时,红色感叹号不清晰。在我的情况下,我必须从绑定定义中删除
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>
您的数据对象必须实现IDataErrorIn
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">
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;
}
}
<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>
<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>
YourGrid.RowValidationErrorTemplate = new ControlTemplate();
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>`
((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);
}
//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;
}