Databinding with a filter and a sort

Recently I have been experimenting with databiniding in winforms.

The support in .NET 2 is really quite extensive.

If you can find and implement the right interfaces you can databind and read write to a class that does not expose any public properties.

The BindingList class has a few limitations, namely that is does not by default support sorting or filtering.  This can be corrected by implementing IBindingListView.  The example implementation on sourceforge does not work well with custom types.  Here is an implementation that while currently limited will allow binding with sort and filter to just about any type:

// Released under the BSD licence. 

using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

namespace BindingApp
{
    /// <summary>
    /// This class is an enhanced BindingList class.
    /// It supplies sorting and a simple form of filtering. 
    /// Currently the filter consists of Name=Value.
    /// The filter is an exact match.
    /// </summary>
    /// <typeparam name=”T”></typeparam>
    public class FilteringSortingBindingList<T> : IBindingListView,
                                                  IRaiseItemChangedEvents
    {
        #region private fields

        private string _filter = null;
        private BindingList<T> _fullList = new BindingList<T>();
        private BindingList<T> _filteredList = new BindingList<T>();
        private ListSortDirection _sortDirection;
        private PropertyDescriptor _sortProperty = null;
        private bool _isSorted = false;

        #endregion

        #region IBindingListView Members

        /// <summary>
        /// We currently do not support multi-column sorts
        /// </summary>
        /// <param name=”sorts”></param>
        public void ApplySort(ListSortDescriptionCollection sorts)
        {
            throw new Exception(“The method or operation is not implemented.”);
        }

        /// <summary>
        ///
        /// </summary>
        public string Filter
        {
            get
            {
                return _filter;
            }
            set
            {
                if (_filter != value)
                {
                    _filter = value;
                    ApplyFilter();
                }
            }
        }

        /// <summary>
        ///
        /// </summary>
        public void RemoveFilter()
        {
            _filter = null;
            ApplyFilter();
        }

        /// <summary>
        /// We currently don’t support multi-column sorts
        /// </summary>
        public ListSortDescriptionCollection SortDescriptions
        {
            get { throw new Exception(“The method or operation is not implemented.”); }
        }

        /// <summary>
        /// This controls multi column sorts
        /// </summary>
        public bool SupportsAdvancedSorting
        {
            get { return false; }
        }

        /// <summary>
        /// We do support filtering
        /// </summary>
        public bool SupportsFiltering
        {
            get { return true; }
        }

        #endregion

        #region IBindingList Members
        /// <summary>
        ///
        /// </summary>
        /// <param name=”property”></param>
        public void AddIndex(PropertyDescriptor property)
        {
            //throw new Exception(“The method or operation is not implemented.”);
        }
        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public object AddNew()
        {
            T res =_fullList.AddNew();
            ApplyFilter();
            return res;
        }

        /// <summary>
        ///
        /// </summary>
        public bool AllowEdit
        {
            get { return true; }
        }

        /// <summary>
        ///
        /// </summary>
        public bool AllowNew
        {
            get { return true; }
        }

        /// <summary>
        ///
        /// </summary>
        public bool AllowRemove
        {
            get { return true; }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name=”property”></param>
        /// <param name=”direction”></param>
        public void ApplySort(PropertyDescriptor property, ListSortDirection direction)
        {
            _isSorted = true;
            _sortDirection = direction;
            _sortProperty = property;

            List<T> sortList = new List<T>();

            foreach (T item in _fullList)
            {
                sortList.Add(item);
            }
            sortList.Sort(new PropertyComparer(property, direction));
            _fullList.Clear();
            foreach (T item in sortList)
            {
                _fullList.Add(item);
            }
            ApplyFilter();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name=”property”></param>
        /// <param name=”key”></param>
        /// <returns></returns>
        public int Find(PropertyDescriptor property, object key)
        {
            throw new Exception(“The method or operation is not implemented.”);
        }

        /// <summary>
        ///
        /// </summary>
        public bool IsSorted
        {
            get { return _isSorted; }
        }

        /// <summary>
        ///
        /// </summary>
        public event ListChangedEventHandler ListChanged;

        /// <summary>
        ///
        /// </summary>
        /// <param name=”property”></param>
        public void RemoveIndex(PropertyDescriptor property)
        {
            throw new Exception(“The method or operation is not implemented.”);
        }

        /// <summary>
        ///
        /// </summary>
        public void RemoveSort()
        {
            throw new Exception(“The method or operation is not implemented.”);
        }

        /// <summary>
        ///
        /// </summary>
        public ListSortDirection SortDirection
        {
            get { return _sortDirection; }
        }

        /// <summary>
        ///
        /// </summary>
        public PropertyDescriptor SortProperty
        {
            get { return _sortProperty; }
        }

        /// <summary>
        ///
        /// </summary>
        public bool SupportsChangeNotification
        {
            get { return true; }
        }

        /// <summary>
        ///
        /// </summary>
        public bool SupportsSearching
        {
            get { return false; }
        }

        /// <summary>
        ///
        /// </summary>
        public bool SupportsSorting
        {
            get { return true; }
        }

        #endregion

        #region IList Members

        /// <summary>
        ///
        /// </summary>
        /// <param name=”value”></param>
        /// <returns></returns>
        public int Add(object value)
        {
            int res = -1;
            if (value is T)
            {
                _fullList.Add((T)value);
                ApplyFilter();
                res = ActiveList.IndexOf((T)value);
            }
            return res;
        }

        /// <summary>
        ///
        /// </summary>
        public void Clear()
        {
            _fullList.Clear();
            _filteredList.Clear();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name=”value”></param>
        /// <returns></returns>
        public bool Contains(object value)
        {
            return ActiveList.Contains((T)value);
        }

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

        /// <summary>
        ///
        /// </summary>
        /// <param name=”index”></param>
        /// <param name=”value”></param>
        public void Insert(int index, object value)
        {
            _fullList.Insert(index, (T)value);
            ApplyFilter();
        }

        /// <summary>
        ///
        /// </summary>
        public bool IsFixedSize
        {
            get { return false; }
        }

        /// <summary>
        ///
        /// </summary>
        public bool IsReadOnly
        {
            get { return true; }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name=”value”></param>
        public void Remove(object value)
        {
            _fullList.Remove((T)value);
            ApplyFilter();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name=”index”></param>
        public void RemoveAt(int index)
        {
            if (Filter != null)
            {
                _fullList.Remove(_filteredList[index]);
                ApplyFilter();
            }
            else
            {
                _fullList.RemoveAt(index);
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name=”index”></param>
        /// <returns></returns>
        public object this[int index]
        {
            get
            {
                return ActiveList[index];
            }
            set
            {
                if (Filter != null)
                {
                    _fullList[_fullList.IndexOf(_filteredList[index])] = (T)value;
                    ApplyFilter();
                }
                else
                {
                    _fullList[index] = (T)value;
                }
            }
        }

        #endregion

        #region ICollection Members

        /// <summary>
        ///
        /// </summary>
        /// <param name=”array”></param>
        /// <param name=”index”></param>
        public void CopyTo(Array array, int index)
        {
            (ActiveList as IList).CopyTo(array, index);
        }

        /// <summary>
        ///
        /// </summary>
        public int Count
        {
            get
            {
                return ActiveList.Count;
            }
        }

        /// <summary>
        ///
        /// </summary>
        public bool IsSynchronized
        {
            get { return false; }
        }

        /// <summary>
        ///
        /// </summary>
        public object SyncRoot
        {
            get { return null; }
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public System.Collections.IEnumerator GetEnumerator()
        {
            return ActiveList.GetEnumerator();
        }

        #endregion

        #region IRaiseItemChangedEvents Members

        /// <summary>
        ///
        /// </summary>
        public bool RaisesItemChangedEvents
        {
            get { return true; }
        }

        #endregion

        #region private methods
        /// <summary>
        ///
        /// </summary>
        private void ApplyFilter()
        {
            _filteredList.Clear();

            if (Filter != null)
            {
                foreach (T item in _fullList)
                {
                    if (MatchesFilter(item))
                    {
                        _filteredList.Add(item);
                    }
                }
            }
            if (ListChanged != null)
            {
                ListChanged(this, new ListChangedEventArgs(ListChangedType.Reset, -1));
            }
        }

        /// <summary>
        ///
        /// </summary>
        private BindingList<T> ActiveList
        {
            get
            {
                if (Filter != null)
                {
                    return _filteredList;
                }
                else
                {
                    return _fullList;
                }
            }
        }

        /// <summary>
        /// This is the implementation of the filter.
        /// Currently the filter is of the form name=value
        /// </summary>
        /// <param name=”item”></param>
        /// <returns></returns>
        private bool MatchesFilter(T item)
        {
            bool includeInList = false;

            string name = null;
            string value = null;

            string[] rules = Filter.Split(new char[] { ‘=’ });

            if (rules.Length > 0)
            {
                name = rules[0];
            }

            if (rules.Length > 1)
            {
                value = rules[1];
            }

            if (name == null)
            {
                //No name, no filter
                includeInList = true;
            }
            else
            {
                Type itemType = typeof(T);
                PropertyInfo prop = itemType.GetProperty(name);
                if (prop != null)
                {

                    if (prop.GetValue(item, null).ToString() == value)
                    {
                        includeInList = true;
                    }
                }
            }
            return includeInList;
        }

        #endregion

        #region private nested class

        /// <summary>
        ///
        /// </summary>
        private class PropertyComparer : IComparer<T>
        {
            #region constructors

            /// <summary>
            ///
            /// </summary>
            /// <param name=”property”></param>
            /// <param name=”direction”></param>
            public PropertyComparer(PropertyDescriptor property, ListSortDirection direction)
            {
                _property = property;
                _direction = direction;
            }

            #endregion

            #region private fields

            private PropertyDescriptor _property;
            private ListSortDirection _direction;

            #endregion

            #region IComparer<T> Members

            /// <summary>
            /// This is a very general purpose comparison.
            /// </summary>
            /// <param name=”x”></param>
            /// <param name=”y”></param>
            /// <returns></returns>
            public int Compare(T x, T y)
            {
                int res;
                object ox;
                object oy;

                ox = typeof(T).GetProperty(_property.Name).GetValue(x, null);
                oy = typeof(T).GetProperty(_property.Name).GetValue(y, null);

                if (ox is IComparable)
                {
                    // If the underlying type can be compared use the comparision.
                    return ((IComparable)ox).CompareTo(oy);
                }
                else if ((ox == null) && (oy == null))
                {
                    res = 0;
                }
                else if ((ox == null) && (oy != null))
                {
                    res = -1;
                }
                else if ((ox != null) && (oy == null))
                {
                    res = 1;
                }
                else
                {
                    string xs = ox.ToString();
                    string ys = oy.ToString();
                    res = xs.CompareTo(ys);
                }

                if (_direction == ListSortDirection.Descending)
                {
                    res *= -1;
                }

                return res;
            }

            #endregion
        }
        #endregion
    }
}
 

Embedding a screen saver in a .net container control.

I have been experimenting with a simple means of embedding a screensaver into a control:

private void button1_Click(object sender, EventArgs e)
{
EmbedScr(@”c:windowssystem32ssmyst.scr”, panel1.Handle);
}
private void EmbedScr(string filename, IntPtr Handle)
{
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
psi.FileName = “cmd.exe”;
psi.Arguments = string.Format(@”/c “”{0} /p {1}”””, filename, panel1.Handle);
psi.WindowStyle = ProcessWindowStyle.Hidden;
System.Diagnostics.Process.Start(psi);
}

This allows for dynamic decoration of controls allowing a ui to become very different very quickly. 

The Factory Gate Design Pattern

Recently I have added some XML serialization/deserialization code to an object model library that I have been working on.  In order to create the objects a Factory object is used to create the objects.  The library implements the base class and is supplied a delegate that allows the application to supply the objects.

One of my colleages commented that this did not seem to be a Factory object.  This is when I described it as a “Factory Gate”. The idea is that if the object comes out of the “Factory Gate” you don’t care if it was made there or bought in from outside and sold on.

Custom Enterprise Application Blocks

There are frequent occasions that you need to use a Plugin architecture to add functionality to an application.

The microsoft patterns and practices group have developed a standard framework for adding plugins to an application.

This is what they refer to as the Enterprise Library.

It contains with a set of supplied application blocks.

Here is an article on writing your own application blocks.