Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

Не использовать 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 тоже не имеет никакого эффекта. И просмотр исходного кода в Toolkit не заставил меня никуда:)

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

Единственным "обходным решением" до сих пор является просто скрыть ErrorTemplate в DataGridRowHeader

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

Мое обходное решение заключалось не в использовании 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.

Ответ 6

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

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

Ответ 7

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

Ответ 8

Если вы видите все большее количество ошибок, подобных 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 существует ошибка, он удаляется, тогда значение проверяется, а если оно не соответствует требованиям, в словарь добавляется ошибка. Это помогает гарантировать наличие только одного экземпляра любой ошибки в этом словаре ошибок проверки сущностей.

Ответ 9

Мой сценарий был следующим:

  • Модель реализует IDataErrorInfo
  • Правило проверки правильной строки на основе Практические примеры WPF DataGrid -Validation с IDataErrorInfo, в которых объединены все ошибки из Model с использованием IDataErrorInfo.

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

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

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

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

Ответ 10

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

Ответ 11

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

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

Ответ 12

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

Если вы используете ValidationRules в определении столбца DataGrid, и вам нужно, чтобы правила проверки выполнялись, когда когда-либо изменялось свойство (в пользовательском интерфейсе или в свойстве), загляните в настройку 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>

Ответ 13

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

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

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

Изменить: Справка Plz здесь

Ответ 14

Я использовал этот метод, который устраняет 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;
}