Model View Controller sample for .NET C# Windows Forms Applications

Here is basic idea of MVC implementation in SDI application.

Model is the most simple part. It is some set of data classes  which are deprived of any built in events. It does not reference controller or any view, basically it is the most restricted operation in MVC. In other words model is not aware of controller and views. So we can save and load model data without triggering of any events and affecting state of application.

Application has several Views connected to model and controller. View has direct link on model and controller. View can read any data of model and subscribe to events notifications of controller. View is prohibited to change model directly. If you want to change model you have to call complimentary methods of controller. So controller can change model and notify all views about changes in data or in application state.

Controller has single instance in SDI and it has direct reference on model and it can read and write any changes to model. Controller creates new instances of views, but it can not update views directly. It can only fire events notifying about changes in model.

Primary design goals:

1. Controller is single place for any model changes.

2. Model updates don't cause unpredictable sporadic events.

3. All views are updated synchronously.

Secondary design goals:

1. Controller can receive notifications from network it can change model and notify all views about changes in data and in application state.

2. All changes can be performed in form of single package (transaction), all views would be notified only at the end of that transaction by controller via events.

3. In case of large database, controller can control partially loaded data. That means controller can load only sub set of database into application model and subscribe and unsubscribe on changes in it. So server can notify only about changes in loaded subset.

Download

MVC.zip - This is sample for .NET Windows Forms application.

Model
Pair.cs

 
using System;
 
namespace Sample.Model
{
      /// <summary>
      /// Summary description for Pair.
      /// </summary>
      public class Pair
      {
            private string m_sName = string.Empty;
            private string m_sValue = string.Empty;
 
            public Pair()
            {
            }
 
            public Pair( string sName, string sValue )
            {
                  Name = sName;
                  Value = sValue;
            }
 
            public Pair( Pair pair )
            {
                  Name = pair.Name;
                  Value = pair.Value;
            }
 
            public string Name
            {
                  set
                  {
                        if( value != null )
                        {
                              m_sName = value;
                        }
                  }
                  get
                  {
                        return m_sName;
                  }
            }
 
            public string Value
            {
                  set
                  {
                        if( value != null )
                        {
                              m_sValue = value;
                        }
                  }
                  get
                  {
                        return m_sValue;
                  }
            }
 
            public override string ToString()
            {
                  return m_sName;
            }
      }
}
PairsArray.cs

using System;
using System.Collections;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
 
namespace Sample.Model
{
      /// <summary>
      /// Summary description for PairsArray.
      /// </summary>
      public class PairsArray : IXmlSerializable
      {
 
            private ArrayList m_alPairs = null;
 
            private const string m_XmlItem = "item";
            private const string m_XmlName = "name";
            private const string m_XmlValue = "value";           
 
            public PairsArray()
            {
                  m_alPairs = new ArrayList();
            }
 
            public void Copy( PairsArray pairsArray )
            {
                  if( pairsArray != null )
                  {
                        for( int i = 0; i < pairsArray.Count; i++ )
                        {
                              m_alPairs.Add( new Pair( pairsArray[ i ] ) );
                        }
                  }
            }
 
            public bool Add( Pair pair )
            {
                  bool bSuccess = true;
                  if( pair != null )
                  {
                        m_alPairs.Add( pair );
                  }
                  else
                  {
                        bSuccess = false;
                  }
                  return bSuccess;
            }
           
            public void Delete( Pair pair )
            {
                  if( pair != null )
                  {
                        m_alPairs.Remove( pair );
                  }
            }
 
            public void Delete( int nPos )
            {
                  if( nPos < Count )
                  {
                        Pair pair = m_alPairs[ nPos ] as Pair;
                        if( pair != null )
                        {
                              m_alPairs.Remove( nPos );
                        }
                  }
            }
 
            public int Count
            {
                  get
                  {
                        return m_alPairs.Count;
                  }
            }
 
            public Pair this[ int nPos ]
            {
                  set
                  {
                        if( value != null && nPos < Count )
                        {
                              Pair updatedItem = m_alPairs[ nPos ] as Pair;
                              if( updatedItem != null )
                              {
                                    updatedItem.Name = value.Name;
                                    updatedItem.Value = value.Value;
                              }
                        }
                  }
                  get
                  {
                        if( nPos < Count )
                        {
                              return ( m_alPairs[ nPos ] as Pair );
                        }
                        else
                        {
                              return null;
                        }
                  }
            }
 
            public void Clean()
            {
                  m_alPairs.Clear();
            }
 
            public void SortBy( CompareBy sortBy )
            {
                  m_alPairs.Sort( new PairsComparer( sortBy ) );
            }
 
            #region IXmlSerializable
 
            XmlSchema IXmlSerializable.GetSchema()
            {
                  return null;
            }
           
            public void WriteXml ( XmlWriter writer )
            {
                  Pair pair = null;
                  for( int n = 0; n < m_alPairs.Count; n++ )
                  {
                        pair = m_alPairs[ n ] as Pair;
                        if( pair != null )
                        {
                              writer.WriteStartElement( m_XmlItem );
   
                              writer.WriteStartElement( m_XmlName );
                              writer.WriteString( pair.Name );
                              writer.WriteEndElement( );
 
                              writer.WriteStartElement( m_XmlValue );
                              writer.WriteString( pair.Value );
                              writer.WriteEndElement( );
 
                              writer.WriteEndElement();
                        }
                  }
            }
           
