Элементы управления пользовательского интерфейса WPF неверно проверяют событие ErrorsChanged

У меня есть следующая реализация 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<ValidationResult> GetValidationResultAsync() обязательный метод, он использует беглый пакет проверки Nuget.

    private readonly ModelValidator _modelValidator = new ModelValidator();

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

Проблема в том, что когда я вызываю из ViewModel ValidateAsync() метод модели, элементы управления вводом пользовательского интерфейса не делают недействительными / проверяют правильно, на самом деле у меня есть элемент управления вкладки и проверить измененные модели в индексе вкладки, некоторые могут отображать красную границу, когда я меняю вкладку, но затем снова возвращаюсь в нормальное состояние к следующему изменение вкладки.

В отладке это показывает, что 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.

Я подключился к событию Validation.Error всех пользовательских контроллеров XAML и заметил, что даже когда я получаю индекс выбранной вкладки, меняем значение Validation.Error запускается один раз для первой вкладки и никогда больше, я подозреваю, что по какой-то причине происходит очистка,

Код для SelectedTabIndex, который запускает проверки моделей.

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

ValidateModels вызовы методов ValidateAsync модели в ViewModel.

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

MainWindow TabControl XAML.

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

[Обновление 2]

После добавления настраиваемого стиля ошибки и настраиваемого шаблона ошибки, я вижу, что всплывающая подсказка элементов управления остается с ошибкой "условие не выполнено", но шаблон ошибки очищается. Таким образом, TextBox не показывает шаблон ошибки, пользовательский или по умолчанию, но ошибка проверки существует, и во всплывающей подсказке отображается ошибка.

Почему шаблоны XAML очищаются в TabIndexChange и почему они не обновляются, по крайней мере, в активном элементе вкладки, который я просматриваю. Это может быть проблема, которую я должен решить.

Кроме того, как упоминалось ранее, я не вижу, чтобы ErrorsChanged повторно проверял элементы управления, за исключением первого вызова SelectedTabIndex setter.

Шаблоны, которые я добавил.

<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>

1 ответ

Решение

Проблема в том, что вкладки, расширители и т. Д. Плохо работают с валидаторами, вам нужно включить AdornerDecoratorили не использовать вкладки, что в моем случае не вариант.

Проблема с проверкой WPF (IDataErrorInfo) и фокусировкой вкладок.

Другие вопросы по тегам