WPF 的驗證可以透過ValidationRules 實現, 然而在實際情況中, 驗證除了Pass / Failure 外, 還可能會有severity level, warning 等. 可惜WPF 本身不支援. 若要加入的話, 最直接的方法只得從ViewModel 著手.
在示範中, 利用DataTrigger 來決定ViewModel 是否valid 及warning. 因為沒有利用WPF 內建的INotifyError 關係, 故直接不用直內置的Validation object.
MainWindow.xaml
<Window x:Class="PoC.WpfWarning.MainWindow"
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:PoC.WpfWarning"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:User />
</Window.DataContext>
<StackPanel>
<TextBlock Text="User Name" />
<TextBox Text="{Binding Name, Mode=TwoWay}" Validation.ErrorTemplate="{x:Null}">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding HasWarnings}" Value="True">
<Setter Property="Background" Value="Yellow" />
</DataTrigger>
<DataTrigger Binding="{Binding Errors}" Value="True">
<Setter Property="Background" Value="Pink" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBox />
</StackPanel>
</Window>
User.cs
public class User : ObservableObject
{
private string _name;
public string Name
{
get { return _name; }
set {
if (_name != value)
{
SetField(ref _name, value);
ValidateProperty(new NameValidor());
}
}
}
}
NameValidator.cs
public class NameValidor : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
ValidationResult result = new WarnedValidationResult(true, false, null);
User source = value as User;
if (source != null)
{
if (string.IsNullOrEmpty(source.Name))
result = new ValidationResult(false, "Name could not be empty.");
else if (source.Name == "aaa")
result = new WarnedValidationResult(true, true, "Message cannot be " + source.Name);
}
return result;
}
}
IDataWarningInfo.cs
public interface IDataWarningInfo
{
event EventHandler<DataErrorsChangedEventArgs> WarningsChanged;
System.Collections.IEnumerable GetWarnings(string propertyName);
bool HasWarnings { get; }
}
ObservableObject.cs
public class ObservableObject : INotifyPropertyChanged, INotifyDataErrorInfo, IDataWarningInfo
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
field = value;
OnPropertyChanged(propertyName);
return true;
}
#region Implementation of Interfaces.
protected Dictionary<string, ICollection<string>> _validationErrors = new Dictionary<string, ICollection<string>>();
protected Dictionary<string, ICollection<string>> _validationWarnings = new Dictionary<string, ICollection<string>>();
public bool HasErrors
{
get { return _errors; }
set { SetField(ref _errors, value); }
}
private bool _errors;
public bool Errors
{
get { return _errors; }
set { SetField(ref _errors, value); }
}
private bool _hasWarnings;
public bool HasWarnings
{
get { return _hasWarnings; }
set { SetField(ref _hasWarnings, value); }
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public event EventHandler<DataErrorsChangedEventArgs> WarningsChanged;
public virtual void OnErrorsChanged(string propertyName)
{
if (ErrorsChanged != null)
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
public virtual void OnWarningsChanged(string propertyName)
{
if (WarningsChanged != null)
WarningsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
protected void UpdateValidationErrors(string propertyName, ICollection<string> errorMessage)
{
if (errorMessage.Count > 0)
{
/* Update the collection in the dictionary returned by the GetErrors method */
_validationErrors[propertyName] = errorMessage;
Errors = true;
}
else if (_validationErrors.ContainsKey(propertyName))
{
/* Remove all errors for this property */
_validationErrors.Remove(propertyName);
Errors = false;
}
/* Raise event to tell WPF to execute the GetErrors method */
OnErrorsChanged(propertyName);
OnPropertyChanged(propertyName);
}
public IEnumerable GetWarnings(string propertyName)
{
if (string.IsNullOrEmpty(propertyName) || !_validationWarnings.ContainsKey(propertyName))
return null;
return _validationWarnings[propertyName];
}
protected void UpdateValidationWarnings(string propertyName, ICollection<string> warningMessage)
{
if (warningMessage.Count > 0)
{
/* Update the collection in the dictionary returned by the GetErrors method */
_validationWarnings[propertyName] = warningMessage;
HasWarnings = true;
}
else if (_validationWarnings.ContainsKey(propertyName))
{
/* Remove all errors for this property */
_validationWarnings.Remove(propertyName);
HasWarnings = false;
}
/* Raise event to tell WPF to execute the GetErrors method */
OnWarningsChanged(propertyName);
OnPropertyChanged(propertyName);
}
IEnumerable INotifyDataErrorInfo.GetErrors(string propertyName)
{
if (string.IsNullOrEmpty(propertyName) || !_validationErrors.ContainsKey(propertyName))
return null;
return _validationErrors[propertyName];
}
#endregion
public bool ValidateProperty(ValidationRule validator, [CallerMemberName] string propertyName = null)
{
/* Call service asynchronously */
bool result = false;
ValidationResult validationResult = validator.Validate(this, null);
if (validationResult != null)
{
ICollection<string> validationErrors = new List<string>();
ICollection<string> warningMessages = new List<string>();
if (validationResult.IsValid == false)
{
validationErrors.Add(validationResult.ErrorContent.ToString());
result = validationResult.IsValid;
}
else if (validationResult is WarnedValidationResult)
{
WarnedValidationResult warnedValidationResult = validationResult as WarnedValidationResult;
if (warnedValidationResult.IsWarned)
{
warningMessages.Add(warnedValidationResult.ErrorContent.ToString());
result = false;
}
else
{
result = true;
}
}
UpdateValidationErrors(propertyName, validationErrors);
UpdateValidationWarnings(propertyName, warningMessages);
}
return result;
}
}
Leave a Reply