Quantcast
Channel: Windows Presentation Foundation (WPF) forum
Viewing all articles
Browse latest Browse all 18858

Duplication when using INotifyPropertyChanged and IDataErrorInfo

$
0
0

Hi all,

This is related to an earlier post, but this provides much more information, so I changed that do a discussion.

I have some sample code I downloaded from http://www.codeproject.com/Articles/14178/Delegates-and-Business-Objects - an article by Paul Stovell.  You can see his logic and explanations without having to download the code.  I am trying to implement this approach in my WPF application.

The code appears to be quite simple, but when I run his test program, the code that checks the rules is run repeatedly (about 8-11 times before the form is even shown and then at least 2 times every time a field is changed. 

The code is supposed to only fire for the changed property, but all properties are evaluated (twice) each time any field is modified.

I added a Debug.WriteLine statement to the "public virtual string this[string propertyName]"property to track how frequently the validations are applied.  I am trying to figure out:

1) Why is the same get being called many times, even when properties are specified

2) What to change so that the aforementioned get is only called for the actual property that has changed.

3) I have a version where I have added code that checks to see if we are in an update notification state and, if so, checks to see if the property has been evaluated or not, then logs the property so it won't be checked again (until the next notifyChanged is called.  But I have to think there is a simpler approach to prevent the large number of duplicate validations to run.

Here is the main code that handles the notification:

[Serializable()]
public abstract class DomainObject:  
    INotifyPropertyChanged,
    IDataErrorInfo {
    private List<Rule> _rules;

    /// <summary>
    /// Constructor.
    /// </summary>
    public DomainObject() {
    }

    /// <summary>
    /// Gets a value indicating whether or not this domain object is valid. 
    /// </summary>
    public virtual bool IsValid {
        get {
            return this.Error == null;
        }
    }

    /// <summary>
    /// Gets an error message indicating what is wrong with this domain object. The default is an empty string ("").
    /// </summary>
    public virtual string Error {
        get {
            string result = this[string.Empty];
            if (result != null && result.Trim().Length == 0) {
                result = null;
            }
            return result;
        }
    }

    /// <summary>
    /// Gets the error message for the property with the given name.
    /// </summary>
    /// <param name="propertyName">The name of the property whose error message to get.</param>
    /// <returns>The error message for the property. The default is an empty string ("").</returns>
    public virtual string this[string propertyName] {
        get {

					Debug.WriteLine("*******  Processing: " + propertyName);
            string result = string.Empty;

            propertyName = CleanString(propertyName);

            foreach (Rule r in GetBrokenRules(propertyName)) {
                if (propertyName == string.Empty || r.PropertyName == propertyName) {
                    result += r.Description;
                    result += Environment.NewLine;
                }
            }
            result = result.Trim();
            if (result.Length == 0) {
                result = null;
            }
            return result;
        }
    }

    /// <summary>
    /// Validates all rules on this domain object, returning a list of the broken rules.
    /// </summary>
    /// <returns>A read-only collection of rules that have been broken.</returns>
    public virtual ReadOnlyCollection<Rule> GetBrokenRules() {
        return GetBrokenRules(string.Empty);
    }

    /// <summary>
    /// Validates all rules on this domain object for a given property, returning a list of the broken rules.
    /// </summary>
    /// <param name="property">The name of the property to check for. If null or empty, all rules will be checked.</param>
    /// <returns>A read-only collection of rules that have been broken.</returns>
    public virtual ReadOnlyCollection<Rule> GetBrokenRules(string property) {
        property = CleanString(property);
        // If we haven't yet created the rules, create them now.
        if (_rules == null) {
            _rules = new List<Rule>();
            _rules.AddRange(this.CreateRules());
        }
        List<Rule> broken = new List<Rule>();

            
        foreach (Rule r in this._rules) {
            // Ensure we only validate a rule 
            if (r.PropertyName == property || property == string.Empty) {
                bool isRuleBroken = !r.ValidateRule(this);
                Debug.WriteLine(DateTime.Now.ToLongTimeString()+ ": Validating the rule: '" + r.ToString() + "' on object '" + this.ToString() + "'. Result = " + ((isRuleBroken == false) ? "Valid" : "Broken"));
                if (isRuleBroken) {
                    broken.Add(r);
                }
            }
        }

        return broken.AsReadOnly();
    }

    /// <summary>
    /// Occurs when any properties are changed on this object.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Override this method to create your own rules to validate this business object. These rules must all be met before 
    /// the business object is considered valid enough to save to the data store.
    /// </summary>
    /// <returns>A collection of rules to add for this business object.</returns>
    protected virtual List<Rule> CreateRules() {
        return new List<Rule>();
    }

    /// <summary>
    /// A helper method that raises the PropertyChanged event for a property.
    /// </summary>
    /// <param name="propertyNames">The names of the properties that changed.</param>
    protected virtual void NotifyChanged(params string[] propertyNames) {
        foreach (string name in propertyNames) {
            OnPropertyChanged(new PropertyChangedEventArgs(name));
        }
        OnPropertyChanged(new PropertyChangedEventArgs("IsValid"));
    }

    /// <summary>
    /// Cleans a string by ensuring it isn't null and trimming it.
    /// </summary>
    /// <param name="s">The string to clean.</param>
    protected string CleanString(string s) {
        return (s ?? string.Empty).Trim();
    }

    /// <summary>
    /// Raises the PropertyChanged event.
    /// </summary>
    /// <param name="e">Event arguments.</param>
    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
        if (this.PropertyChanged != null) {
            this.PropertyChanged(this, e);
        }
    }
}
Thank you!

 


me (and yes, I DO mark correct answers)


Viewing all articles
Browse latest Browse all 18858

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>