Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
WPF DataGrid验证错误未清除_Wpf_Validation_Datagrid_Wpfdatagrid_Idataerrorinfo - Fatal编程技术网

WPF DataGrid验证错误未清除

WPF DataGrid验证错误未清除,wpf,validation,datagrid,wpfdatagrid,idataerrorinfo,Wpf,Validation,Datagrid,Wpfdatagrid,Idataerrorinfo,因此我有一个WPFDataGrid,它绑定到一个可观测集合。集合通过IDataErrorInfo对其成员进行验证。如果我以无效的方式编辑一个单元格,然后在按enter键之前将其从选项卡上移开,然后返回并使其有效,则该单元格将停止显示无效,但行首的“!”仍将存在,并且工具提示将引用上一个无效值 尝试从每个绑定元素中删除每个DataGridTextColumns的Mode=TwoWay。对于DataGridTextColumns不使用Mode=TwoWay解决了问题的一个版本,但似乎这个问题也可能出

因此我有一个WPF
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;
    }