Hi, if you want to see default error information you can include your own conversion and use IDataError in dataobject like in following demo:
XAML:
<Window x:Class="WpfApp1.Window44"
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:WpfApp44"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
Title="Demo Validation IDataError" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<StackPanel x:Name="panel">
<StackPanel.BindingGroup>
<BindingGroup x:Name="ValidateAllFields" NotifyOnValidationError="True">
<BindingGroup.ValidationRules>
<local:ValidateAll ValidationStep="ConvertedProposedValue"/>
</BindingGroup.ValidationRules>
</BindingGroup>
</StackPanel.BindingGroup>
<StackPanel.Resources>
<DataTemplate DataType="{x:Type ValidationError}">
<TextBlock Text="{Binding ErrorContent}"/>
</DataTemplate>
</StackPanel.Resources>
<i:Interaction.Behaviors>
<local:StackPanelBehavior/>
</i:Interaction.Behaviors>
<StackPanel DataContext="{Binding View}">
<TextBox Margin="5">
<TextBox.Text>
<Binding Path="Name" BindingGroupName="ValidateAllFields" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<TextBox Margin="5">
<TextBox.Text>
<Binding Path="Age" BindingGroupName="ValidateAllFields" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
<Button Content="Button to execute BindingGroup.CommitEdit" Command="{Binding Cmd}" Margin="5"/>
<StatusBar Margin="5 20 5 0">
<ContentPresenter Content="{Binding ElementName=panel, Path=(Validation.Errors).CurrentItem}"/>
</StatusBar>
</StackPanel>
</Window>
-----------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace WpfApp44
{
public class ViewModel
{
public Data View { get; set; } = new Data() { Name = "xxx" };
public ICommand Cmd { get => new RelayCommand((state) => { ValidateAllFields?.CommitEdit(); }, null); }
public BindingGroup ValidateAllFields { get; set; }
}
public class Data : IDataErrorInfo, INotifyPropertyChanged
{
private string _name = string.Empty;
public string Name
{
get => this._name;
set { this._name = value; errorMessages[nameof(Name)] = this[nameof(Name)]; OnPropertyChanged(); }
}
private int _age = 0;
private string _ageString = null;
public object Age
{
get => (this._ageString == null) ? this._age.ToString() : this._ageString;
set
{
if (value == null) this._ageString = "?";
else
{
this._ageString = value.ToString();
int.TryParse(value.ToString(), out this._age);
errorMessages[nameof(Age)] = this[nameof(Age)];
OnPropertyChanged();
}
}
}
private Dictionary<string, string> errorMessages = new Dictionary<string, string>();
public string Error
{
get
{
String result = String.Empty;
foreach (var item in errorMessages)
if (!string.IsNullOrEmpty(item.Value)) result += (string.IsNullOrEmpty(result)) ? item.Value : Environment.NewLine + item.Value;
return result;
}
}
public string this[string columnName]
{
get
{
string result = string.Empty;
switch (columnName)
{
case "Name": if (string.IsNullOrEmpty(Name)) result = "Name may not be null or empty"; break;
case "Age":
if (string.IsNullOrEmpty(this._ageString)) result = "Age may not be null or empty";
else if (this._age < 18 || this._age > 65) result = "Age must be beetween 18 an 65"; break;
};
return result;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class ValidateAll : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
BindingGroup bindingGroup = (BindingGroup)value;
ViewModel vm = (ViewModel)bindingGroup.Items[0];
if (!string.IsNullOrEmpty(vm.View.Error)) return new ValidationResult(false, vm.View.Error);
return ValidationResult.ValidResult;
}
}
public class StackPanelBehavior : Behavior<StackPanel>
{
protected override void OnAttached()
{
var vm = AssociatedObject.DataContext as ViewModel;
var bg = AssociatedObject.BindingGroup;
if (vm == null || bg == null) return;
vm.ValidateAllFields = bg;
}
}
public class RelayCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _action;
public RelayCommand(Action<object> action) { _action = action; _canExecute = null; }
public RelayCommand(Action<object> action, Predicate<object> canExecute) { _action = action; _canExecute = canExecute; }
public void Execute(object o) => _action(o);
public bool CanExecute(object o) => _canExecute == null ? true : _canExecute(o);
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}