How can I validate multiple properties when any of them changes?

霸气de小男生 提交于 2019-11-28 06:24:19
Rachel

The simplest way is to raise a PropertyChanged notification for in the setter for both properties that need to be validated like bathineni suggests

private DateTime StartDate
{
    get { return _startDate; }
    set
    {
        if (_startDate != value)
        {
            _startDate = value;
            RaisePropertyChanged("StartDate");
            RaisePropertyChanged("EndDate");
        }
    }
}

private DateTime EndDate
{
    get { return _endDate; }
    set
    {
        if (_endDate!= value)
        {
            _endDate= value;
            RaisePropertyChanged("StartDate");
            RaisePropertyChanged("EndDate");
        }
    }
}

However if that doesn't work for you, I figured out one way to validate a group of properties together, although your classes have to implement INotifyPropertyChanging in addition to INotifyPropertyChanged (I'm using EntityFramework and by default their classes implement both interfaces)

Extension Method

public static class ValidationGroup
{
    public delegate string ValidationDelegate(string propertyName);
    public delegate void PropertyChangedDelegate(string propertyName);

    public static void AddValidationGroup<T>(this T obj, 
        List<string> validationGroup, bool validationFlag,
        ValidationDelegate validationDelegate, 
        PropertyChangedDelegate propertyChangedDelegate)
        where T : INotifyPropertyChanged, INotifyPropertyChanging
    {

        // This delegate runs before a PropertyChanged event. If the property
        // being changed exists within the Validation Group, check for validation
        // errors on the other fields in the group. If there is an error with one
        // of them, set a flag to true.
        obj.PropertyChanging += delegate(object sender, PropertyChangingEventArgs e)
        {
            if (validationGroup.Contains(e.PropertyName))
            {
                foreach(var property in validationGroup)
                {
                    if (validationDelegate(property) != null)
                    {
                        validationFlag = true;
                        break;
                    }
                }
            }
        };

        // After the Property gets changed, if another field in this group was
        // invalid prior to the change, then raise the PropertyChanged event for 
        // all other fields in the Validation Group to update them.
        // Also turn flag off so it doesn't get stuck in an infinite loop
        obj.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
        {
            if (validationGroup.Contains(e.PropertyName))
            {
                if (validationFlag && validationDelegate(e.PropertyName) == null)
                {
                    validationFlag = false;
                    foreach(var property in validationGroup)
                    {
                        propertyChangedDelegate(property);
                    }
                }
            }
        };
    }
}

To use it, add the following call to the constructor of any class that should validate a group of properties together.

this.AddValidationGroup(
    new List<string> { "StartDate", "EndDate" },
    GetValidationError, OnPropertyChanged);

I've tested this with up to 3 properties in a Validation Group and it seems to work OK.

Use this trick, it prevents they call OnPropertyChanged each other :

private bool RPCfromStartDate = false;
private bool RPCfromEndDate = false;

public string this[string columnName]
{
    get 
    {
                string result = null;
                switch (columnName)
                {
                    case "StartDate":
                        if (StartDate.Date >= EndDate.Date)
                        {
                            result = "Start Date cannot be later than End Date";
                        }
                        if (!RPCfromEndDate)
                        {
                            RPCfromStartDate = true;                            
                            OnPropertyChanged("EndDate");
                            RPCfromStartDate = false;
                        } 
                    case "EndDate":
                        if (StartDate.Date >= EndDate.Date)
                        {
                            result = "End Date cannot be earlier than Start Date";
                        }
                        if (!RPCfromStartDate)
                        {
                            RPCfromEndDate = true;                            
                            OnPropertyChanged("StartDate");
                            RPCfromEndDate = false;
                        } 
                        break;
                }
...
CodeWarrior

I generally add all of my validation errors to a dictionary, and have the validation template subscribe to that via the property name. In each property changed event handler, I can check any number of properties and add or remove their validation status as necessary.

Check this answer for how my implementation looks. Sorry it is in VB.NET, but should be fairly straightforward.

You can also subscribe to the SelectedDateChanged event handler and update needed binding.

BindingExpression expression = datePickerStartDate.GetBindingExpression(DatePicker.SelectedDateProperty);
if (expression != null)
{
    expression.UpdateSource();
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!