【问题标题】:WPF UI controls not validating correctly on ErrorsChanged eventWPF UI 控件无法正确验证 ErrorsChanged 事件
【发布时间】:2015-11-01 12:15:32
【问题描述】:

我在抽象基类中有以下 INotifyDataErrorInfo 实现。

    private IEnumerable<ValidationErrorModel> _validationErrors = new List<ValidationErrorModel>();
    public IEnumerable<ValidationErrorModel> ValidationErrors
    {
        get { return _validationErrors; }
        private set
        {
            _validationErrors = value;
            OnPropertyChanged();
        }
    }

    protected abstract Task<ValidationResult> GetValidationResultAsync();

    public IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName) ||
            ValidationErrors == null)
            return null;

        IEnumerable<string> errors = ValidationErrors
            .Where(p => p.PropertyName.Equals(propertyName))
            .Select(p => p.ToString())
            .ToList();

        return errors;
    }

    public bool HasErrors
    {
        get
        {
            bool hasErrors = ValidationErrors != null && ValidationErrors.Any();
            return hasErrors;
        }
    }

    public Task<ValidationResult> ValidateAsync()
    {
        Task<ValidationResult> validationResultTask = GetValidationResultAsync();

        validationResultTask.ContinueWith((antecedent) =>
        {
            if (antecedent.IsCompleted &&
                !antecedent.IsCanceled &&
                !antecedent.IsFaulted)
            {
                ValidationResult validationResult = antecedent.Result;
                if (validationResult != null)
                {
                    lock (ValidationErrors)
                    {
                        ValidationErrors =
                            validationResult.Errors
                                .Select(validationFailure =>
                                    new ValidationErrorModel(validationFailure.PropertyName, validationFailure.ErrorMessage))
                                    .ToList();

                        foreach (ValidationErrorModel validationErrorModel in ValidationErrors)
                        {
                            RaiseErrorsChanged(validationErrorModel.PropertyName);
                        }
                    }
                }
            }
        });
        return validationResultTask;
    }

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { };

    protected virtual void RaiseErrorsChanged(string propertyName)
    {
        var handler = ErrorsChanged;
        if (handler != null)
        {
            Dispatcher.InvokeOnMainThread(() =>
            {
                handler(this, new DataErrorsChangedEventArgs(propertyName));
            });
        }
    }

在从基类派生的模型中,我实现了Task&lt;ValidationResult&gt; GetValidationResultAsync() 所需的方法,它使用流畅的验证 Nuget 包。

    private readonly ModelValidator _modelValidator = new ModelValidator();

    protected override Task<ValidationResult> GetValidationResultAsync()
    {
        return _modelValidator.ValidateAsync(this);
    }

问题是,当我从 ViewModel 调用模型的 ValidateAsync() 方法时,UI 输入控件未正确无效/验证,我实际上有一个选项卡控件并验证选项卡索引中的模型已更改,有些可能会显示更改选项卡后红色边框,然后再次返回正常状态以进行下一个选项卡更改。

在调试中显示ValidationErrors 属性返回错误。

我的 XAML 输入控件代码如下所示。

    <Grid>
        <StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Name:"/>
                <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, ValidatesOnNotifyDataErrors=True}" Width="200"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Scheduled Date:"/>
                <DatePicker DisplayDate="{Binding ScheduledDate, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, ValidatesOnNotifyDataErrors=True}"/>
            </StackPanel>
        </StackPanel>
    </Grid>

[更新 1]

我要提一下,我在MainWindow 中使用了一个选项卡控件和3 个选项卡项,每个选项卡项都是一个UserControl。

我连接到所有 XAML 用户控件的 Validation.Error 事件,我注意到即使我获得选项卡选择的索引更改值,Validation.Error 也会为第一个选项卡触发一次,并且不会再触发一次,我怀疑某处有清理是有原因的。

触发模型验证的 SelectedTabIndex 代码。

        private int _selectedTabIndex = 0;
        public int SelectedTabIndex
        {
            get { return _selectedTabIndex; }
            set
            {
                _selectedTabIndex = value;
                ValidateModels();
                Tab2ViewModel.ValidateModels();
                Tab3ViewModel.ValidateModels();
                OnPropertyChanged();
            }
        }

ValidateModels 方法调用 ViewModel 中模型的ValidateAsync

        public override Task ValidateModels()
        {
            return Model.ValidateAsync();
        }

MainWindow TabControl XAML。

<TabControl SelectedIndex="{Binding SelectedTabIndex, Mode=TwoWay}">

[更新 2]

添加自定义错误样式和自定义错误模板后,我看到控件工具提示停留在条件未满足错误但错误模板正在清除中。因此,TextBox 没有显示错误模板,自定义或默认,但存在验证错误并且工具提示显示错误。

为什么 XAML 模板会在 TabIndexChange 上清除,为什么它们至少不会在我正在查看的活动选项卡项上刷新。这可能是我应该解决的问题。

另外,如前所述,除了第一次调用 SelectedTabIndex 设置器之外,我没有看到 ErrorsChanged 重新验证控件。

我添加的模板。

<Application.Resources>
        <Style x:Key="ErrorStyle"
               TargetType="FrameworkElement">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="ToolTip" Value="{Binding (Validation.Errors).CurrentItem.ErrorContent, RelativeSource={x:Static RelativeSource.Self}}"></Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
        <ControlTemplate x:Key="TextBoxErrorTemplate">
            <DockPanel>
                <Ellipse DockPanel.Dock="Right"
                         Margin="2,0"
                         ToolTip="Contains Invalid Data"
                         Width="10"
                         Height="10"
                         >
                    <Ellipse.Fill>
                        <LinearGradientBrush>
                            <GradientStop Color="#11FF1111" Offset="0"/>
                            <GradientStop Color="#FFFF0000" Offset="1"/>
                        </LinearGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>
                <AdornedElementPlaceholder/>
            </DockPanel>
        </ControlTemplate>
        <Style TargetType="TextBox">
            <Setter Property="Margin" Value="4,4,15,4"/>
            <Setter Property="Validation.ErrorTemplate" Value="{StaticResource TextBoxErrorTemplate}"/>
            <Style.Triggers>
                    <Trigger Property="Validation.HasError" Value="True">
                        <Setter Property="ToolTip">
                            <Setter.Value>
                                <Binding Path="(Validation.Errors).CurrentItem.ErrorContent" RelativeSource="{x:Static RelativeSource.Self}"/>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
            </Style.Triggers>
        </Style>
    </Application.Resources>

【问题讨论】:

  • 正如我在您的代码中看到的,您为ValidationErrors 设置值的唯一位置是ValidateAsync 方法,在这种情况下,该方法由命令调用。因此,要让GetErrors 返回任何错误,您至少应该调用在您的情况下不是选项的命令。
  • 该命令只是 ValidateAsync 的包装器,我从 ViewModels 调用 validateAsync,但如果我想从 XAML 代码调用,也有一个命令。我从示例代码中删除了该命令。
  • 调用 ValidateAsync 的部分与问题相关,最好包含在帖子中。
  • 已更新,如果有兴趣,请在 Bitbucket 公共存储库 bitbucket.org/gtas/entityvalidator 中找到重现此问题的示例原型。
  • 我没有找到你在代码中的哪里设置SelectedTabIndex

标签: c# wpf validation inotifydataerrorinfo


【解决方案1】:

问题是选项卡、扩展器等不适用于验证器,您需要包含 AdornerDecorator,或者不使用选项卡,在我的情况下这不是一个选项。

Issue with WPF validation(IDataErrorInfo) and tab focusing.

【讨论】:

    猜你喜欢
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 2021-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多