Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data Validation in mvvm

I have a application with multiply ViewModels. Some property have DataAnnotations.

 [Required(ErrorMessage = "Field 'Range' is required.")]
    [Range(1, 10, ErrorMessage = "Field 'Range' is out of range.")]
    public int Password
    {
        get
        {
            return _password;
        }
        set
        {
            if (_password != value)
            {
                _password = value;
                RaisePropertyChanged("Password");
            }
        }
    }

How validation can be done by implementing the IDataErrorInfo or INotifyDataErrorInfo interface for all viewmodels?

I use This article, but validate when propertychange and don't validate required field.

like image 377
ar.gorgin Avatar asked Jul 16 '26 22:07

ar.gorgin


2 Answers

Here is a simple example which uses IDataErrorInfo. This should help you get started.

XAML:

<Grid>
    <Grid.Resources>
        <ControlTemplate x:Key="LeftErrorTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding AdornedElement.(Validation.Errors).[0].ErrorContent, ElementName=ErrorAdorner}" Background="Red" Foreground="White" FontWeight="Bold" VerticalAlignment="Center"/>
                <AdornedElementPlaceholder x:Name="ErrorAdorner">
                    <Border BorderBrush="Red" BorderThickness="1" />
                </AdornedElementPlaceholder>
            </StackPanel>
        </ControlTemplate>
    </Grid.Resources>
    <TextBlock Height="23" HorizontalAlignment="Left" Margin="158,66,0,0" Name="textBlock1" Text="Name" VerticalAlignment="Top" Width="44" />
    <TextBox Height="23" HorizontalAlignment="Left" Margin="217,65,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" 
             Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" 
             Validation.ErrorTemplate="{StaticResource LeftErrorTemplate}"/>
</Grid>

Code behind:

using System;
using System.Windows;
using System.ComponentModel;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var vm = new ViewModel();

            this.DataContext = vm;
        }
    }

    public class ViewModel : ObservableBase, IDataErrorInfo
    {
        private string _Name;

        public string Name
        {
            get { return _Name; }
            set
            {
                _Name = value;
                OnPropertyChanged("Name");
            }
        }

        public string Error
        {
            get { throw new NotImplementedException(); }
        }

        public string this[string columnName]
        {
            get
            {
                string errorMessage = string.Empty;

                switch (columnName)
                {
                    case "Name":
                        if (string.IsNullOrEmpty(Name))
                            errorMessage = "Enter name";
                        else if (Name.Trim() == string.Empty)
                            errorMessage = "Enter valid name";
                        break;
                }
                return errorMessage;
            }
        }

    }

    public class ObservableBase : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
like image 170
Anand Murali Avatar answered Jul 19 '26 13:07

Anand Murali


Hi you will have to create some method that Validates the annotations Like ValidateMethod in my code

public class ViewModel:INotifyPropertyChanged,IDataErrorInfo
    {
        int _password;

        [Required(ErrorMessage = "Field 'Range' is required.")]
        [Range(1, 10, ErrorMessage = "Field 'Range' is out of range.")]
        public int Password
        {
            get
            {
                return _password;
            }
            set
            {
                if (_password != value)
                {
                    _password = value;
                    Validate("Password", value);
                    Notify("Password");
                }
            }
        }

        private void Validate(string propertyName, object value)
        {
            if (string.IsNullOrEmpty(propertyName))
                throw new ArgumentNullException("propertyName");

            string error = string.Empty;

            var results = new List<System.ComponentModel.DataAnnotations.ValidationResult>(2);

            bool result = Validator.TryValidateProperty(
                value,
                new ValidationContext(this, null, null)
                {
                    MemberName = propertyName
                },
                results);

            if (!result && (value == null || ((value is int || value is long) && (int)value == 0) || (value is decimal && (decimal)value == 0)))
                return;

            if (!result)
            {
                System.ComponentModel.DataAnnotations.ValidationResult validationResult = results.First();
                if (!errorMessages.ContainsKey(propertyName))
                    errorMessages.Add(propertyName, validationResult.ErrorMessage);
            }

            else if (errorMessages.ContainsKey(propertyName))
                errorMessages.Remove(propertyName);
        }

        #region INotifyPropertyChanged 

        public void Notify(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));

        }
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region IDataErrorInfo

        public string Error
        {
            get { throw new NotImplementedException(); }
        }

        private Dictionary<string, string> errorMessages = new Dictionary<string, string>();

        public string this[string columnName]
        {
            get 
            { 
                if(errorMessages.ContainsKey(columnName))
                    return errorMessages[columnName];
                return null;

            }
        }

        #endregion
    }

>xaml

<TextBox Text="{Binding Password, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" Height="70" Width="200" />

xaml.cs

public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }

You need to call the Validate method from setter of the property which applies DataAnnotations and make sure call it before Notifying the PropertyChanged.

like image 30
yo chauhan Avatar answered Jul 19 '26 12:07

yo chauhan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!