            public void ReadXml ( XmlReader reader )
            {
                  string sName = string.Empty;
                  string sValue = string.Empty;
 
                  reader.ReadStartElement();
   
                  while( reader.IsStartElement( m_XmlItem ) )
                  {
                        reader.ReadStartElement( m_XmlItem );
   
                        reader.ReadStartElement( m_XmlName );
                        sName = reader.ReadString();
                        reader.ReadEndElement();
 
                        reader.ReadStartElement( m_XmlValue );
                        sValue = reader.ReadString();
                        reader.ReadEndElement();
 
                        reader.ReadEndElement();
 
                        m_alPairs.Add( new Pair( sName, sValue ) );
                  }
 
                  reader.ReadEndElement();
            }
 
            #endregion
 
      }
}
PairsComparer.cs

using System;
using System.Collections;
 
namespace Sample.Model
{
      public enum CompareBy
      {
            Name,
            Value
      }
      /// <summary>
      /// Summary description for PairsComparer.
      /// </summary>
      public class PairsComparer : IComparer
      {
            #region Private Variables
           
            private CompareBy m_SortBy = CompareBy.Name;
           
            #endregion
 
            public CompareBy SortBy
            {
                  get
                  {
                        return m_SortBy;
                  }
                  set
                  {
                        m_SortBy = value;
                  }
            }
 
            public PairsComparer()
            {
                  //
                  // TODO: Add constructor logic here
                  //
            }
 
            public PairsComparer( CompareBy pSortBy)
            {
                  m_SortBy = pSortBy;
            }
 
            int IComparer.Compare( Object pFirstObject, Object pObjectToCompare )
            {
                  if( pFirstObject is Pair && pObjectToCompare is Pair )
                  {
                        switch( m_SortBy )
                        {
                              case CompareBy.Name:
                                    return String.Compare( ( pFirstObject as Pair ).Name, ( pObjectToCompare as Pair ).Name );
                              case CompareBy.Value:
                                    return String.Compare( ( pFirstObject as Pair ).Value, ( pObjectToCompare as Pair ).Value );
                              default:
                                    return 0;
                        }
                  }
                  else
                        return 0;
            }
      }
}
 

 

View
Form.cs

 
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text.RegularExpressions;
 
using Sample.Model;
using Sample.Controller;
 
namespace Sample.View
{
      /// <summary>
      /// Summary description for Form1.
      /// </summary>
      public class FormMain : System.Windows.Forms.Form
      {
            /// <summary>
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.Container components = null;
           
            private Sample.Controller.DashBoard m_Controller = null;
            private Sample.Model.PairsArray m_Model = null;
 
            private System.Windows.Forms.TextBox m_TextBoxEditItem;
            private System.Windows.Forms.Label m_LabelListBox;
            private System.Windows.Forms.TextBox m_TextBoxNewItem;
            private System.Windows.Forms.Label m_LabelAddItem;
            private System.Windows.Forms.Button m_ButtonAdd;
            private System.Windows.Forms.Button m_ButtonDelete;
            private System.Windows.Forms.Button m_ButtonSave;
            private System.Windows.Forms.Button m_ButtonExit;
            private System.Windows.Forms.Button m_ButtonSortByName;
            private System.Windows.Forms.Button m_ButtonSortByValue;
            private System.Windows.Forms.ListBox m_ListBoxPairs;
            private System.Windows.Forms.ContextMenu m_ContextMenuListBox;
            private System.Windows.Forms.MenuItem m_MenuItemEdit;
            private System.Windows.Forms.MenuItem m_MenuItemDelete;
            private System.Windows.Forms.StatusBar m_StatusBar;
            private System.Windows.Forms.Button m_buttonLoad;
 
            private DataSet al = new DataSet();
 
            private const int m_nKeyEnter = 13;
            private const int m_nKeyEscape = 27;
            private const string m_sMatchPattern = @"\A[a-zA-Z0-9][a-zA-Z0-9]*{0}[a-zA-Z0-9][a-zA-Z0-9]*\Z";
            private const string m_sFileExtension = "XML";
 
            public FormMain( Model.PairsArray model, Controller.DashBoard controller )
            {
                  //
                  // Required for Windows Form Designer support
                  //
                  InitializeComponent();
 
                  m_Model = model;
                  m_Controller = controller;
 
                  m_Controller.Model = model;
                  m_Controller.View = this;
 
                  m_Controller.EventDataChanged +=new EventHandler( OnReload );
                  m_Controller.EventItemAdded += new Sample.Controller.DashBoard.EventHandlerItemUpdated( OnItemAdded );
                  m_Controller.EventItemDeleted +=new Sample.Controller.DashBoard.EventHandlerItemUpdated( OnItemDeleted );
                  m_Controller.EventItemUpdated += new Sample.Controller.DashBoard.EventHandlerItemUpdated( OnItemUpdated );
                  m_Controller.EventEnableDelete += new Sample.Controller.DashBoard.EventHandlerEnable( OnEnableDelete );
                  m_Controller.EventEnableSort += new Sample.Controller.DashBoard.EventHandlerEnable( OnEnableSort );
            }
 
            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            protected override void Dispose( bool disposing )
            {
                  if( disposing )
                  {
                        if (components != null)
                        {
                              components.Dispose();
                        }
                  }
                  base.Dispose( disposing );
            }
 
