Ошибки проверки WPF DataGrid не очищаются

Итак, у меня есть WPF DataGrid, который связан с ObservableCollection. Коллекция имеет проверку на своих членах, через IDataErrorInfo. Если я редактирую ячейку таким образом, чтобы быть недействительным, а затем tab от него, прежде чем нажать enter, а затем вернуться и сделать его действительным, ячейка перестанет показывать недействительный, однако,"!- во главе ряда по-прежнему будет стоять ... --3--> будет ссылаться на предыдущее, недопустимое значение.

14 ответов


не используя Mode=TwoWay на DataGridTextColumns решает одну версию проблемы, однако кажется, что эта проблема может появиться из ниоткуда по другим причинам.

(любой, у кого есть хорошее объяснение, почему не использовать Mode=TwoWay решает это в первую очередь, вероятно, близко к решению этой проблемы)

то же самое только что случилось со мной с DataGridComboBoxColumn поэтому я попытался копнуть немного глубже.

проблема не в Binding на Control это показывает ErrorTemplate внутри DataGridHeaderBorder. Это связывает его Visibility to Validation.HasError для предка DataGridRow (точно так, как это должно быть), и эта часть работает.

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

проблема в том, что ошибка проверки не очищены от DataGridRow как только он будет решен. В моей версии проблемы DataGridRow началось с 0 ошибок. Когда я ввел недопустимое значение, он получил 1 ошибку так, до сих пор так хорошо. Но когда я решил ошибку, она прыгнула до 3 ошибок, все из которых были одинаковыми.

здесь я попытался решить это с помощью DataTrigger, что установлено ValidationErrorTemplate to {x:Null} если Validation.Errors.Count не 1. Он отлично работал для первой итерации, но как только я очистил ошибку во второй раз, она вернулась. У него больше не было 3 ошибок, у него было 7! Через несколько итераций он был выше 10.

я также попытался очистить ошибки вручную, выполнив UpdateSource и UpdateTarget на BindingExpressions но ничего не вышло. Validation.ClearInvalid и никакого эффекта. И просмотр исходного кода в инструментарии не дал мне ничего:)

поэтому у меня нет хороших решений, но я подумал, что все равно должен опубликовать свои выводы..

мой единственный "обходной путь" до сих пор - просто скрыть ErrorTemplate на DataGridRowHeader

<DataGrid ...>
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Setter Property="ValidationErrorTemplate" Value="{x:Null}"/>
        </Style>
    </DataGrid.RowStyle>
    <!-- ... -->
</DataGrid>

Я нашел лучший ответ, который работал для меня. Просто очистите свой DataGrid ' s RowValidationErrorTemplate.

  1. Код

    YourGrid.RowValidationErrorTemplate = new ControlTemplate();
    
  2. В Xaml

    <DataGrid.RowValidationErrorTemplate>
        <ControlTemplate>
        </ControlTemplate>
    </DataGrid.RowValidationErrorTemplate>`
    
  3. затем сделайте свой собственный шаблон ошибки проверки строки.

    если ваш элемент данных 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. напишите свой собственный метод IsValid, как вы как


моим решением было реализовать обратную связь проверки пользовательских строк, аналогичную на этой странице под для настройки обратной связи проверки строки. Ошибка строки затем исчезает соответствующим образом.

(Я также добавил RowHeaderWidth="20" до DataGrid определение, чтобы избежать смещения таблицы вправо при первом появлении восклицательного знака.)


У меня такая же проблема с шаблоном ошибки RowHeader не уходит. Я использую INotifyDataErrorInfo. Следуя исследованиям Фредрика Хедблада, я сделал обходной путь; я изменил шаблон 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" с уведомлением об изменении. В моем проекте я гарантировал, что свойство HasErrors обновлено путем вызова PropertyChanged для HasErrors в событии item EndEdit.


попробуйте удалить Mode=TwoWay для каждого DataGridTextColumns С каждого из элементов привязки.


мой обходной путь не должен был использовать проверку.Ошибки, но используйте 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.Пункт.Ошибка proeprty.


в моем случае он работал хорошо и хорошо, когда мы использовали DataGrid WPF3.5 версия. Мы обновились до 4.0, затем он перестал сбрасываться. После поисков на SO, google и т. д. Я случайно нашел свое решение. Настройка UpdateSourceTrigger=PropertyChanged на привязке в DataGridTextColumn исправил его для меня.

Я только что понял, что красный восклицательный знак не очищается при установке его на правильное значение.


Если вы видите все большее количество ошибок, подобных 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

A Свойство Проверяется

    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

Итак, при запуске обработчика измененного свойства, если в словаре ValidationErrors существует ошибка, она удаляется, затем проверяется значение, и если оно не соответствует требованиям, в словарь добавляется ошибка. Это позволяет убедиться, что в словаре ошибок проверки сущностей присутствует только один экземпляр ошибки.


мое решение было просто удалить свойство UpdateSourceTrigger= "LostFocus" из объявления привязки в каждом столбце datagridcolumn.


в моем случае мне пришлось удалить с обязательным определением

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">

проверка: 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


Я не использую IDataErrorInfo или INotifyDataErrorInfo и моим решением было изменить мои привязки с UpdateSourceTrigger="PropertyChanged" to UpdateSourceTrigger="LostFocus" Это было единственное, что

если вы используете ValidationRules в определении столбца DataGrid, и вам нужны правила проверки для запуска, когда когда-либо изменения свойства (в пользовательском интерфейсе или свойстве) смотрят в setting ValidatesOnTargetUpdated="True" на ValidationRule

пример 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>

хорошо, после некоторой работы для меня сработало изменение решения synergetic, вот как я его реализовал:

    <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>

Не забудьте сослаться на BooleanToVisibilityConverter:

    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>

просто стиль строки (пока) не выглядят так хорошо, как по умолчанию, я работаю над этим.

Edit: Plz справка здесь


мой сценарий был такой:

  1. модель реализует IDataErrorInfo
  2. Пользовательское правило проверки строк на основе практические примеры WPF DataGrid-проверка с помощью IDataErrorInfo, который объединил все ошибки из модели с помощью IDataErrorInfo.

    <DataGrid.RowValidationRules>
        <local:RowDataInfoValidationRule ValidationStep="UpdatedValue" />
    </DataGrid.RowValidationRules>
    
  3. ValidatesOnDataErrors=True, ValidatesOnExceptions=True, NotifyOnValidationError=True внутри привязки (с которой я начал)

это вызвало множественный доступ к моему проверка двигателя и eventualy оставил мой DataGrid в несогласованном состоянии (уведомление об ошибке в заголовке строки, даже если строка действительна).

решение состояло в том, чтобы удалить переключатели из привязки (пункт 3.)

Я предлагаю прочитать через очистка ошибки проверки строки DataGrid тоже.


я использовал этот метод, который устраняет RowValidationRules и вместо этого использует проверки свойств в viewmodel. Для этого требуются статические переменные и аннотации данных:

//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;
}