Management and Validation of Data: the PropertyCollection class

This is the second of three articles about the management and validation of data in a graphical user interface. Within this article series I want to introduce a data class which can manage a single value, a data collection based on this data class and an example how to use these classes within a WPF user interface.

In the first article we have developed a data class which offers data management and data validation. This class is designed to manage a single value. Of course you will nearly never work with single values. Instead you want to create data structures with all properties belonging together. Therefore we will now develop a PropertyCollection class.

 
Requirements for the PropertyCollection class

The PropertyCollection class should have the same features like the Property class because the same data management and validation is needed, independent if you manage one single values or a set of related values.

So we want a data management with the possibility to detect changes and undo changes. Furthermore the class should have functions to check whether the value is valid or not and to get detailed information about validation errors. According to the validation of single values, it should also possible to validate properties which belong together.

The PropertyCollection class should store the properties in a dictionary. So each single value can be managed by a name.

 
PropertyCollection class Interface

According to the requirements we can define an interface for the PropertyCollection class. As we thought, there should be no difference whether we use a single value or a collection. As a result the PropertyCollection interface contains the same properties and functions like the interface for the Property class.

using System.Collections.ObjectModel;

namespace DataDomain
{
    public interface IPropertyCollection
    {
        bool IsChanged { get; }
        void RevertChanges();
        void AcceptChanges();

        bool IsValid { get; }
        ObservableCollection<string> ValidationErrors { get; }
    }
}

 

The functions IsChanged, RevertChanges and AcceptChanges are used to see and undo changes. For the data validation we will have the properties IsValid and ValidationErrors.

 
Member

The PropertyCollection class does not need member variables to store the properties because we can create a class, based on a dictionary. So we directly use a dictionary and expand it with the needed management and validation features.

As we want an additional possibility to validate properties which belong together, we have to add a new validation function. So it is possible to validate the properties independent or together. To store the additional validator the PropertyCollection class needs an according member variable. The following source code shows the class, based on a dictionary and with the needed member to store the validator.

using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System;

namespace DataDomain
{
    public class PropertyCollection : Dictionary<string, IProperty>, IPropertyCollection
    {
        #region Member

        private Func<PropertyCollection, ObservableCollection<string>> _validator;

        #endregion
    }
}

 
ctor

The PropertyCollection class will get two constructors: an empty one and an additional one with the optional validator function.

using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System;

namespace DataDomain
{
    public class PropertyCollection : Dictionary<string, IProperty>, IPropertyCollection
    {
        #region ctor

        /// <summary>
        /// standard constrcutor
        /// </summary>
        public PropertyCollection()
        {
        }

        /// <summary>
        /// constructor with addidional validation checker to check conditions which affects several properties together
        /// </summary>
        public PropertyCollection(
            Func<PropertyCollection, ObservableCollection<string>> validator)
            : this()
        {
            _validator = validator;
        }

        #endregion
    }
}

 
Data management

The data management should contain functions to check for changes, reset changes and commit changes. Furthermore I have implemented a getter for a single property. This getter will convert the property in the according generic Property class type.

using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System;

namespace DataDomain
{
    public class PropertyCollection : Dictionary<string, IProperty>, IPropertyCollection
    {
        #region Value management

        /// <summary>
        /// get the property
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public Property<T> GetProperty<T>(string key)
        {
            return (Property<T>)this[key];
        }

        /// <summary>
        /// get whether a member of the he collection was changed
        /// </summary>
        public bool IsChanged
        {
            get
            {
                return (this.Any(x => x.Value.IsChanged));
            }
        }

        /// <summary>
        /// reverts all changes of all elements to the last values, set with 'AcceptChanges'
        /// </summary>
        public void RevertChanges()
        {
            foreach (IProperty property in this.Values)
            {
                property.RevertChanges();
            }
        }

        /// <summary>
        /// accepts all changes, this will set the new value for a revert
        /// </summary>
        public void AcceptChanges()
        {
            foreach (IProperty property in this.Values)
            {
                property.AcceptChanges();
            }
        }

        #endregion        
    }
}

 
Validation

Most of the previous functions have used the functions of the base Property class. But the validation function is more complex because we have the additional validator. So a combination of the validation results of each single value and the validation results of the collection is necessary.

using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System;

namespace DataDomain
{
    public class PropertyCollection : Dictionary<string, IProperty>, IPropertyCollection
    {
        #region Validation