            #region Windows Form Designer generated code
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                  this.m_TextBoxNewItem = new System.Windows.Forms.TextBox();
                  this.m_LabelAddItem = new System.Windows.Forms.Label();
                  this.m_LabelListBox = new System.Windows.Forms.Label();
                  this.m_ButtonAdd = new System.Windows.Forms.Button();
                  this.m_ButtonDelete = new System.Windows.Forms.Button();
                  this.m_ButtonSave = new System.Windows.Forms.Button();
                  this.m_ButtonExit = new System.Windows.Forms.Button();
                  this.m_StatusBar = new System.Windows.Forms.StatusBar();
                  this.m_ButtonSortByName = new System.Windows.Forms.Button();
                  this.m_ButtonSortByValue = new System.Windows.Forms.Button();
                  this.m_TextBoxEditItem = new System.Windows.Forms.TextBox();
                  this.m_ContextMenuListBox = new System.Windows.Forms.ContextMenu();
                  this.m_MenuItemEdit = new System.Windows.Forms.MenuItem();
                  this.m_MenuItemDelete = new System.Windows.Forms.MenuItem();
                  this.m_ListBoxPairs = new System.Windows.Forms.ListBox();
                  this.m_buttonLoad = new System.Windows.Forms.Button();
                  this.SuspendLayout();
                  //
                  // m_TextBoxNewItem
                  //
                  this.m_TextBoxNewItem.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.m_TextBoxNewItem.Location = new System.Drawing.Point(8, 24);
                  this.m_TextBoxNewItem.Name = "m_TextBoxNewItem";
                  this.m_TextBoxNewItem.Size = new System.Drawing.Size(304, 20);
                  this.m_TextBoxNewItem.TabIndex = 1;
                  this.m_TextBoxNewItem.Text = "";
                  //
                  // m_LabelAddItem
                  //
                  this.m_LabelAddItem.Location = new System.Drawing.Point(8, 8);
                  this.m_LabelAddItem.Name = "m_LabelAddItem";
                  this.m_LabelAddItem.Size = new System.Drawing.Size(296, 16);
                  this.m_LabelAddItem.TabIndex = 2;
                  this.m_LabelAddItem.Text = "Name/Value Pair";
                  //
                  // m_LabelListBox
                  //
                  this.m_LabelListBox.Location = new System.Drawing.Point(8, 56);
                  this.m_LabelListBox.Name = "m_LabelListBox";
                  this.m_LabelListBox.Size = new System.Drawing.Size(296, 16);
                  this.m_LabelListBox.TabIndex = 2;
                  this.m_LabelListBox.Text = "Name/Value Pair List";
                  //
                  // m_ButtonAdd
                  //
                  this.m_ButtonAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
                  this.m_ButtonAdd.Location = new System.Drawing.Point(320, 24);
                  this.m_ButtonAdd.Name = "m_ButtonAdd";
                  this.m_ButtonAdd.Size = new System.Drawing.Size(96, 24);
                  this.m_ButtonAdd.TabIndex = 2;
                  this.m_ButtonAdd.Text = "Add";
                  this.m_ButtonAdd.Click += new System.EventHandler(this.OnButtonAddClick);
                  //
                  // m_ButtonDelete
                  //
                  this.m_ButtonDelete.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
                  this.m_ButtonDelete.Location = new System.Drawing.Point(320, 136);
                  this.m_ButtonDelete.Name = "m_ButtonDelete";
                  this.m_ButtonDelete.Size = new System.Drawing.Size(96, 23);
                  this.m_ButtonDelete.TabIndex = 6;
                  this.m_ButtonDelete.Text = "Delete";
                  this.m_ButtonDelete.Click += new System.EventHandler(this.OnButtonDeleteClick);
                  //
                  // m_ButtonSave
                  //
                  this.m_ButtonSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
                  this.m_ButtonSave.Location = new System.Drawing.Point(320, 168);
                  this.m_ButtonSave.Name = "m_ButtonSave";
                  this.m_ButtonSave.Size = new System.Drawing.Size(96, 23);
                  this.m_ButtonSave.TabIndex = 7;
                  this.m_ButtonSave.Text = "Save";
                  this.m_ButtonSave.Click += new System.EventHandler(this.OnButtonSaveClick);
                  //
                  // m_ButtonExit
                  //
                  this.m_ButtonExit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
                  this.m_ButtonExit.DialogResult = System.Windows.Forms.DialogResult.Cancel;
                  this.m_ButtonExit.Location = new System.Drawing.Point(320, 296);
                  this.m_ButtonExit.Name = "m_ButtonExit";
                  this.m_ButtonExit.Size = new System.Drawing.Size(96, 23);
                  this.m_ButtonExit.TabIndex = 9;
                  this.m_ButtonExit.Text = "Exit";
                  this.m_ButtonExit.Click += new System.EventHandler(this.OnButtonExitClick);
                  //
                  // m_StatusBar
                  //
                  this.m_StatusBar.Location = new System.Drawing.Point(0, 324);
                  this.m_StatusBar.Name = "m_StatusBar";
                  this.m_StatusBar.Size = new System.Drawing.Size(424, 22);
                  this.m_StatusBar.TabIndex = 5;
                  //
                  // m_ButtonSortByName
                  //
                  this.m_ButtonSortByName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
                  this.m_ButtonSortByName.Location = new System.Drawing.Point(320, 72);
                  this.m_ButtonSortByName.Name = "m_ButtonSortByName";
                  this.m_ButtonSortByName.Size = new System.Drawing.Size(96, 23);
                  this.m_ButtonSortByName.TabIndex = 4;
                  this.m_ButtonSortByName.Text = "Sort By Name";
                  this.m_ButtonSortByName.Click += new System.EventHandler(this.OnButtonSortByNameClick);
                  //
                  // m_ButtonSortByValue
                  //
                  this.m_ButtonSortByValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
                  this.m_ButtonSortByValue.Location = new System.Drawing.Point(320, 104);
                  this.m_ButtonSortByValue.Name = "m_ButtonSortByValue";
                  this.m_ButtonSortByValue.Size = new System.Drawing.Size(96, 23);
                  this.m_ButtonSortByValue.TabIndex = 5;
                  this.m_ButtonSortByValue.Text = "Sort By Value";
                  this.m_ButtonSortByValue.Click += new System.EventHandler(this.OnButtonSortByValueClick);
                  //
                  // m_TextBoxEditItem
                  //
                  this.m_TextBoxEditItem.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.m_TextBoxEditItem.BackColor = System.Drawing.Color.White;
                  this.m_TextBoxEditItem.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
                  this.m_TextBoxEditItem.Location = new System.Drawing.Point(8, 280);
                  this.m_TextBoxEditItem.Name = "m_TextBoxEditItem";
                  this.m_TextBoxEditItem.Size = new System.Drawing.Size(304, 20);
                  this.m_TextBoxEditItem.TabIndex = 10;
                  this.m_TextBoxEditItem.Text = "";
                  this.m_TextBoxEditItem.Visible = false;
                  this.m_TextBoxEditItem.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.OnTextBoxEditItemKeyPress);
                  this.m_TextBoxEditItem.Leave += new System.EventHandler(this.OnTextBoxEditItemLeave);
                  //
                  // m_ContextMenuListBox
                  //
                  this.m_ContextMenuListBox.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
                                                                                                                                                 this.m_MenuItemEdit,
                                                                                                                                                 this.m_MenuItemDelete});
                  this.m_ContextMenuListBox.Popup += new System.EventHandler(this.OnMenuItemDeletePopup);
                  //
                  // m_MenuItemEdit
                  //
                  this.m_MenuItemEdit.Index = 0;
                  this.m_MenuItemEdit.Text = "Edit";
                  this.m_MenuItemEdit.Click += new System.EventHandler(this.OnListBoxPairsDoubleClick);
                  //
                  // m_MenuItemDelete
                  //
                  this.m_MenuItemDelete.Index = 1;
                  this.m_MenuItemDelete.Text = "Delete";
                  this.m_MenuItemDelete.Popup += new System.EventHandler(this.OnMenuItemDeletePopup);
                  this.m_MenuItemDelete.Click += new System.EventHandler(this.OnButtonDeleteClick);
                  //
                  // m_ListBoxPairs
                  //
                  this.m_ListBoxPairs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.m_ListBoxPairs.ContextMenu = this.m_ContextMenuListBox;
                  this.m_ListBoxPairs.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
                  this.m_ListBoxPairs.Location = new System.Drawing.Point(8, 72);
                  this.m_ListBoxPairs.Name = "m_ListBoxPairs";
                  this.m_ListBoxPairs.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended;
                  this.m_ListBoxPairs.Size = new System.Drawing.Size(304, 238);
                  this.m_ListBoxPairs.TabIndex = 3;
                  this.m_ListBoxPairs.DoubleClick += new System.EventHandler(this.OnListBoxPairsDoubleClick);
                  this.m_ListBoxPairs.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.OnListBoxPairsDrawItem);
                  //
                  // m_buttonLoad
                  //
                  this.m_buttonLoad.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
                  this.m_buttonLoad.Location = new System.Drawing.Point(320, 200);
                  this.m_buttonLoad.Name = "m_buttonLoad";
                  this.m_buttonLoad.Size = new System.Drawing.Size(96, 23);
                  this.m_buttonLoad.TabIndex = 8;
                  this.m_buttonLoad.Text = "Load";
                  this.m_buttonLoad.Click += new System.EventHandler(this.OnButtonLoadClick);
                  //
                  // FormMain
                  //
                  this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
                  this.CancelButton = this.m_ButtonExit;
                  this.ClientSize = new System.Drawing.Size(424, 346);
                  this.Controls.Add(this.m_TextBoxEditItem);
                  this.Controls.Add(this.m_TextBoxNewItem);
                  this.Controls.Add(this.m_StatusBar);
                  this.Controls.Add(this.m_ButtonDelete);
                  this.Controls.Add(this.m_ButtonAdd);
                  this.Controls.Add(this.m_LabelAddItem);
                  this.Controls.Add(this.m_LabelListBox);
                  this.Controls.Add(this.m_ButtonSave);
                  this.Controls.Add(this.m_ButtonExit);
                  this.Controls.Add(this.m_ButtonSortByName);
                  this.Controls.Add(this.m_ButtonSortByValue);
                  this.Controls.Add(this.m_ListBoxPairs);
                  this.Controls.Add(this.m_buttonLoad);
                  this.MinimumSize = new System.Drawing.Size(400, 300);
                  this.Name = "FormMain";
                  this.Text = "Key/Value Pair Entry Program";
                  this.Load += new System.EventHandler(this.OnFormMainLoad);
                  this.ResumeLayout(false);
 
            }
            #endregion
 
            private void OnButtonAddClick(object sender, System.EventArgs e)
            {
                  Pair newItem = GetPairFromString( m_TextBoxNewItem.Text );
                  if( newItem != null )
                  {
                        m_Controller.Add( newItem );
                        m_TextBoxNewItem.Text = string.Empty;
                  }
            }
 
            private Pair GetPairFromString( string sString )
            {
 
                  string sMatchPattern = string.Format( m_sMatchPattern, m_Controller.Delimiter );
                  Regex objNotNaturalPattern = new Regex( sMatchPattern );
 
                  if( objNotNaturalPattern.IsMatch( sString ) )
                  {
                        string[] saNameValue = sString.Split( m_Controller.Delimiter );
                        return new Pair( saNameValue[0], saNameValue[1] );
                  }
                  else
                  {
                        string sMessage = string.Format( Messages.sWrongFormat, m_Controller.Delimiter );
                        MessageBox.Show( sMessage, Messages.sTitleError, MessageBoxButtons.OK, MessageBoxIcon.Stop );
                  }
                  return null;
            }
 
            private void OnButtonDeleteClick( object sender, System.EventArgs e )
            {
                  if( m_ListBoxPairs.SelectedItems.Count > 0 )
                  {
                        PairsArray pairs = new PairsArray();
                        for( int n = 0; n < m_ListBoxPairs.SelectedItems.Count; n++ )
                        {
                              Pair pair = m_ListBoxPairs.SelectedItems[ n ] as Pair;
                              if( pair != null )
                              {
                                    pairs.Add( pair );
                              }
                        }
                 
                        m_Controller.Delete( pairs );
                  }
            }
 
            private void OnButtonSaveClick( object sender, System.EventArgs e )
            {
                  SaveFileDialog sfd = new SaveFileDialog();
                  sfd.AddExtension = true;
                  sfd.CheckPathExists = true;
                  sfd.DefaultExt = m_sFileExtension;
 
                  if( sfd.ShowDialog( this ) == DialogResult.OK )
                  {
                        string sMessage = string.Empty;
                        if( !m_Controller.SaveToXML( sfd.FileName, ref sMessage ) )
                        {
                              DialogResult result = MessageBox.Show( sMessage, Messages.sTitleError,
                                    MessageBoxButtons.OK, MessageBoxIcon.Stop );
                        }
                  }
            }
           
            private void OnButtonLoadClick(object sender, System.EventArgs e)
            {
                  OpenFileDialog sfd = new OpenFileDialog();
                  sfd.CheckPathExists = true;
                  sfd.CheckFileExists = true;
                  sfd.DefaultExt = m_sFileExtension;
 
                  if( sfd.ShowDialog( this ) == DialogResult.OK )
                  {
                        string sMessage = string.Empty;
                        if( !m_Controller.LoadFromXML( sfd.FileName, ref sMessage ) )
                        {
                              DialogResult result = MessageBox.Show( sMessage, Messages.sTitleError,
                                    MessageBoxButtons.OK, MessageBoxIcon.Stop );
                        }
                  }
            }
 
            private void OnButtonSortByValueClick(object sender, System.EventArgs e)
            {
                  m_Controller.Sort( CompareBy.Value );
            }
 
            private void OnButtonSortByNameClick(object sender, System.EventArgs e)
            {
                  m_Controller.Sort( CompareBy.Name );
            }
 
            private void OnButtonExitClick( object sender, System.EventArgs e )
            {
                  Close();
            }
 
            private void Clear()
            {
                  m_ListBoxPairs.Items.Clear();
            }
 
            private void OnFormMainLoad(object sender, System.EventArgs e)
            {
                  OnReload( sender, e );
            }
 
            private void OnListBoxPairsDoubleClick( object sender, System.EventArgs e )
            {
           
                  int itemSelected = m_ListBoxPairs.SelectedIndex;
                  if( itemSelected != -1 )
                  {
                        string itemText = PairToString( m_ListBoxPairs.Items[ itemSelected ] as Pair );
                                                                                           
                        Rectangle r = m_ListBoxPairs.GetItemRectangle( itemSelected );
                        m_TextBoxEditItem.Location = new System.Drawing.Point( r.X + m_ListBoxPairs.Location.X, r.Y + m_ListBoxPairs.Location.Y );
                        m_TextBoxEditItem.Size=new System.Drawing.Size( r.Width+4 , r.Height-10 );
                        m_TextBoxEditItem.Visible=true;
                        m_TextBoxEditItem.Text=itemText;
                        m_TextBoxEditItem.Focus();
                        m_TextBoxEditItem.SelectAll();           
                  }
            }
 
            private string PairToString( Pair pair )
            {
                  string sOut = string.Empty;
                  if( pair != null )
                  {
                        sOut = pair.Name + m_Controller.Delimiter + pair.Value;
                  }
                  return sOut;
            }
 
            private void OnMenuItemDeletePopup(object sender, System.EventArgs e)
            {
                  if( m_ListBoxPairs.SelectedIndex == -1 )
                  {
                        m_MenuItemDelete.Enabled =false;
                        m_MenuItemEdit.Enabled = false;
                  }
                  else
                  {
                        m_MenuItemDelete.Enabled = true;
                        m_MenuItemEdit.Enabled = true;
                  }
            }
           
            #region Controller Events
 
            private void OnEnableDelete( bool bEnable )
            {
                  m_ButtonDelete.Enabled = bEnable;
                  m_ButtonSave.Enabled = bEnable;
            }
 
            private void OnEnableSort( bool bEnable )
            {
                  m_ButtonSortByName.Enabled = bEnable;
                  m_ButtonSortByValue.Enabled = bEnable;
            }
 
            private void OnItemAdded( Pair pair )
            {
                  if( pair != null )
                  {
                        m_ListBoxPairs.Items.Add( pair );
                        for( int n = m_ListBoxPairs.SelectedIndices.Count; n > 0 ; n-- )
                        {
                              m_ListBoxPairs.SetSelected( m_ListBoxPairs.SelectedIndices[ n - 1 ], false );
                        }                      
                        m_ListBoxPairs.SelectedItem = pair;
                  }
            }
 
            private void OnItemDeleted( Pair deleteItem )
            {
                  if( deleteItem != null )
                  {
                        m_ListBoxPairs.Items.Remove( deleteItem );
                  }
            }
           
            private void OnItemUpdated( Pair pair )
            {
                  int nPos = m_ListBoxPairs.Items.IndexOf( pair );
                  if( pair != null )
                  {
                        m_ListBoxPairs.Invalidate( m_ListBoxPairs.GetItemRectangle( nPos ) );
                        m_ListBoxPairs.Update();
                       
                  }
            }
 
            private void OnReload( object sender, System.EventArgs e )
            {
                  object objCurrent = m_ListBoxPairs.SelectedItem;
 
                  Clear();
 
                  for( int i = 0; i < m_Model.Count; i++ )
                  {
                        m_ListBoxPairs.Items.Add( m_Model[ i ] );
                  }
 
                  if( objCurrent != null )
                  {
                        m_ListBoxPairs.SelectedIndex = m_ListBoxPairs.Items.IndexOf( objCurrent );
                  }
            }
 
            #endregion
 
            private void OnListBoxPairsDrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
            {
                  Brush myBrush = Brushes.Black;
 
                  if( ( e.State & DrawItemState.Selected ) > 0 )
                  {
                        myBrush = Brushes.White;
                  }
                  string sText = string.Empty;
                  if( e.Index != -1 )
                  {
                        sText = PairToString( m_ListBoxPairs.Items[e.Index] as Pair );
                  }
                  e.DrawBackground();
                  e.Graphics.DrawString( sText, e.Font, myBrush,e.Bounds,StringFormat.GenericDefault);
                  e.DrawFocusRectangle();      
            }
 
            private void OnTextBoxEditItemLeave(object sender, System.EventArgs e)
            {
                  m_TextBoxEditItem.Visible = false;       
            }
 
            private void OnTextBoxEditItemKeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
            {
                  if ( e.KeyChar == m_nKeyEnter )
                  {
                        Pair newItem = GetPairFromString( m_TextBoxEditItem.Text );
                        if( newItem != null )
                        {
                              m_Controller[ m_ListBoxPairs.SelectedIndex ] =  newItem;                  
                              m_TextBoxEditItem.Visible = false;
                        }
                  }
                  if ( e.KeyChar == m_nKeyEscape )
                        m_TextBoxEditItem.Visible = false;       
            }
      }
}
Controller.cs
Controller.cs

