Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

ButtonBar Control using .NET

, 3 Dec 2009
Themed ButtonBar control supporting custom draw with full Designer support
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;

namespace ButtonBarsControl.Design.Generics
{
    /// <summary>
    /// Generic collection with events.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GenericCollection<T> : CollectionBase, IDeserializationCallback, IDisposable, ISerializable
    {
        #region Delegates

        /// <summary>
        /// Represents a method which will handle <see cref="GenericCollection{T}.Inserted"/> and <see cref="GenericCollection{T}.Removed"/> events.
        /// </summary>
        /// <param name="index">Index of item</param>
        /// <param name="value">Item</param>
        public delegate void CollectionChangedHandler(int index, T value);

        /// <summary>
        /// Represents a method which will handle <see cref="GenericCollection{T}.Inserting"/> and <see cref="GenericCollection{T}.Removing"/> events.
        /// </summary>
        /// <param name="index">Index of item</param>
        /// <param name="value">Item</param>
        public delegate void CollectionChangingHandler(int index, GenericCancelEventArgs<T> value);

        /// <summary>
        /// Represents a method which will handle <see cref="GenericCollection{T}.Cleared"/> event.
        /// </summary>
        public delegate void CollectionClearHandler();

        /// <summary>
        /// Represents a method which will handle <see cref="GenericCollection{T}.Clearing"/> event.
        /// </summary>
        /// <param name="value"></param>
        public delegate void CollectionClearingHandler(GenericCancelEventArgs<GenericCollection<T>> value);

        /// <summary>
        /// Represents a method which will handle <see cref="GenericCollection{T}.Changed"/> event.
        /// </summary>
        /// <param name="index">Index of item.</param>
        /// <param name="oldValue">Old object value</param>
        /// <param name="newValue">New object value.</param>
        public delegate void ItemChangeHandler(int index, T oldValue, T newValue);

        /// <summary>
        /// Represents a method which will handle <see cref="GenericCollection{T}.Changing"/>
        /// </summary>
        /// <param name="index">Index of item.</param>
        /// <param name="e">Object containing event data.</param>
        public delegate void ItemChangingHandler(int index, GenericChangeEventArgs<T> e);

        /// <summary>
        /// Represents a method which will handle <see cref="GenericCollection{T}.Validating"/> event.
        /// </summary>
        /// <param name="value">Value of object validating.</param>
        public delegate void ValidateHandle(T value);

        #endregion

        #region Constructor

        /// <summary>
        /// Initialize default instance.
        /// </summary>
        public GenericCollection()
        {
        }

        /// <summary>
        /// Initialize new instance with specified <see cref="Owner"/>
        /// </summary>
        /// <param name="owner"></param>
        public GenericCollection(object owner)
        {
            Owner = owner;
        }

        /// <summary>
        /// Initialize new instance.
        /// </summary>
        /// <param name="info">Serialization info object</param>
        /// <param name="context">Streaming context</param>
        protected GenericCollection(SerializationInfo info, StreamingContext context)
        {
            siInfo = info;
        }

        /// <summary>
        /// Initialize new instance with specified items.
        /// </summary>
        /// <param name="items">Items to be added by default.</param>
        public GenericCollection(IEnumerable<T> items) : this()
        {
            foreach (T barItem in items)
            {
#pragma warning disable DoNotCallOverridableMethodsInConstructor
                OnInsert(InnerList.Count, barItem);
#pragma warning restore DoNotCallOverridableMethodsInConstructor
                InnerList.Add(barItem);
#pragma warning disable DoNotCallOverridableMethodsInConstructor
                OnInsertComplete(InnerList.Count - 1, barItem);
#pragma warning restore DoNotCallOverridableMethodsInConstructor
            }
        }

        /// <summary>
        /// Initialize new instance with specified items.
        /// </summary>
        /// <param name="items">Items to be added by default.</param>
        public GenericCollection(GenericCollection<T> items) : this()
        {
            foreach (T item in items)
            {
                var newItem = (T) (item is ICloneable ? (item as ICloneable).Clone() : item);
#pragma warning disable DoNotCallOverridableMethodsInConstructor
                OnInsert(InnerList.Count, newItem);
#pragma warning restore DoNotCallOverridableMethodsInConstructor
                InnerList.Add(newItem);
#pragma warning disable DoNotCallOverridableMethodsInConstructor
                OnInsertComplete(InnerList.Count - 1, newItem);
#pragma warning restore DoNotCallOverridableMethodsInConstructor
            }
        }

        #endregion

        #region Property

        /// <summary>
        /// Gets object from collection with sepcific index.
        /// </summary>
        /// <param name="index">Index in collection</param>
        /// <returns>Returns object at specified index in the collection.</returns>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public T this[int index]
        {
            get { return (T) InnerList[index]; }
            set { InnerList[index] = value; }
        }

        /// <summary>
        /// Gets or Sets owner of the collection
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public object Owner { get; set; }

        #endregion

        #region Events

        /// <summary>
        /// Occurs when collection is being cleared.
        /// </summary>
        public event CollectionClearHandler Cleared;

        /// <summary>
        /// Occurs when collection is about to clear
        /// </summary>
        public event CollectionClearingHandler Clearing;

        /// <summary>
        /// Occurs when item is inserted to the collection
        /// </summary>
        public event CollectionChangedHandler Inserted;

        /// <summary>
        /// Occurs when an item is about to be inserted.
        /// </summary>
        public event CollectionChangingHandler Inserting;

        /// <summary>
        /// Occurs when item is removed from collection
        /// </summary>
        public event CollectionChangedHandler Removed;

        /// <summary>
        /// Occurs when item is about to be removed.
        /// </summary>
        public event CollectionChangingHandler Removing;

        /// <summary>
        /// Occurs when item is about to change.
        /// </summary>
        public event ItemChangingHandler Changing;

        /// <summary>
        /// Occurs when item is changed.
        /// </summary>
        public event ItemChangeHandler Changed;

        /// <summary>
        /// Occurs when collection requests additional custom processes when validating a value.
        /// </summary>
        public event ValidateHandle Validating;

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds an item to the end of the collection.
        /// </summary>
        /// <param name="item">The item to be added to the end of the Collection. The value can be null.</param>
        /// <returns>The index at which the value has been added.</returns>
        public int Add(T item)
        {
            OnInsert(InnerList.Count, item);
            int index = InnerList.Add(item);
            OnInsertComplete(InnerList.Count, item);
            return index;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="items"></param>
        public void AddRange(T[] items)
        {
            foreach (T item in items)
            {
                OnInsert(InnerList.Count, item);
                InnerList.Add(item);
                OnInsertComplete(InnerList.Count, item);
            }
        }

        /// <summary>
        /// Adds an item(s) to the end of the collection.
        /// </summary>
        /// <param name="items">The item to be added to the end of the Collection. The value can be null. </param>
        public void Add(T[] items)
        {
            foreach (T item in items)
            {
                OnInsert(InnerList.Count, item);
                InnerList.Add(item);
                OnInsertComplete(InnerList.Count, item);
            }
        }

        /// <summary>
        /// Inserts an element into the Collection at the specified index.
        /// </summary>
        /// <param name="index">Index at which item has to be inserted.</param>
        /// <param name="item">Item to be inserted</param>
        public void Insert(int index, T item)
        {
            OnInsert(index, item);
            InnerList.Insert(index, item);
            OnInsertComplete(index, item);
        }

        /// <summary>
        /// Removes item from the collection.
        /// </summary>
        /// <param name="item">Item to be removed.</param>
        public void Remove(T item)
        {
            int index = IndexOf(item);
            OnRemove(index, item);
            InnerList.Remove(item);
            OnRemoveComplete(index, item);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public int LastIndexOf(T item)
        {
            return InnerList.LastIndexOf(item);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public int LastIndexOf(T item, int startIndex)
        {
            return InnerList.LastIndexOf(item, startIndex);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <param name="startIndex"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public int LastIndexOf(T item, int startIndex, int count)
        {
            return InnerList.LastIndexOf(item, startIndex, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <param name="items"></param>
        public void InsertRange(int index, GenericCollection<T> items)
        {
            InnerList.InsertRange(index, items);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Contains(T item)
        {
            return InnerList.Contains(item);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public int IndexOf(T value)
        {
            return InnerList.IndexOf(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public int IndexOf(T value, int startIndex)
        {
            return InnerList.IndexOf(value, startIndex);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="startIndex"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public int IndexOf(T value, int startIndex, int count)
        {
            return InnerList.IndexOf(value, startIndex, count);
        }

        #endregion

        #region Overrides

        ///<summary>
        ///Performs additional custom processes when clearing the contents of the <see cref="T:System.Collections.CollectionBase"></see> instance.
        ///</summary>
        ///
        protected override void OnClear()
        {
            var e = new GenericCancelEventArgs<GenericCollection<T>>(this);
            if (Clearing != null)
            {
                Clearing(e);
                if (e.Cancel)
                {
                    return;
                }
            }
            base.OnClear();
        }

        ///<summary>
        ///Performs additional custom processes after clearing the contents of the <see cref="T:System.Collections.CollectionBase"></see> instance.
        ///</summary>
        protected override void OnClearComplete()
        {
            base.OnClearComplete();
            if (Cleared != null)
            {
                Cleared();
            }
        }

        ///<summary>
        ///Performs additional custom processes before inserting a new element into the <see cref="T:System.Collections.CollectionBase"></see> instance.
        ///</summary>
        ///
        ///<param name="value">The new value of the element at index.</param>
        ///<param name="index">The zero-based index at which to insert value.</param>
        protected override void OnInsert(int index, object value)
        {
            var e = new GenericCancelEventArgs<T>((T) value);
            if (Inserting != null)
            {
                Inserting(index, e);
                if (e.Cancel)
                {
                    return;
                }
            }
            base.OnInsert(index, value);
        }

        ///<summary>
        ///Performs additional custom processes after inserting a new element into the <see cref="T:System.Collections.CollectionBase"></see> instance.
        ///</summary>
        ///
        ///<param name="value">The new value of the element at index.</param>
        ///<param name="index">The zero-based index at which to insert value.</param>
        protected override void OnInsertComplete(int index, object value)
        {
            base.OnInsertComplete(index, value);
            if (Inserted != null)
            {
                Inserted(index, (T) value);
            }
        }

        ///<summary>
        ///Performs additional custom processes when removing an element from the <see cref="T:System.Collections.CollectionBase"></see> instance.
        ///</summary>
        ///
        ///<param name="value">The value of the element to remove from index.</param>
        ///<param name="index">The zero-based index at which value can be found.</param>
        protected override void OnRemove(int index, object value)
        {
            var e = new GenericCancelEventArgs<T>((T) value);
            if (Removing != null)
            {
                Removing(index, e);
                if (e.Cancel)
                {
                    return;
                }
            }
            base.OnRemove(index, value);
        }

        ///<summary>
        ///Performs additional custom processes after removing an element from the <see cref="T:System.Collections.CollectionBase"></see> instance.
        ///</summary>
        ///
        ///<param name="value">The value of the element to remove from index.</param>
        ///<param name="index">The zero-based index at which value can be found.</param>
        protected override void OnRemoveComplete(int index, object value)
        {
            base.OnRemoveComplete(index, value);
            if (Removed != null)
            {
                Removed(index, (T) value);
            }
        }

        ///<summary>
        ///Performs additional custom processes when validating a value.
        ///</summary>
        ///
        ///<param name="value">The object to validate.</param>
        protected override void OnValidate(object value)
        {
            if (value == null)
            {
                throw new ArgumentNullException();
            }
            if (Validating != null)
            {
                Validating((T) value);
            }
            base.OnValidate(value);
        }

        ///<summary>
        ///Performs additional custom processes before setting a value in the <see cref="T:System.Collections.CollectionBase"></see> instance.
        ///</summary>
        ///
        ///<param name="oldValue">The value to replace with newValue.</param>
        ///<param name="newValue">The new value of the element at index.</param>
        ///<param name="index">The zero-based index at which oldValue can be found.</param>
        protected override void OnSet(int index, object oldValue, object newValue)
        {
            var e = new GenericChangeEventArgs<T>((T) oldValue, (T) newValue);
            if (Changing != null)
            {
                Changing(index, e);
                if (e.Cancel)
                {
                    return;
                }
            }
            base.OnSet(index, oldValue, newValue);
        }

        ///<summary>
        ///Performs additional custom processes after setting a value in the <see cref="T:System.Collections.CollectionBase"></see> instance.
        ///</summary>
        ///
        ///<param name="oldValue">The value to replace with newValue.</param>
        ///<param name="newValue">The new value of the element at index.</param>
        ///<param name="index">The zero-based index at which oldValue can be found.</param>
        protected override void OnSetComplete(int index, object oldValue, object newValue)
        {
            base.OnSetComplete(index, oldValue, newValue);
            if (Changed != null)
            {
                Changed(index, (T) oldValue, (T) newValue);
            }
        }

        #endregion

        private SerializationInfo siInfo;

        #region IDeserializationCallback Members

        ///<summary>
        ///Runs when the entire object graph has been deserialized.
        ///</summary>
        ///
        ///<param name="sender">The object that initiated the callback. The functionality for this parameter is not currently implemented. </param>
        public void OnDeserialization(object sender)
        {
            if (siInfo != null)
            {
                Clear();
                if (siInfo.GetInt32("Count") != 0)
                {
                    Clear();
                    int num = siInfo.GetInt32("Count");
                    for (int i = 0; i < num; i++)
                    {
                        Add((T) siInfo.GetValue("Items" + i, typeof (T)));
                    }
                }
                siInfo = null;
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            Owner = null;
            List.Clear();
            InnerList.Clear();
            siInfo = null;
        }

        #endregion

        #region ISerializable Members

        /// <summary>
        /// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
        /// </summary>
        /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data. </param><param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization. </param><exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
            {
                throw new ArgumentNullException("info");
            }
            info.AddValue("Count", Count);
            if (Count != 0)
            {
                for (int i = 0; i < Count; i++)
                {
                    info.AddValue("Items" + i, this[i]);
                }
            }
        }

        #endregion

        /// <summary>
        /// Sets index of the item to given index.
        /// </summary>
        /// <param name="item">Item whose index is to be set.</param>
        /// <param name="index">New index of item.</param>
        public void SetChildIndex(T item, int index)
        {
            if (List.Count > 0)
            {
                int num = IndexOf(item);
                if (index < 0)
                {
                    index = 0;
                }
                if (index >= List.Count)
                {
                    index = List.Count - 1;
                }
                if ((index >= 0) && (index < List.Count))
                {
                    if (num < index)
                    {
                        for (int i = num; i < index; i++)
                        {
                            List[i] = List[i + 1];
                        }
                        List[index] = item;
                    }
                    else if (num > index)
                    {
                        for (int j = num; j > index; j--)
                        {
                            List[j] = List[j - 1];
                        }
                        List[index] = item;
                    }
                }
            }
        }

        /// <summary>
        /// Sorts list using specified <see cref="IComparer"/>
        /// </summary>
        /// <param name="comparer"><see cref="IComparer"/> used to sort items.</param>
        public void Sort(IComparer comparer)
        {
            if ((List.Count > 0) && (comparer != null))
            {
                var array = new object[List.Count];
                for (int i = 0; i < List.Count; i++)
                {
                    array[i] = List[i];
                }
                Array.Sort(array, comparer);
                List.Clear();
                for (int j = 0; j < array.Length; j++)
                {
                    List.Add(array[j]);
                }
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Manish Ranjan Kumar
Software Developer (Senior)
India India
No Biography provided

| Advertise | Privacy | Mobile
Web01 | 2.8.140826.1 | Last Updated 3 Dec 2009
Article Copyright 2009 by Manish Ranjan Kumar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid