programing

WPF DataGrid 유효성 검사 오류가 지워지지 않습니다.

showcode 2023. 4. 25. 23:46
반응형

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잘 알겠습니다.이 문제에 대한 제 견해로는요,DataGridRow0으로요, 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.Count11은 아니었어요. 첫 번째 반복에서는 잘 작동했지만 두 번째로 오류를 해결하자 다시 돌아왔습니다.첫 번째 반복에서는 잘 작동했지만 두 번째로 오류를 해결하자 다시 복구되었습니다.오류가 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요.

  1. 암호입니다.

    YourGrid.RowValidationErrorTemplate = new ControlTemplate();
    
  2. Xaml에서요

    <DataGrid.RowValidationErrorTemplate>
        <ControlTemplate>
        </ControlTemplate>
    </DataGrid.RowValidationErrorTemplate>`
    
  3. 그런 다음 행 유효성 검사 오류 템플릿을 직접 만듭니다.

    데이터 항목이 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);
    }
    
  4. 원하는 방식으로 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>

제 시나리오는 이렇습니다.

  1. 구현을 모델링합니다.
  2. IDataErrorInfo를 사용하여 모델의 모든 오류를 결합한 WPF DataGrid 실제 예 -IDataErrorInfo 를 기반으로 하는 사용자 지정 행 유효성 검사 규칙입니다.

    <DataGrid.RowValidationRules>
        <local:RowDataInfoValidationRule ValidationStep="UpdatedValue" />
    </DataGrid.RowValidationRules>
    
  3. 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 입니다.

반응형