using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
 
using Sample.Model;
using Sample.View;
 
 
namespace Sample.Controller
{
      /// <summary>
      /// Summary description for Controller.
      /// </summary>
      public class DashBoard
      {
            public delegate void EventHandlerItemUpdated( Pair item );
            public delegate void EventHandlerEnable( bool bEnable );
 
            private event EventHandler m_EventDataChanged = null;      
            private event EventHandlerItemUpdated m_EventItemAdded = null;
            private event EventHandlerItemUpdated m_EventItemDeleted = null;
            private event EventHandlerItemUpdated m_EventItemUpdated = null;
            private event EventHandlerEnable m_EventEnableSort = null;
            private event EventHandlerEnable m_EventEnableDelete = null;
 
            private PairsArray m_Model = null;
            private FormMain m_View = null;
 
            private CompareBy m_SortBy = CompareBy.Name;
            private const char m_sDelimiter = '=';
 
            public DashBoard()
            {
            }
 
            #region Events
            public event EventHandler EventDataChanged
            {
                  add
                  {
                        m_EventDataChanged += value;
                  }
                  remove
                  {
                        m_EventDataChanged -= value;
                  }
            }
 
            public event EventHandlerItemUpdated EventItemUpdated
            {
                  add
                  {
                        m_EventItemUpdated += value;
                  }
                  remove
                  {
                        m_EventItemUpdated -= value;
                  }
            }
 