        /// <summary>
        /// get whether the value is valid
        /// </summary>
        public bool IsValid
        {
            get
            {
                return !(this.Any(x => !x.Value.IsValid));
            }
        }

        /// <summary>
        /// get validation errors
        /// </summary>
        public ObservableCollection<string> ValidationErrors
        {
            get
            {
                ObservableCollection<string> errors = null;
                ObservableCollection<string> collectionErrors = null;

                //check all properties
                foreach (IProperty property in this.Values)
                {
                    if (property.IsValid == false)
                    {
                        if (errors == null)
                        {
                            errors = new ObservableCollection<string>();
                        }

                        foreach (string error in property.ValidationErrors)
                        {
                            errors.Add(error);
                        }
                    }
                }

                //check collection
                if (_validator != null)
                {
                    collectionErrors = _validator(this);

                    if (collectionErrors != null)
                    {
                        if (collectionErrors.Count > 0)
                        {
                            if (errors == null)
                            {
                                errors = new ObservableCollection<string>();
                            }

                            foreach (string error in collectionErrors)
                            {
                                errors.Add(error);
                            }
                        }
                    }
                }

                return errors;
            }
        }

        #endregion
    }
}

 
PropertyCollection class

With the implementation of the validation functionality the PropertyCollection class is finished. The following code shows the full class. You may use it in your projects and of course feel free to adapt it to your own needs.

using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System;

namespace DataDomain
{
    public class PropertyCollection : Dictionary<string, IProperty>, IPropertyCollection
    {
        #region ctor

        /// <summary>
        /// standard constrcutor
        /// </summary>
        public PropertyCollection()
        {
        }

        /// <summary>
        /// constructor with addidional validation checker to check conditions which affects several properties together
        /// </summary>
        public PropertyCollection(
            Func<PropertyCollection, ObservableCollection<string>> validator)
            : this()
        {
            _validator = validator;
        }

        #endregion

        #region Value management

        /// <summary>
        /// get the property
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public Property<T> GetProperty<T>(string key)
        {
            return (Property<T>)this[key];
        }

        /// <summary>
        /// get whether a member of the he collection was changed
        /// </summary>
        public bool IsChanged
        {
            get
            {
                return (this.Any(x => x.Value.IsChanged));
            }
        }

        /// <summary>
        /// reverts all changes of all elements to the last values, set with 'AcceptChanges'
        /// </summary>
        public void RevertChanges()
        {
            foreach (IProperty property in this.Values)
            {
                property.RevertChanges();
            }
        }

        /// <summary>
        /// accepts all changes, this will set the new value for a revert
        /// </summary>
        public void AcceptChanges()
        {
            foreach (IProperty property in this.Values)
            {
                property.AcceptChanges();
            }
        }

        #endregion

        #region Validation

        /// <summary>
        /// get whether the value is valid
        /// </summary>
        public bool IsValid
        {
            get
            {
                return !(this.Any(x => !x.Value.IsValid));
            }
        }

        /// <summary>
        /// get validation errors
        /// </summary>
        public ObservableCollection<string> ValidationErrors
        {
            get
            {
                ObservableCollection<string> errors = null;
                ObservableCollection<string> collectionErrors = null;

                //check all properties
                foreach (IProperty property in this.Values)
                {
                    if (property.IsValid == false)
                    {
                        if (errors == null)
                        {
                            errors = new ObservableCollection<string>();
                        }

                        foreach (string error in property.ValidationErrors)
                        {
                            errors.Add(error);
                        }
                    }
                }

                //check collection
                if (_validator != null)
                {
                    collectionErrors = _validator(this);

                    if (collectionErrors != null)
                    {
                        if (collectionErrors.Count > 0)
                        {
                            if (errors == null)
                            {
                                errors = new ObservableCollection<string>();
                            }

                            foreach (string error in collectionErrors)
                            {
                                errors.Add(error);
                            }
                        }
                    }
                }

                return errors;
            }
        }

        #endregion

        #region Member

        private Func<PropertyCollection, ObservableCollection<string>> _validator;

        #endregion
    }
}
Werbung
Dieser Beitrag wurde unter .NET, C# veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Eine Antwort zu Management and Validation of Data: the PropertyCollection class

  1. Pingback: Management and Validation of Data: the User Interface | coders corner

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s