Ошибки проверки WPF DataGrid не очищаются - PullRequest
35 голосов
/ 24 февраля 2011

Итак, у меня есть WPF DataGrid, который связан с ObservableCollection.Коллекция имеет проверку на своих членов, через IDataErrorInfo.Если я отредактирую ячейку таким образом, чтобы она была недействительной, а затем отошел от нее перед нажатием клавиши ввода, затем вернусь и сделаю ее действительной, ячейка перестанет показывать недействительную, однако «!»в начале строки все еще будет там, и ToolTip будет ссылаться на предыдущее недопустимое значение.

Ответы [ 14 ]

22 голосов
/ 17 сентября 2011

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

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

То же самое произошло со мной с DataGridComboBoxColumn, поэтому я попытался копнуть немного глубже.

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

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.

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

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

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

<DataGrid ...>
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Setter Property="ValidationErrorTemplate" Value="{x:Null}"/>
        </Style>
    </DataGrid.RowStyle>
    <!-- ... -->
</DataGrid>
5 голосов
/ 01 октября 2014

Я нашел лучший ответ, который работал для меня. Просто очистите свои DataGrid 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, как вам нравится

2 голосов
/ 24 сентября 2014

У меня та же проблема с шаблоном ошибки 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 в событии EndEdit элемента.

2 голосов
/ 15 марта 2013

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

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

1 голос
/ 13 мая 2013

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

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

1 голос
/ 18 января 2013

Мой обходной путь - не использовать 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.

1 голос
/ 22 сентября 2011

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

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

1 голос
/ 16 сентября 2011

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

0 голосов
/ 05 октября 2016

Я использовал эту технику, которая покончила с 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;
}       
0 голосов
/ 19 августа 2015

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

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

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

Редактировать: Пожалуйста, помогите здесь

...