            public event EventHandlerItemUpdated EventItemAdded
            {
                  add
                  {
                        m_EventItemAdded += value;
                  }
                  remove
                  {
                        m_EventItemAdded -= value;
                  }
            }
 
            public event EventHandlerItemUpdated EventItemDeleted
            {
                  add
                  {
                        m_EventItemDeleted += value;
                  }
                  remove
                  {
                        m_EventItemDeleted -= value;
                  }
            }
 
            public event EventHandlerEnable EventEnableSort
            {
                  add
                  {
                        m_EventEnableSort += value;
                  }
                  remove
                  {
                        m_EventEnableSort -= value;
                  }
            }
 
            public event EventHandlerEnable EventEnableDelete
            {
                  add
                  {
                        m_EventEnableDelete += value;
                  }
                  remove
                  {
                        m_EventEnableDelete -= value;
                  }
            }
            #endregion
 
            public char Delimiter
            {
                  get
                  {
                        return m_sDelimiter;
                  }
            }
            public PairsArray Model
            {
                  set
                  {
                        m_Model = value;
                        m_Model.SortBy( m_SortBy );
                  }
                  get
                  {
                        return m_Model;
                  }
            }
 
            public FormMain View
            {
                  set
                  {
                        m_View = value;
                  }
                  get
                  {
                        return m_View;
                  }
            }
 
