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.

About Models

In this set of posts when I talk about model I mean the Model component of Model View Controller.

Recently I have been working on a Model/Business Rule engine.

A lost of people that implement MVC or MVP tend to use the Model component as a simple data container with methods.  This does not keep the model as powerful or flexable as possible.

The Model can be used to validate a set of data.  For example we have a utility (in about 20 lines of code) that loops through our database and reports all of the validation faliures.

In order to validate data the model must be able to be populated with invalid data (or null data).  This means thatlanguage type systems are not sufficient.  We developed our own abstract type system in which any type can be supplied a string value. Integers set to XXX are not valid, but at least the model can report this.

Our model forms a tree of composite objects. Business rules are attached to these objects.  The composite supports XPath querying so that our business rules can be robust with respect to changes in the model structure.  We implemented an XPathNavigator descendant to provide this functionality.  Our composite objects have “attributes” (not in the C# sense of [XXXAttribute])  that use our model types.

How to view WMI queries in a dataset.

The following sample code demonstrates putting WMI WQL queries into a DataSet and displaying it in a datagridview.

This could become the basis of a really powerful system management utility. 

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Management;

namespace WMIForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            WMIQuery(“SELECT * FROM Win32_Service”);           
        }

        private void WMIQuery(string query)
        {
            SelectQuery qry = new SelectQuery(query);

            DataSet ds = new DataSet();
            DataTable Table = ds.Tables.Add(“WMI”);

            ManagementObjectSearcher ms = new ManagementObjectSearcher(qry);
            ManagementObjectCollection mc = ms.Get();

            Boolean loaded = false;

            foreach (ManagementObject mo in ms.Get())
            {
                if (!loaded)
                {
                    foreach (PropertyData prop in mo.Properties)
                    {
                        Table.Columns.Add(prop.Name);
                    }
                    loaded = true;
                }
                DataRow row = Table.NewRow();
                foreach (PropertyData prop in mo.Properties)
                {
                    row[prop.Name] = prop.Value;
                }
                Table.Rows.Add(row);
            }

            dataGridView1.DataSource = Table;
        }

        private void GoButton_Click(object sender, EventArgs e)
        {
            WMIQuery(QueryTextBox.Text);
        }
    }
}

Lightweight Maintenance Screens

The following can easily be turned into a lightweight maintenance screen:

        private void Form1_Load(object sender, EventArgs e)
        {
            _Conn = new SqlConnection(“My Connection String”);
            _Adapter = new SqlDataAdapter();
            _Adapter.SelectCommand = new SqlCommand(“execute wsp_my_select_all”, _Conn);
            _Builder = new SqlCommandBuilder(_Adapter);
            _DS = new DataSet();
            _Adapter.Fill(_DS);
            dataGridView1.DataSource = _DS.Tables[0];
            dataGridView1.Columns[0].Visible = false;
            dataGridView1.Columns[“rowversion”].Visible = false;   
        }

        private void SaveBtnClick(object sender, EventArgs e)
        {
            //Do something useful with any exceptions here
            _Adapter.Update(_DS, _DS.Tables[0].TableName);
        }

What Dot Net Rocks Does Not Get About The GPL

In a number of recent Dot Net Rocks shows Carl Franklin has mentioned that Microsoft is not anti-open source, just anti-GPL. From his comments it appears that Carl does not understand that a given piece of code may be released under a number of licences.

The GPL is based upon copyright law.  By default you may not use someone else’s copyrighted code without permission.

With a commercial licence you are paid for the right to use the code. That is the price, if the price is too high don’t use the code.

GPL code is released on the condition that it is only built into an application or dll that only consists of GPL code or GPL compatible code.  This is the price of using GPL code. If the price is too high don’t use the code.

If you modify GPL code you only need supply the changed source (under the GPL) to the person you give the application to and only if they ask.  Some people do more and publish publicly but that is not part of the GPL.

There is no reason that a given piece of code cannot be released under multiple licences.  If you own the copyright you can sell the code commercially and release the code under the GPL.