Reputation: 23
I have model named EditableSong
which is derived from ValidatableModel
Class which
implements INotifyPropertyChanged
and INotifyDataErrorInfo
.
class EditableSong : ValidatableModel
{
CommandRelay addcommand = null;
public ICommand AddCommand
{
get { return addcommand; }
}
public EditableSong()
{
addcommand = new CommandRelay(Add, CanAdd);
}
private void Add(object obj = null)
{
MessageBox.Show("Succesfully Added!");
}
private bool CanAdd(object obj = null)
{
return !HasErrors;
}
private string title;
[Required]
[MaxLength(45)]
public string Title
{
get { return title; }
set
{ SetProperty(ref title, value); }
}
private string lyrics;
[MaxLength(3000)]
public string Lyrics
{
get { return lyrics; }
set { SetProperty(ref lyrics, value); }
}
private string artist;
[Required]
public string Artist
{
get { return artist; }
set {SetProperty(ref artist, value); }
}
}
And here is ValidatableModel
class:
class ValidatableModel : BindableBase, INotifyDataErrorInfo
{
private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
public bool HasErrors
{
get { return _errors.Count >0; ; }
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
protected override void SetProperty<T>(ref T member, T val,
[CallerMemberName] string propertyName = null)
{
base.SetProperty(ref member, val, propertyName);
ValidateProperty(propertyName, val);
}
public IEnumerable GetErrors(string propertyName)
{
if (_errors.ContainsKey(propertyName))
return _errors[propertyName];
else
return null;
}
protected void ValidateProperty<T>(string propertyName, T value)
{
var results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(this);
context.MemberName = propertyName;
Validator.TryValidateProperty(value, context, results);
if (results.Any())
{
_errors[propertyName] = results.Select(c => c.ErrorMessage).ToList();
}
else
{
_errors.Remove(propertyName);
}
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
protected void OnErrorsChanged(string propName)
{
ErrorsChanged(this, new DataErrorsChangedEventArgs(propName));
}
}`
And it works well, but only after I change properties in textboxes of my window. The main problem is that user mustn't be able to save model without filling required fields, but when windows loads button Save (which uses command) available, because of validation doesn't run.
Here is xaml:
<Window x:Class="ValidationTests.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:ValidationTests"
xmlns:rules="clr-namespace:ValidationTests.Rules"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<Style x:Key="TextBoxError" TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate x:Name="TextErrorTemplate">
<DockPanel LastChildFill="True">
<AdornedElementPlaceholder>
<Border BorderBrush="Red" BorderThickness="2"/>
</AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Label>Artist</Label>
<TextBox Style="{StaticResource TextBoxError}" Text="{Binding Artist,
ValidatesOnNotifyDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
<Label>Title</Label>
<TextBox Style="{StaticResource TextBoxError}" Text="{Binding Title,
ValidatesOnNotifyDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Label>Lyrics</Label>
<TextBox Style="{StaticResource TextBoxError}" Text="{Binding Lyrics,
ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Button Content="Add" Command="{Binding AddCommand}"></Button>
</StackPanel>
</Grid>
I wonder how can i fix this...
Upvotes: 2
Views: 594
Reputation: 169420
I wonder how can i fix this...
You need to perform the actual validation upfront.
Call the ValidateProperty
method for all properties in the constructor of your EditableSong
class to populate the Dictionary
and raise the ErrorsChanged
event:
public EditableSong()
{
addcommand = new CommandRelay(Add, CanAdd);
ValidateProperty(nameof(Title), title);
ValidateProperty(nameof(Lyrics), lyrics);
ValidateProperty(nameof(Artist), artist);
}
Upvotes: 1