            public void Sort( Model.CompareBy sortBy )
            {
                  m_SortBy = sortBy;
                  m_Model.SortBy( sortBy );
                  DataChanged();
            }
 
            public void Add( Pair pair )
            {
                  m_Model.Add( pair );
                  ItemAdded( pair );
                  m_Model.SortBy( m_SortBy );
                  DataChanged();
 
                  EnableButtons();
            }
 
            public Pair this[ int nPos ]
            {
                  set
                  {
                        if( value != null && nPos < m_Model.Count )
                        {
                              bool bSortRequired = false;
 
                              switch( m_SortBy )
                              {
                                    case CompareBy.Name:
                                          if( !m_Model[ nPos ].Name.Equals( value.Name ) )
                                          {
                                                bSortRequired = true;
                                          }
                                          break;
                                    case CompareBy.Value:
                                          if( !m_Model[ nPos ].Value.Equals( value.Value ) )
                                          {
                                                bSortRequired = true;
                                          }
                                          break;
                                    default:
                                          break;
                              }
 
                              m_Model[ nPos ] = value;
                             
                              if( !bSortRequired )
                              {
                                    ItemUpdated( m_Model[ nPos ] );
                              }
                              else
                              {
                                    m_Model.SortBy( m_SortBy );
                                    DataChanged();
                              }
                        }
                  }
            }
 
