抽象基本クラスに次の 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()
、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 UserControls の Validation.Error イベントに接続しましたが、タブで選択したインデックスの値が変更されても、Validation.Error が最初のタブで 1 回発生し、二度と発生しないことに気付きました。何らかの理由でどこかにクリーンアップがあると思われます。 .
モデルの検証を開始する SelectedTabIndex のコード。
private int _selectedTabIndex = 0;
public int SelectedTabIndex
{
get { return _selectedTabIndex; }
set
{
_selectedTabIndex = value;
ValidateModels();
Tab2ViewModel.ValidateModels();
Tab3ViewModel.ValidateModels();
OnPropertyChanged();
}
}
ViewModel 内のモデルのValidateModels
メソッド呼び出し。ValidateAsync
public override Task ValidateModels()
{
return Model.ValidateAsync();
}
MainWindow タブ コントロール XAML。
<TabControl SelectedIndex="{Binding SelectedTabIndex, Mode=TwoWay}">
【アップデート2】
カスタム エラー スタイルとカスタム エラー テンプレートを追加した後、コントロールのツールチップには条件が満たされていないエラーが表示されたままですが、エラー テンプレートはクリアされています。そのため、TextBox にはカスタムまたはデフォルトのエラー テンプレートは表示されませんが、検証エラーが存在し、ツールヒントにエラーが表示されます。
TabIndexChange で XAML テンプレートがクリアされる理由と、少なくとも表示しているアクティブなタブ項目で更新されない理由。これは私が解決すべき問題かもしれません。
また、前に述べたように、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>