Элементы управления пользовательского интерфейса 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) и фокусировкой вкладок.