            public void Delete( Model.PairsArray pairs )
            {
                  if( pairs != null )
                  {
                        if( pairs.Count > 0 )
                        {
                              for( int n = 0; n < pairs.Count; n++ )
                              {
                                    m_Model.Delete( pairs[ n ] );
                                    ItemDeleted( pairs[ n ] );
                              }
 
                              EnableButtons();
                        }
                  }
            }
 
            public bool LoadFromXML( string sFileName, ref string sMessage )
            {
                  bool bSuccess = true;
                  PairsArray pairsArray = null;
                  XmlSerializer serializer = null;
                  XmlTextReader reader = null;
                  try
                  {
                        serializer = new XmlSerializer( m_Model.GetType() );
                        reader = new XmlTextReader( sFileName );
                        pairsArray = serializer.Deserialize( reader ) as PairsArray;
                  }
                  catch( Exception ex )              
                  {                                  
                        if( null != ex.InnerException )              
                        {  
                              sMessage += string.Format( Messages.sExceptionInfo, ex.Message, ex.GetType().FullName, ex.Source, ex.StackTrace, ex.TargetSite );
                        }  
                        bSuccess = false;
                  } 
                  finally
                  {
                        reader.Close();
 
                        if( pairsArray != null )
                        {
                              m_Model.Clean();
                              m_Model.Copy( pairsArray );
                              m_Model.SortBy( m_SortBy );
                              DataChanged();
 
                              EnableButtons();
                        }
                  }
                  return bSuccess;
            }
 
            public bool SaveToXML( string sFileName, ref string sMessage )
            {
                  bool bSuccess = true;
                  // Serialize the object to a file.
                  try
                  {
                        XmlSerializer serializer = new XmlSerializer( m_Model.GetType() );
                        XmlTextWriter writer = new XmlTextWriter( sFileName, System.Text.Encoding.Unicode );
                        writer.Formatting = Formatting.Indented;
                        serializer.Serialize( writer, m_Model );
                        writer.Close();
                  }
                  catch( Exception ex )              
                  {                                  
                        if( null != ex.InnerException )              
                        {  
                              sMessage += string.Format( Messages.sExceptionInfo, ex.Message, ex.GetType().FullName, ex.Source, ex.StackTrace, ex.TargetSite );
                        }        
                        bSuccess = false;
                  } 
                  return bSuccess;
            }
 
