AfDs
AfDs

Reputation: 23

Call validation on when window loads, WPF using INotifyDataErrorInfo

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

Answers (1)

mm8
mm8

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

Related Questions