Liens
ValidatesOnExceptions
Le Binding catch les exceptions, il est donc possible de lancer une exception si la valeur n'est pas valide.
|
<TextBox Text="{Binding InputValue, ValidatesOnExceptions=True, UpdateSourceTrigger=PropertyChanged}">
|
|
private string _inputValue;
public string InputValue
{
get => _inputValue;
set
{
if(value.Contains("x"))
{
throw new ArgumentException("Message d'ereeur.");
}
_inputValue= value;
}
}
|
Classe ValidationRule
La validation est réalisée avant l'affectation de la valeur, celle-ci n'est setée que si elle est valide.
Desavantage: la validation se fait uniquement sur la valeur, le context n'est pas accessible.
Les paramètres ne peuvent être bindés car ce ne sont pas des dependancy properties, leurs valeurs sont donc statiques.
|
public class MyValidationRule : ValidationRule
{
public string ForbiddenValue { get; set; }
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (ForbiddenValue == null)
return ValidationResult.ValidResult;
if (value is string input && !input.Contains("x"))
{
return ValidationResult.ValidResult;
}
return new ValidationResult(false, "Error message.");
}
}
|
|
<UserControl xmlns:local="clr-namespace:Main.Views">
<TextBox>
<TextBox.Text>
<Binding>
<Binding.ValidationRules>
<local:MyValidationRule ForbiddenValue="x" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
|
ExceptionValidationRule
|
<TextBox>
<TextBox.Text>
<Binding>
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
|
IDataErrorInfo
Désaventage: la valeur est setée avant la validation.
|
<TextBox Text="{Binding InputValue, ValidatesOnDataErrors=True}" />
|
|
public class MyViewModel : ViewModelBase, IDataErrorInfo
{
public string Error => string.Empty;
public string this[string propertyName] => GetErrorForProperty(propertyName);
private string GetErrorForProperty(string propertyName)
{
switch (propertyName)
{
case nameof(InputValue):
{
if (InputValue.Contains("x"))
{
// si le retour est différent de null ou vide
// le programme considère qu'il y a une erreur.
return "Error message.";
}
return string.Empty;
}
default:
return string.Empty;
}
|
INotifyDataErrorInfo
Même concept qu'IDataErrorInfo avec une validation asynchrone.
Permet aussi de renvoyer plusieurs erreurs pour une seule propriété.
|
<TextBox Text="{Binding InputValue, ValidatesOnNotifyDataErrors=True}" />
|
|
public class MyViewModel : ViewModelBase, INotifyDataErrorInfo
{
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public IEnumerable GetErrors(string propertyName)
{
lock (_propertyErrors)
{
if (_propertyErrors.ContainsKey(propertyName))
return _propertyErrors[propertyName];
}
return null;
}
public bool HasErrors => _propertyErrors.Values.Any(errors => errors != null);
private readonly Dictionary<string, IList<string>> _propertyErrors = new Dictionary<string, IList<string>>();
private string _inputValue;
public string InputValue
{
get => _inputValue;
set
{
SetProperty(ref _inputValue, value);
ValidateAsync(InputValue).ContinueWith((errorTasks) =>
{
lock (_propertyErrors)
{
_propertyErrors[nameof(InputValue)] = errorTasks.Result;
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(InputValue)));
}
});
}
}
private async Task<IList<string>> ValidateAsync(string inputValue)
{
await Task.Run(() =>
{
Thread.Sleep(4000);
});
return inputValue.Contains("y") ? new List<string> { "Error message." } : null;
}
|
Event
Lève un évenement en cas d'erreur de validation.
|
<TextBox Text="{Binding InputValue, NotifyOnValidationError=True}"
Validation.Error="Validation_OnError" />
|
|
private void Validation_OnError(object sender, ValidationErrorEventArgs e)
{
Debug.WriteLine(e.Error.ErrorContent);
}
|
Style
|
<Style x:Key="ErrorStyle" TargetType="FrameworkElement">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
|
ErrorTemplate
Change la manière dont est signalé une erreur.
|
<ControlTemplate x:Key="TextBoxErrorTemplate">
<DockPanel>
<Ellipse DockPanel.Dock="Right"
Margin="2,0"
ToolTip="Invalid data"
Width="10"
Height="10">
<Ellipse.Fill>
<LinearGradientBrush>
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="Red" 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"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
|
DataAnnotation
Ajouter la référence System.ComponentModel.DataAnnotations
ValidationException
MyViewModel.cs
|
public class MyViewModel : BindableBase
{
private string _inputValue;
[Required(AllowEmptyStrings = false, ErrorMessage = "InputValue is required")]
public string InputValue
{
get => _inputValue;
set
{
ValidateProperty(nameof(InputValue), value);
SetProperty(ref _inputValue, value);
}
}
private void ValidateProperty(string propertyName, object value)
{
var context = new ValidationContext(this);
context.MemberName = propertyName;
Validator.ValidateProperty(value, context);
}
|
IDataErrorInfo
MyViewModel.cs
|
public class MyViewModel : BindableBase, IDataErrorInfo
{
private string _inputValue;
[Required(AllowEmptyStrings = false, ErrorMessage = "InputValue is required")]
public string InputValue
{
get => _inputValue;
set => SetProperty(ref _inputValue, value);
}
public string Error => string.Empty;
public string this[string propertyName] => GetErrorForProperty(propertyName);
private string GetErrorForProperty(string propertyName)
{
var validationResults = ValidateProperty(propertyName, GetType().GetProperty(propertyName)?.GetValue(this));
return validationResults.FirstOrDefault()?.ErrorMessage;
}
private IList<ValidationResult> ValidateProperty(string propertyName, object value)
{
var context = new ValidationContext(this);
context.MemberName = propertyName;
var validationResults = new List<ValidationResult>();
Validator.TryValidateProperty(value, context, validationResults);
return validationResults;
}
|
INotifyDataErrorInfo
MyViewModel.cs
|
public class MyViewModel : BindableBase, INotifyDataErrorInfo
{
private string _inputValue;
[Required(AllowEmptyStrings = false, ErrorMessage = "InputValue is required")]
public string InputValue
{
get => _inputValue;
set
{
ValidateProperty(nameof(InputValue), value);
SetProperty(ref _inputValue, value);
}
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public IEnumerable GetErrors(string propertyName)
{
lock (_propertyErrors)
{
if (_propertyErrors.ContainsKey(propertyName))
return _propertyErrors[propertyName];
}
return null;
}
public bool HasErrors => _propertyErrors.Values.Any(errors => errors != null);
private readonly Dictionary<string, IList<string>> _propertyErrors = new Dictionary<string, IList<string>>();
private void ValidateProperty(string propertyName, object value)
{
var context = new ValidationContext(this);
context.MemberName = propertyName;
var validationResults = new List<ValidationResult>();
Validator.TryValidateProperty(value, context, validationResults);
lock (_propertyErrors)
{
_propertyErrors[propertyName] = validationResults.Select(vr => vr.ErrorMessage).ToList();
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
}
|
Validation d'une fenêtre avec un bouton
|
<TextBox Text="{Binding Path=..., UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=true, NotifyOnValidationError=true}"
Validation.Error="Validation_Error" />
<Button Command="{x:Static views:ReportSaver.SaveCmd}">OK</Button>
|
|
public static RoutedCommand SaveCmd = new RoutedCommand();
private void ExecutedSaveCmd(object sender, ExecutedRoutedEventArgs e)
{
this.DialogResult = true;
this.Close();
}
private void CanExecuteSaveCmd(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = _noOfErrorsOnScreen == 0;
e.Handled = true;
}
private int _noOfErrorsOnScreen = 0;
private void Validation_Error(object sender, ValidationErrorEventArgs e)
{
if (e.Action == ValidationErrorEventAction.Added)
_noOfErrorsOnScreen++;
else
_noOfErrorsOnScreen--;
}
|
Permet d'avoir le visuel de validation dans un control parent.