WPF DataGrid 유효성 검사 오류가 지워지지 않습니다.
그래서 저는 WPF를 가지고 있습니다.DataGrid
에 묶인 글씨입니다ObservableCollection
. 이 컬렉션은 회원들에게 유효성을 부여하고 있습니다IDataErrorInfo
잘못된 방식으로 셀을 편집한 다음 Enter 키를 누르기 전에 해당 셀에서 떨어져 탭을 누른 다음 다시 돌아와 유효하게 만들면, 행 맨 앞에 있는 "!"는 여전히 남아 있고, 을 입력할 수 있습니다. 그러나 행 맨 앞에 "!"는 여전히 남아 있습니다.ToolTip
이겁니다.
사용하지 않습니다.Mode=TwoWay
해위에 대해서요DataGridTextColumns
한 가지 버전의 문제를 해결하지만 다른 이유로 인해 이 문제가 갑자기 나타날 수 있습니다.
사용하지 않는 이유에 대해 좋은 설명을 하는 사람(사용하지 않는 이유)이 있는 사람(사용하지 않는 사람)Mode=TwoWay
애초에 이 문제를 해결하는 것이 아마도 이 문제에 대한 해결책에 가까울 것입니다.)
저도 똑같은 일이 생겼어요.DataGridComboBoxColumn
이 노래는요.
문제는 이 문제가 .Binding
서 the에서요.Control
이렇게 표시해 줍니다.ErrorTemplate
안에서.DataGridHeaderBorder
. 그것은 그것의 .을 구속하고 있습니다Visibility
로로 이동합니다.Validation.HasError
조상을 위해서요.DataGridRow
(그렇게 해야 하는 대로) 그리고 그 부분은 작동하고 있습니다.
Visibility="{Binding (Validation.HasError),
Converter={StaticResource bool2VisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"/>
는 검증오류가 안 풀린다는 예요.DataGridRow
잘 알겠습니다.이 문제에 대한 제 견해로는요,DataGridRow
0으로요, 0으로 하다.잘못된 값을 입력했을 때 오류가 1개 발생했기 때문에 지금까지는 양호합니다.하지만 오류를 해결해보니 오류가 3개까지 급증했고, 모두 동일했습니다.
여기에서는 이 문제를 to here here here a a a a a a a a a a a it it it it 。DataTrigger
이렇게 되어 있습니다.ValidationErrorTemplate
로로 이동합니다.{x:Null}
만약 그렇다면요Validation.Errors.Count
11은 아니었어요. 첫 번째 반복에서는 잘 작동했지만 두 번째로 오류를 해결하자 다시 돌아왔습니다.첫 번째 반복에서는 잘 작동했지만 두 번째로 오류를 해결하자 다시 복구되었습니다.오류가 3개가 아니라 7개였어요! 몇 번 더 반복하니 10개가 넘었어요.
그리고 제가 '수동적으로'를 해서 '오류'를 없애려고 노력했습니다.UpdateSource
그리고요.UpdateTarget
서 the에서요.BindingExpressions
주사위는 없어요 Validation.ClearInvalid
이겁니다.툴킷의 소스 코드를 아무 소용이 없었습니다. :) 툴툴툴 and툴 and툴 and툴 and and and and and) and) )) )) )) )) )) )) ) )) )) ) ) ) ) ) ) 。 : )
그래서 저는 이에 대한 좋은 해결책이 없지만 어쨌든 제 조사 결과를 올려야겠다고 생각했습니다.
지금까지의 유일한"해결책"은 그냥 "어느 정도"를 숨기는 것뿐입니다.ErrorTemplate
서 the에서요.DataGridRowHeader
<DataGrid ...>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ValidationErrorTemplate" Value="{x:Null}"/>
</Style>
</DataGrid.RowStyle>
<!-- ... -->
</DataGrid>
이겁니다.그것은 어떻게 해야 하는가에 관한 것입니다.BindingExpressionBase
는 참조가 손실됩니다.BindingGroup
, 왜냐하면만요.BindingExpression
제거의 책임이 있습니다.ValidationErrors
요.
이 DataGrid 검증의 경우 참조가 손실될 수 있는 소스가 여러 개 있습니다.
- 명시적으로 시각 트리를 재구축할 때 사용합니다.
DataGridCell
타고 by로요DataGridCell.BuildVisualTree()
, 모두 오래된 것입니다.BindingExpressions
§의 것입니다.BindingGroup
이 세포에 속한 세포는 제거가 되고, 그 전에 제거가 됩니다.Content
속성이 새 값으로 변경됩니다. - 명시적으로, 다음과 같습니다.
Content
소유물입니다.DataGridCell
((으)로 바꿉니다.DataGridCell.BuildVisualTree()
또는 다른 방법) ,BindingExpressionBase.Detach()
method는 이전 속성 값의 바인딩에 대해 호출되며, 이 경우 이전 속성 값에 대한 참조도 제거됩니다.BindingGroup
어느 때보다도 먼저요.ValidationError
제거될 기회가 . - 암묵적으로, 왜냐하면 대부분의 경우에서 오고 가는 모든 참조가
BindingExpressionBase
실제로 그렇습니다.WeakReference
위의 시나리오가 참조를 제거하지는 않지만, 것이 s를 검색할 때, s는 참조를 제거하지 않습니다.TargetElement
★★★★★★★★★★★★★★★★의 경우BindingExpressionBase
, 기초가 되는 것은 가능성이 있습니다.WeakReference
반환됩니다.null
재산관리인이 고장난 사람을 다시 불러옵니다.Detach()
(method)를 선택합니다.
위의 조사 결과를 통해 왜 사용하지 않는지도 분명해졌습니다.Mode=TwoWay
해위에 대해서요DataGridTextColumn
이겁니다.»는 다음과 같습니다.DataGridTextColumn
읽기 전용이 되고, 읽기 전용이 됩니다.Content
소유권입니다.의 재산입니다.DataGridCell
이겁니다.
첨부 파일을 사용하여 해결 방법을 작성했습니다.DependencyProperty
이걸 위해서요
using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Controls.Primitives;
namespace Utilities
{
public static class DataGridExtension
{
/// <summary>
/// Identifies the FixBindingGroupValidationErrorsFor attached property.
/// </summary>
public static readonly DependencyProperty FixBindingGroupValidationErrorsForProperty =
DependencyProperty.RegisterAttached("FixBindingGroupValidationErrorsFor", typeof(DependencyObject), typeof(DataGridExtension),
new PropertyMetadata(null, new PropertyChangedCallback(OnFixBindingGroupValidationErrorsForChanged)));
/// <summary>
/// Gets the value of the FixBindingGroupValidationErrorsFor property
/// </summary>
public static DependencyObject GetFixBindingGroupValidationErrorsFor(DependencyObject obj)
{
return (DependencyObject)obj.GetValue(FixBindingGroupValidationErrorsForProperty);
}
/// <summary>
/// Sets the value of the FixBindingGroupValidationErrorsFor property
/// </summary>
public static void SetFixBindingGroupValidationErrorsFor(DependencyObject obj, DependencyObject value)
{
obj.SetValue(FixBindingGroupValidationErrorsForProperty, value);
}
/// <summary>
/// Handles property changed event for the FixBindingGroupValidationErrorsFor property.
/// </summary>
private static void OnFixBindingGroupValidationErrorsForChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DependencyObject oldobj = (DependencyObject)e.OldValue;
if (oldobj != null)
{
BindingGroup group = FindBindingGroup(d); //if d!=DataGridCell, use (DependencyObject)e.NewValue
var leftOverErrors = group.ValidationErrors != null ?
Validation.GetErrors(group.Owner).Except(group.ValidationErrors).ToArray() : Validation.GetErrors(group.Owner).ToArray();
foreach (var error in leftOverErrors)
{
//HINT: BindingExpressionBase.Detach() removes the reference to BindingGroup, before ValidationErrors are removed.
if (error.BindingInError is BindingExpressionBase binding && (binding.Target == null ||
TreeHelper.IsDescendantOf(binding.Target, oldobj)) && binding.BindingGroup == null &&
(binding.ValidationErrors == null || binding.ValidationErrors.Count == 0 || !binding.ValidationErrors.Contains(error)))
{
typeof(Validation).GetMethod("RemoveValidationError", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] {error, group.Owner, group.NotifyOnValidationError});
}
}
}
}
private static BindingGroup FindBindingGroup(DependencyObject obj)
{
do
{
if (obj is FrameworkElement fe)
{
return fe.BindingGroup;
}
if (obj is FrameworkContentElement fce)
{
return fce.BindingGroup;
}
obj = LogicalTreeHelper.GetParent(obj);
} while (obj != null);
return null;
}
private static class TreeHelper
{
private static DependencyObject GetParent(DependencyObject element, bool recurseIntoPopup)
{
if (recurseIntoPopup)
{
// Case 126732 : To correctly detect parent of a popup we must do that exception case
Popup popup = element as Popup;
if ((popup != null) && (popup.PlacementTarget != null))
return popup.PlacementTarget;
}
Visual visual = element as Visual;
DependencyObject parent = (visual == null) ? null : VisualTreeHelper.GetParent(visual);
if (parent == null)
{
// No Visual parent. Check in the logical tree.
parent = LogicalTreeHelper.GetParent(element);
if (parent == null)
{
FrameworkElement fe = element as FrameworkElement;
if (fe != null)
{
parent = fe.TemplatedParent;
}
else
{
FrameworkContentElement fce = element as FrameworkContentElement;
if (fce != null)
{
parent = fce.TemplatedParent;
}
}
}
}
return parent;
}
public static bool IsDescendantOf(DependencyObject element, DependencyObject parent)
{
return TreeHelper.IsDescendantOf(element, parent, true);
}
public static bool IsDescendantOf(DependencyObject element, DependencyObject parent, bool recurseIntoPopup)
{
while (element != null)
{
if (element == parent)
return true;
element = TreeHelper.GetParent(element, recurseIntoPopup);
}
return false;
}
}
}
}
그런 다음 이 성질을 이 성질에 바인딩으로 붙입니다.Content
의 of of of of of of of of of의 재산DataGridCell
요.
<Window ...
xmlns:utils="clr-namespace:Utilities">
...
<DataGrid ...>
<DataGrid.CellStyle>
<Style BasedOn="{StaticResource {x:Type DataGridCell}}" TargetType="{x:Type DataGridCell}">
<Setter Property="utils:DataGridExtension.FixBindingGroupValidationErrorsFor" Value="{Binding Content, RelativeSource={RelativeSource Self}}" />
</Style>
</DataGrid.CellStyle>
</DataGrid>
...
</Window>
네, 그렇습니다.★★★★★★★★★★★★★★★★★★★★★★★★DataGrid
의 경우 입니다.RowValidationErrorTemplate
요.
암호입니다.
YourGrid.RowValidationErrorTemplate = new ControlTemplate();
Xaml에서요
<DataGrid.RowValidationErrorTemplate> <ControlTemplate> </ControlTemplate> </DataGrid.RowValidationErrorTemplate>`
그런 다음 행 유효성 검사 오류 템플릿을 직접 만듭니다.
데이터 항목이 INotify인 경우입니다.속성이 변경되었습니다.
((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 메서드를 작성하십시오.
RowHeader 오류 템플릿이 사라지지 않는 것과 같은 문제가 있습니다.INotifyDataErrorInfo를 사용하고 있습니다.Fredrik Hedblad의 조사에 이어 해결 방법을 찾았습니다. DataGridRow를 수정했습니다.ValidationErrorTemplate 가시성을 위해 MultiBinding을 사용하는 헤더 템플릿입니다.
<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" 속성이 있는 바인딩된 개체에 의존합니다.내 프로젝트에서 EndEdit 이벤트에서 HasErrors에 대해 PropertyChanged for HasErrors를 올려 HasErrors 속성이 업데이트되도록 했습니다.
이 솔루션은 행 유효성 검사 피드백 섹션을 사용자 지정하는 페이지와 유사한 사용자 지정 행 유효성 검사 피드백을 구현하는 것이었습니다.그러면 행 오류가 적절하게 사라집니다.
나도 덧붙였어요.RowHeaderWidth="20"
the toDataGrid
처음 느낌표가 나타날 때 테이블이 오른쪽으로 이동하지 않도록 합니다.)
이렇게 해서 빼보세요.Mode=TwoWay
각각에 대해서요.DataGridTextColumns
이겁니다.
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 사전에 오류가 있으면 해당 핸들러가 제거되고 값이 확인되며 요구 사항과 일치하지 않으면 사전에 오류가 추가됩니다.이렇게 하면 해당 엔티티 유효성 검사 오류 사전에 오류 인스턴스가 하나만 있는지 확인할 수 있습니다.
해결 방법은 검증을 사용하지 않는 것이었습니다.오류이지만 DataGridRow를 사용하십시오.항목 속성입니다.DataGrid가 IDataErrorInfo 인터페이스를 구현하는 비즈니스 개체에 바인딩된 경우 IsNotValid 속성(또는 IsValid)을 추가하고 Error 속성이 개체와 관련된 모든 오류를 반환하는지 확인할 수 있습니다.그런 다음 DataGridRow의 기본 스타일을 사용자 지정합니다.머리글은 다음과 같습니다.
<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.오류 프로그램입니다.
원래 질문은 2011년부터이며 Datagrids Validation-System은 여전히 버그가 심하여 사용할 수 없습니다.저는 다음 작업을 위한 솔루션을 찾기 위해 이틀을 보냈습니다.
- My Model-Items는 INotifyDataErrorInfo 및 INotify를 구현합니다.속성이 변경되었습니다.
- DataGrid에 바인딩된 BindingList에 있습니다.
- DataGrid는 사용자 입력의 유효성 검사 오류와 다른 소스의 모델 변경 사항을 표시해야 합니다.
- RowValidation-Error-mark는 사용자가 현재 행을 편집, 커밋 또는 아무 것도 하지 않는 셀에 유효성 검사 오류가 있는지 여부를 표시해야 합니다.
- 잘못된 셀은 오류 텍스트와 함께 도구 설명을 표시해야 합니다.
- 버그나 결함이 없습니다.
이 동작을 수행하는 유일한 방법은 RowValidation 및 CellValidation을 버리고 대신 RowHeader 및 Styles를 사용하는 것입니다.저는 인터넷을 통해 다양한 소스의 다음 코드를 함께 복사했습니다.아직 광범위한 테스트를 할 수는 없지만 언뜻 보기에는 유망해 보입니다.
DataGrids XAML에서 다음을 수행합니다.
<DataGrid ... local:DataGridProps.ShowCellErrorBorder="False">
<DataGrid.Resources>
<local:DataGridValidationConverter x:Key="DataGridValidationConverter" />
<Style TargetType="TextBlock" x:Key="errTemplate">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding Path=(Validation.Errors)[0].ErrorContent}
RelativeSource={x:Static RelativeSource.Self}, "/>
<Setter Property="Background" Value="LightSalmon"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<Grid Margin="0,-2,0,-2"
Visibility="{Binding Path=DataContext.HasErrors,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type DataGridRow}},
Converter={StaticResource DataGridValidationConverter},
FallbackValue=Hidden}">
<Ellipse StrokeThickness="0" Fill="Red"
Width="{Binding Path=FontSize,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type DataGridRow}}}"
Height="{Binding Path=FontSize,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type DataGridRow}}}" />
<TextBlock Text="!" FontWeight="Bold" Foreground="White" HorizontalAlignment="Center"
FontSize="{Binding Path=FontSize,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type DataGridRow}}}" />
</Grid>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
<DataGrid.Columns>
<DataGridTextColumn Header="Vorname" ElementStyle="{StaticResource errTemplate}"
Binding="{Binding Path=Vorname,
ValidatesOnNotifyDataErrors=True,
NotifyOnValidationError=True}" />
...
</DataGrid.Columns>
</DataGrid>
DataGridValidationConverter:
public class DataGridValidationConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
return Visibility.Visible;
else
return Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
DataGridProps를 참조하십시오.
public class DataGridProps
{
public static readonly DependencyProperty ShowCellErrorBorderProperty = DependencyProperty.RegisterAttached(
"ShowCellErrorBorder", typeof(bool), typeof(DataGridProps), new PropertyMetadata(true, ShowCellErrorBorderPropertyChangedCallback));
public static bool GetShowCellErrorBorder(DependencyObject element)
{
return (bool)element.GetValue(ShowCellErrorBorderProperty);
}
public static void SetShowCellErrorBorder(DependencyObject element, bool value)
{
element.SetValue(ShowCellErrorBorderProperty, value);
}
private static void ShowCellErrorBorderPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (GetShowCellErrorBorder(dependencyObject)) return;
var dg = dependencyObject as DataGrid;
if (null != dg)
{
dg.Loaded += (sender, args) =>
{
var scrollView = dg.Template.FindName("DG_ScrollViewer", dg) as ScrollViewer;
if (null == scrollView) return;
var scrollContent = scrollView.Template.FindName("PART_ScrollContentPresenter", scrollView) as ScrollContentPresenter;
if (null == scrollContent) return;
scrollContent.AdornerLayer.Visibility = Visibility.Hidden;
};
}
}
}
모델을 구현합니다.
public Model()
{
if (Vorname == null)
Vorname = "";
...
errorsByPropertyName = new Dictionary<string, List<string>>();
this.PropertyChanged += Model_PropertyChanged;
ForceRevalidation(null);
}
private string _vorname;
public string Vorname { get => _vorname; set => SetField(ref _vorname, value); }
...
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
private Dictionary<string, List<string>> errorsByPropertyName;
public void ForceRevalidation(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
foreach (PropertyInfo property in GetType().GetProperties())
{
ValidateProperty(property.Name);
}
}
else
{
ValidateProperty(propertyName);
}
}
protected virtual void OnErrorsChanged(string propertyName)
{
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
OnPropertyChanged(nameof(HasErrors));
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public bool HasErrors => errorsByPropertyName.Any();
public System.Collections.IEnumerable GetErrors(string propertyName)
{
if (propertyName == null)
propertyName = "";
return errorsByPropertyName.ContainsKey(propertyName) ? errorsByPropertyName[propertyName] : null;
}
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
ValidateProperty(e.PropertyName);
}
protected virtual void ValidateProperty(string propertyName)
{
if (propertyName == null)
propertyName = "";
ClearErrors(propertyName);
switch (propertyName)
{
case nameof(Vorname):
if (string.IsNullOrWhiteSpace(Vorname))
AddError(propertyName, propertyName + " is empty");
break;
...
default:
break;
}
}
protected void AddError(string propertyName, string error)
{
if (!errorsByPropertyName.ContainsKey(propertyName))
errorsByPropertyName[propertyName] = new List<string>();
if (!errorsByPropertyName[propertyName].Contains(error))
{
errorsByPropertyName[propertyName].Add(error);
OnErrorsChanged(propertyName);
}
}
protected void ClearErrors(string propertyName)
{
if (errorsByPropertyName.ContainsKey(propertyName))
{
errorsByPropertyName.Remove(propertyName);
OnErrorsChanged(propertyName);
}
}
저는 더 큰 Model-Base-Class에서 이 최소한의 예를 만들었으며, 이 측면에서 중요한 모든 것을 얻기를 바랍니다.
해결 방법은 각 데이터 ID 열의 바인딩 선언에서 UpdateSourceTrigger="LostFocus" 속성을 제거하는 것이었습니다.
제 경우 DataGrid WPF3.5 버전을 사용할 때 모든 것이 잘 작동했습니다.4.0으로 업그레이드한 후 리셋이 중단되었습니다.SO, 구글 등을 검색한 후 우연히 해결책을 찾았습니다.DataGridTextColumn의 바인딩에서 UpdateSourceTrigger=MessageChanged를 설정하면 수정되었습니다.
빨간색 느낌표는 올바른 값으로 설정할 때 지워지지 않는다는 것을 방금 깨달았습니다.
제 경우 바인딩 정의에서 삭제해야 했습니다.
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"
로로 이동합니다.UpdateSourceTrigger="LostFocus"
이것밖에 없었습니다.
DataGrid 열 정의에서 ValidationRules를 하고 있고 UI 또는 속성에서 속성이 변경될 때마다 실행할 검증 규칙이 필요한 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>
제 시나리오는 이렇습니다.
- 구현을 모델링합니다.
IDataErrorInfo를 사용하여 모델의 모든 오류를 결합한 WPF DataGrid 실제 예 -IDataErrorInfo 를 기반으로 하는 사용자 지정 행 유효성 검사 규칙입니다.
<DataGrid.RowValidationRules> <local:RowDataInfoValidationRule ValidationStep="UpdatedValue" /> </DataGrid.RowValidationRules>
ValidatesOnDataErrors=True
, , 입니다.ValidatesOnExceptions=True
, , 입니다.NotifyOnValidationError=True
(처음부터 시작했던 바인딩 안에서요.
이로 인해 내 검증 엔진에 번 액세스하게 되었고 결국 내 인증 엔진이 사라졌습니다.DataGrid
일치하지 않는 상태입니다(행 머리글에 대한 오류 알림이 유효하더라도).
해결책은 바인딩에서 스위치를 제거하는 것이었습니다(포인트 3).
DataGrid 행 유효성 검사 오류 지우기를 통해 읽어보는 것이 좋습니다.
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;
}
언급URL : https://stackoverflow.com/questions/5099039/wpf-datagrid-validation-errors-not-clearing 입니다.
'programing' 카테고리의 다른 글
bash의 if 블록에서 부울 변수를 평가하는 방법은 무엇입니까? (0) | 2023.04.25 |
---|---|
Excel 한 열의 값이 다른 열의 값 범위에 있습니다. (0) | 2023.04.25 |
git reset --hard HEAD는 추적되지 않은 파일을 남깁니다. (0) | 2023.04.25 |
VBA에서 두 가지 범위에서 범위를 생성하는 방법은 무엇입니까? (0) | 2023.04.25 |
SQL Server의 스키마 바인딩 보기에서 참조하는 열 크기 변경 (0) | 2023.04.20 |