WPF Validation - Using INotifyDataErrorInfo | .NET Land (kmatyaszek.github.io)

WPF Validation - Using INotifyDataErrorInfo | .NET Land (kmatyaszek.github.io)

In the .NET 4.5 was introduced new interface INotifyDataErrorInfo which enables data entity classes to implement custom validation rules and expose validation results asynchronously. This interface has three members:

  • HasErrors property indicates whether there are any validation errors
  • GetErrors method returns an IEnumerable that contains validation errors for the specified property (when the propertyName parameter isn’t equal to null or empty string) or for the entire entity (when the propertyName parameter is equal to null or empty string)
  • ErrorsChanged event must occur when the validation errors have changed for a property or for the entity

As GetErrors returns IEnumerable you can return multiple errors per property. Also, you can return custom error object (this is not possible using IDataErrorInfo interface). As you can see this new interface provides more flexible validation model in WPF. By default, ValidatesOnNotifyDataErrors is set to true so you don’t have to set this explicit in the binding expression. Below you can see the result of my example program (as you can see TextBox has two errors).

Below you can see an example implementation of the INotifyDataErrorInfo:

public class MainViewModel : BindableBase, INotifyDataErrorInfo
{
    private string _userName;
    private readonly Dictionary<string, List<string>> _errorsByPropertyName = new Dictionary<string, List<string>>();

    public MainViewModel()
    {
        UserName = null;
    }

    public string UserName
    {
        get => _userName;
        set
        {
            _userName = value;
            ValidateUserName();
            RaisePropertyChanged();
        }
    }

    public bool HasErrors => _errorsByPropertyName.Any();

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public IEnumerable GetErrors(string propertyName)
    {
        return _errorsByPropertyName.ContainsKey(propertyName) ?
            _errorsByPropertyName[propertyName] : null;
    }

    private void OnErrorsChanged(string propertyName)
    {
        ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
    }

    private void ValidateUserName()
    {
        ClearErrors(nameof(UserName));
        if (string.IsNullOrWhiteSpace(UserName))
            AddError(nameof(UserName), "Username cannot be empty.");
        if (string.Equals(UserName, "Admin", StringComparison.OrdinalIgnoreCase))
            AddError(nameof(UserName), "Admin is not valid username.");
        if (UserName == null || UserName?.Length <= 5)
            AddError(nameof(UserName), "Username must be at least 6 characters long.");
    }

    private void AddError(string propertyName, string error)
    {
        if (!_errorsByPropertyName.ContainsKey(propertyName))
            _errorsByPropertyName[propertyName] = new List<string>();

        if (!_errorsByPropertyName[propertyName].Contains(error))
        {
            _errorsByPropertyName[propertyName].Add(error);
            OnErrorsChanged(propertyName);
        }
    }

    private void ClearErrors(string propertyName)
    {
        if (_errorsByPropertyName.ContainsKey(propertyName))
        {
            _errorsByPropertyName.Remove(propertyName);
            OnErrorsChanged(propertyName);
        }
    }
}

And below snippet of code shows you how you can show to user multiple errors to single property:

<TextBox Text="{Binding UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}">
    <Validation.ErrorTemplate>
        <ControlTemplate>
            <StackPanel>
                <AdornedElementPlaceholder x:Name="textBox" />
                <ItemsControl ItemsSource="{Binding}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding ErrorContent}" Foreground="Red" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </ControlTemplate>
    </Validation.ErrorTemplate>
</TextBox>

A complete example is available on the following link: https://github.com/kmatyaszek/WPFValidationINotifyDataErrorInfo

 

WPF validation using INotifyDataErrorInfo - Dot Net For All

In this article I will discuss about INotifyDataErrorInfo which is used to validate the data asynchronously in WPF. In my previous articles I have discussed about the ways to validate data entered in the UI.

Please find the previous articles link below

Validation using INotifyDataErrorInfo

  • Added in .NET 4.5 for WPF
  • It works on the same principle as IDataErrorInfo but it is based on the event driven mechanism which notifies for the validation errors asynchronously based on the input from some time consuming operation like service call. It will wait for the results to come back and eventually it will raise the event.
  • Set ValidatesOnNotifyDataErrors=True on binding.
  • Binding will call the GetErrors method of the INotifyDataErrorInfo when the property is set in the ViewModel through view.
  • Binding also subscribes to the ErrorsChanged event in the interface.
  • If the ErrorsChanged event is raised, it will go and re query the GetErrors method for the property for which the event is raised.
  • To implement this we need to manage a dictionary of errors per property as part of error.
  • One of the advantage of this over IDataErrorInfo is that we can return multiple errors per property.

 

The interface has three members which the derived class need to implement. Whenever we are setting this validation, the HasErrors property is called by the binding which in turn calls the GetErrors method to return the error if the HasErrors return true. After some point when GetErrors is called the ErrorsChanged event is raised and it again calls GetErrors method. It is assuming that there are some asynchronous operation that filled the collection on which the GetErrors is working on.

As we can see the GetErrors method return the IEnumerable object. It means that it is working on some collection of the objects to get the errors.

 

作者:Chuck Lu    GitHub    
posted @   ChuckLu  阅读(136)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2022-03-09 jQuery if checkbox is checked
2021-03-09 Using AntiXss As The Default Encoder For ASP.NET
2021-03-09 What is the difference between AntiXss.HtmlEncode and HttpUtility.HtmlEncode?
2021-03-09 Should I use .done() and .fail() for new jQuery AJAX code instead of success and error
2019-03-09 Inversion of Control Containers and the Dependency Injection pattern
2019-03-09 82. Remove Duplicates from Sorted List II
2019-03-09 83. Remove Duplicates from Sorted List
点击右上角即可分享
微信分享提示