Thoughts on Model View Presenter

Over the last year I have been working on a couple of applications (Winforms) that use the MVP pattern. There is a lot of articles published covering MVC and MVP to various degrees of detail.  They all seem to stop at the point where you have one controller, one view and a simple model.  I am trying to find out if anyone else has been trying to solve the same problem in the same way.  Very little of the literature actually describes the implementation of a Domain Model.

The model component consists of a Domain Model that is a tree of Composite Entities with attached Business Rules.  The Business Rules entities use XPathNavigator to negotiate the structure of the model.  We have defined an abstract type system for the attributes so that that can be treated polymorphically (all types may be set to a string) and may be invalid (for example an integer attribute may be set to “XXX”). Our business rules perform a selection of functions:  Providing defaults, performing calculations and providing validation.  When the rules are applied we obtain a set of rule messages that determines the result of the action.  Business functions are implemented as Facades over the model components so that the controller need only make a single call.  By using our navigator compenents we can refer to other parts of the model using an XPath expression – this makes our rules rugged with respect to changes in the structure of the model (for excample introduction of an intemediate node).

The Business Entity layer is distinct from the persistance layer – it only depends upon the database to provide the values of the primary keys which it holds as attributes.  The Pesistence Layer consists of a set of interfaces that may be implemented by a concrete implementation.  By keeping the persistance layer distinct from the model we protect ourselves from changes in the persistance mechanism.  Our reporting requirements have forced us to duplicate calculations between views and the rules.  Since we load the model from the views it is an easy task to ensure that these calculations are in step (we have a model checker that loads each business entity from the database and fires the rules – if the model and database views are in step then there will be no mesages recorded).

The closest that I have found to what we are doing is the CSLA.NET.  The big difference is that CSLA deals in terms of a collection of “broken rules” and unifies the persistance and data access objects.  It seems to provide no core functionality for implementing business functions other than validation and authorisation.

The Microsoft UIP seems to implement the Model View Controller pattern.  This is a very heavyweight solution and seems to have reduced the model component to little more than a data container.

The Microsoft Validation Application block seems to be heading in the same direction as the business rules approach that we have been taking.  Again this stops at validation (no defaults or calculations) and may only validate a single object.

Simian – code similarity comparision tool

Simian is a relatively cheap code analysis tool.  It performs simularity analysis on a whole range of files.  It is great for detecting cut and paste code that really needs to be refactored. It costs $450 per project for commercial use.  I am unsure of how the costs scale up for the enterprise version.

I found this as one of the cruise control options.

Cruise Control

A recent dotnet rocks defined Cruise Control.NET as:

10 Get and build all files under version control if any file has changed

20 GOTO 10

It is a little more powerful than that. It is a generalised scheduling program that can run tasks on a timed schedule or once a polled event occours.  I am sure that a number of major end-user applications could be built around this (scheduled reports, model integrity checks).

I now have two of the four C# projects at my work built under cruise control.

I am using PVCS which does not integrate very cleanly as it’s pcli tool is frankly a little cranky.

I have managed to put together a batch file that pulls all of the files from a project with a given promotion group. This gets called as a prebuild task.  I then use the msbuild task to build the project and the nunit task to test the project.  The build script is set to run three times per day: just before I start for the day, at lunchtime and just after I leave.  This is not quite the intended instant feedback but is a hell of a lot better than finding the build is broken when you are just about to apply an important critical fix.

The web application that can be used to monitor CC is very good.  It allows drill down to the times that individual tests took to run!  It also permits a build to be run on demand. 

The only problem I have at the moment is that the CC.net website is down so I can’t get the source…

Using a class in an exe in Boo

Boo is great for lightweight experiments:

===  myclass.boo ===

namespace foo

class myclass:
    def foo():
        print(“Hello”)

c = myclass()
c.foo()

===============

=== myclass2.boo ===

import foo from “myclass.exe”

c = myclass()
c.foo()

=================

Create the two above files.

Compile both with the booc compiler. 

 run myclass2.exe

This shows how easy it is to extend a .net app in Boo. 

Boo Podcast Client

# Podcatcher.boo
#
# (C) Chris Eyre 2007

# This is released under the BSD licence.

#
# This is the start of a podcast download script.
# I have been unable to find a suitable podcast client to replace iPodder under Ubuntu 7.4
# so there was a need to write one.
#
# Required features:
# Download a podcast to a defined directory.
# Easy reload.
# No excessive configuration.
#
# Todo:
# Scheduler
# UI
# bitorrent support.
# Currently it is portable between Mono and .Net

import System.IO
import System.Net
import System.Xml

# This reads a file structured:
# podcastlist
# podcast
# name
# url
# output
#
def ProcessFeeds(target as string):
x = XmlDocument();
x.Load(target)
for xnode as XmlNode in x.FirstChild.ChildNodes:
url = xnode.SelectSingleNode(“url”).InnerText
name = xnode.SelectSingleNode(“output”).InnerText
ProcessFeed( url, name)

#Decodes the RSS feed.
def ProcessFeed(uri as string, output as string):
w = WebClient()
x = XmlDocument()
x.Load(w.OpenRead(uri))
xnode = x.SelectSingleNode(“//channel”)
for itemnode as XmlNode in xnode.ChildNodes:
if itemnode.Name == “item”:
url = itemnode.SelectSingleNode(“enclosure/@url”).InnerText
print(url)
GetPodcast(url, output)

// Grabs the podcast if not already got.
// If the downloaded file is corrupt then delete it and it will be replaced on the next run.
def GetPodcast(podcast as string, output as string):
filename = output + “/” + NameFromUrl(podcast)
if not File.Exists(filename):
WebClient().DownloadFile(FixUrl(podcast), filename)

# another hack for .NetRocks
def FixUrl(url as string) as string:
if Path.GetExtension(url) == “.torrent”:
return Path.ChangeExtension(url,null)
else:
return url

def NameFromUrl(url as string) as string:
#HACK: This may work for dotnet rocks, but I am still working on a torrent client
if Path.GetExtension(url) == “.torrent”:
return Path.ChangeExtension(Path.GetFileName(url),null)
else:
return Path.GetFileName(url)

ProcessFeeds(“/home/chris/Projects/PodCatcher/list.xml”)
print(“done”)

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.