Итак, у меня есть WPF DataGrid
, связанный с ObservableCollection
. Коллекция имеет валидацию на своих членах, через IDataErrorInfo
. Если я отредактирую ячейку таким образом, чтобы она была недействительной, а затем удалилась от нее, прежде чем нажать Enter, вернитесь и сделайте ее действительной, ячейка перестанет показывать недопустимую, однако, "!" во главе строки все равно будет, а ToolTip
будет ссылаться на предыдущее недопустимое значение.
Ошибки проверки WPF DataGrid, не очищающие
Ответ 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;
}