            #region Private methods
 
            private void EnableButtons()
            {
                  EnableSort( m_Model.Count > 1 );
                  EnableDelete( m_Model.Count > 0 );
            }
 
            private void DataChanged()
            {
                  if( m_EventDataChanged != null )
                  {
                        EventsHelper.Fire( m_EventDataChanged, this, new EventArgs() );
                  }
            }
 
            private void ItemAdded( Pair newItem )
            {
                  if( m_EventItemAdded != null )
                  {
                        EventsHelper.Fire( m_EventItemAdded, newItem );
                  }
            }
 
            private void ItemUpdated( Pair updatedItem )
            {
                  if( m_EventItemUpdated != null )
                  {
                        EventsHelper.Fire( m_EventItemUpdated, updatedItem );
                  }
            }
 
            private void ItemDeleted( Pair deletedItem )
            {
                  if( m_EventItemDeleted != null )
                  {
                        EventsHelper.Fire( m_EventItemDeleted, deletedItem );
                  }
            }
 
            private void EnableSort( bool bEnable )
            {
                  if( m_EventEnableSort != null )
                  {
                        EventsHelper.Fire( m_EventEnableSort, bEnable );
                  }
            }
 
            private void EnableDelete( bool bEnable )
            {
                  if( m_EventEnableDelete != null )
                  {
                        EventsHelper.Fire( m_EventEnableDelete, bEnable );
                  }
            }
            #endregion
      }
}

 

EventsHelper.cs

using System;

 

namespace Sample.Controller
{
      /// <summary>
      /// AsyncHelper.
      /// </summary>
      public class AsyncHelper
      {
            delegate void DynamicInvokeShimProc( Delegate d, object[] args );
 
            static DynamicInvokeShimProc dynamicInvokeShim = new DynamicInvokeShimProc(DynamicInvokeShim);
            static AsyncCallback dynamicInvokeDone = new AsyncCallback( DynamicInvokeDone );
 
            public static void FireAndForget( Delegate d, params object[] args )
            {
                  dynamicInvokeShim.BeginInvoke( d, args, dynamicInvokeDone, null );
            }
 
            static void DynamicInvokeShim( Delegate d, object[] args )
            {
                  d.DynamicInvoke( args );
            }
 
            static void DynamicInvokeDone( IAsyncResult ar )
            {
                  dynamicInvokeShim.EndInvoke( ar );
            }
      }
      /// <summary>
      /// EventsHelper.
      /// </summary>
      public class EventsHelper
      {
            public static void FireAsync( Delegate del, params object[] args )
            {
                  if ( del == null )
                  {
                        return;
                  }
                  Delegate[] delegates = del.GetInvocationList();
                  AsyncFire asyncFire;
                  foreach ( Delegate sink in delegates )
                  {
                        asyncFire = new AsyncFire( InvokeDelegate );
                        AsyncHelper.FireAndForget( asyncFire, sink, args );
                  }
            }
 
            delegate void AsyncFire( Delegate del, object[] args );
 
            static void InvokeDelegate( Delegate del, object[] args )
            {
                  del.DynamicInvoke( args );
            }
 
            public static void Fire( Delegate del, params object[] args )
            {
                  if ( del == null )
                  {
                        return;
                  }
                 
                  Delegate[] delegates = del.GetInvocationList( );
 
                  foreach (Delegate sink in delegates)
                  {
                        try
                        {
                              sink.DynamicInvoke( args );
                        }
                        catch
                        {
                       
                        }
                  }
            }
      }
}
SampleApp.cs
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
 
using Sample.Model;
using Sample.View;
using Sample.Controller;
 
namespace Sample
{
      /// <summary>
      /// Summary description for SampleApp.
      /// </summary>
      class SampleApp
      {
            /// <summary>
            /// The main entry point for the application.
            /// </summary>
            [STAThread]
            static void Main()
            {
                  // new Model
                  Sample.Model.PairsArray pairsArray = new PairsArray();
                 
                  pairsArray.Add( new Pair( "Zebra", "Africa" ) );
                  pairsArray.Add( new Pair( "Kiwi", "NewZealand" ) );
                  pairsArray.Add( new Pair( "AmazonaParrot", "Jamaica" ) );
                  pairsArray.Add( new Pair( "Tiger", "India" ) );
                  pairsArray.Add( new Pair( "Squirrel", "Canada" ) );
                  pairsArray.Add( new Pair( "Kangaroo", "Australia" ) );
                  pairsArray.Add( new Pair( "Wolf", "Russia" ) );
 
                  pairsArray.SortBy( CompareBy.Name );
 
                  // new Controller
                  DashBoard ctrl = new DashBoard();
                 
                  // new View
                  Application.Run( new FormMain( pairsArray, ctrl ) );
            }
      }
}

 


Andrey Eliseev