如何在WPF表单上使用INotifyDataErrorInfo正确检查2个相关数据字段?

问题描述 投票:0回答:1

我在表单上有 2 个字符串数据字段。我的规则是两个数据字段的值不能相同且不能为空。看来检查规则确实有效,因为在值再次更改之前不会再次引发更改的数据。例如:F1 = F2 空字符串 ==> 无效数据。 F1 = 1,F2 =2 ==> 通过。 F1=2; F2 = 2 ==> F1 无效(红色矩形)。 F2= 3 ==> F1 仍然有红色矩形,因为数据没有改变,所以它不会再次计算,虽然屏幕上显示 F1 <> F2 但错误消息仍然是 F1 = F2。如果我将 F1 更改为其他内容并返回到 2,那么就可以了,因为它已被再次评估。当用户尝试保存表单时,我最终检查了 F1 和 F2 数据字段。你会如何处理这种情况?谢谢

wpf inotifydataerrorinfo
1个回答
0
投票
using Simplified;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace Core2023.SO.ThichCoiPhim
{
    public class TwoFieldsVM : ViewModelBase, INotifyDataErrorInfo
    {
        public string Field1 { get => Get<string>(); set => Set(value); }
        public string Field2 { get => Get<string>(); set => Set(value); }
        public bool HasErrors => !Errors.All(pair => string.IsNullOrEmpty(pair.Value));

        public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;

        public IEnumerable GetErrors(string? propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                foreach (var pair in Errors)
                {
                    if (!string.IsNullOrEmpty(pair.Value))
                        yield return $"{pair.Key}: {pair.Value}";
                }
            }
            else if (Errors.TryGetValue(propertyName, out var errors))
            {
                if (!string.IsNullOrEmpty(errors))
                    yield return $"{propertyName}: {errors}";
            }
        }

        private readonly Dictionary<string, string> Errors = new()
        {
            { nameof(Field1), Field1EmptyError },
            { nameof(Field2), Field2EmptyError },
        };
        private const string Field1EmptyError = $"{nameof(Field1)} Empty";
        private const string Field2EmptyError = $"{nameof(Field2)} Empty";
        private const string Field1EqualsField2Error = $"{nameof(Field1)} Equals {nameof(Field2)}";

        protected override void OnPropertyChanged(string propertyName, object? oldValue, object? newValue)
        {
            base.OnPropertyChanged(propertyName, oldValue, newValue);

            if (propertyName is nameof(Field1) or nameof(Field2))
            {
                bool f1 = string.IsNullOrEmpty(Field1);
                bool f2 = string.IsNullOrEmpty(Field2);

                string fe1 = Errors[nameof(Field1)];
                string fe2 = Errors[nameof(Field2)];

                if (f1 || f2)
                {
                    Errors[nameof(Field1)] = f1 ? Field1EmptyError : string.Empty;
                    Errors[nameof(Field2)] = f2 ? Field2EmptyError : string.Empty;
                }
                else if (Field1 == Field2)
                {
                    Errors[nameof(Field1)] = Field1EqualsField2Error;
                    Errors[nameof(Field2)] = string.Empty;
                }
                else
                {
                    Errors[nameof(Field1)] = string.Empty;
                    Errors[nameof(Field2)] = string.Empty;
                }
                if (fe1 != Errors[nameof(Field1)])
                {
                    ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Field1)));
                }
                if (fe2 != Errors[nameof(Field2)])
                {
                    ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Field2)));
                }
            }
        }
    }
}
<Window x:Class="Core2023.SO.ThichCoiPhim.TwoFieldsWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Core2023.SO.ThichCoiPhim"
        mc:Ignorable="d"
        Title="TwoFieldsWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:TwoFieldsVM/>
    </Window.DataContext>
    <UniformGrid>
        <TextBox x:Name="textBox1" Text="{Binding Field1, UpdateSourceTrigger=PropertyChanged}" Margin="10"/>
        <TextBox x:Name="textBox2" Text="{Binding Field2, UpdateSourceTrigger=PropertyChanged}" Margin="10"/>
        <ItemsControl ItemsSource="{Binding Path=(Validation.Errors), ElementName=textBox1, Mode=OneWay}"
                      DisplayMemberPath="ErrorContent"/>
        <ItemsControl ItemsSource="{Binding Path=(Validation.Errors), ElementName=textBox2, Mode=OneWay}"
                      DisplayMemberPath="ErrorContent"/>
    </UniformGrid>
</Window>
© www.soinside.com 2019 - 2024. All rights reserved.