Source Code -
Download DemoMain_v_1.0.zip - 1.48 MB
DB is inside - DemoCustomerFrms\DB\Raptor_Core_Local_DB_Backup_17_11_2010
Check - Rocket Framework (.Net 4.0) to see how this is being used in practise
Table of Contents
- Introduction
- Background
- How You Use a Framework?
- Let's Have a Look at Our Demo Framework
- How We Build Our Framework Gradually?
- High Level Design Overview
- Let's look at the ‘Demo.Framework’ Module Design
- Have a Look at the ‘BaseGridView’ Code Below
- Let's look at the ‘Demo.Data’ Module Design
- Let's look at the ‘Demo.Core’ Module Design
- Let's look at the ‘Demo.Core.UserControls’
- Module Design Implementation of Data Controls
- Final Note
- History
Introduction
Web system designing is well supported with many online materials. In contrast to that the support materials available for Windows-Form based (desktop applications) system designing is lesser. In 1980s, during the era of two-tier systems the Desktop applications ruled the software world. Now the time has changed and all software geeks are now focused on web systems leaving developers to design desktop applications at their will. Today you hardly see any architectural discipline being adopted in desktop applications. Therefore I thought of shading some lights over this ignored sector to improve the designing aspect of desktop applications.
In this attempt, I will demonstrate architecting a simple desktop application. Additionally, I will also develop a reusable Framework that you can use to develop any desktop application.
When I talk about ‘Framework’, you may have thought what a framework really is. I see there are many misinterpretations around the term ‘Framework’. So I will give you an introduction to ‘Framework’ too. That I will do at the early part of this write-up. But even before that, let me tell you how the idea to write this article have started.
Background
We, in our company, are developing a large windows form based application on .net framework 4.0 in visual studio 2010 these days. In that, we had a need to develop an administrative user interface (to do CRUD operations of master records), where users with certain rights can directly edit certain element of the database tables. So we needed a set of screens, where they can edit the records of DB tables. Once of such screen is given below for your reference. You may develop this screen in a matter of 20 minutes using our 'Framework'.

During the initial stages of this project, as usual, I spent few days researching to find the right mix of technologies and design approaches to form the design of this system. I thought of developing a generic Framework having the main goal to support editing of DB records. That Framework have planned in such a manner so that we can use that as the basis for any 'Desktop Application' development. The main goal of this writing is to introduce that ‘Framework’ to our ‘CodeProject’ member community.
I thought that the system architecture not only should support editing database tables directly but also should support dealing with custom business objects that use many fields from different tables too. Developing a generic 'Framework' to support this is nearly impossible as different domains have different business needs. Therefore I thought that it is important to introduce multiple ‘wrapping’ layers on top of the generic framework to support handling domain specific needs. In another term this mean we need domain specific Framework on top of the generic framework. That is to implement unique domain specific features of systems. So going forward there will be several of Domain specific Frameworks adding to this library. Additionally to support customers very specific highly customized unique requirements, I planned having another layer on top of both generic Framework and the domain specific Framework.
If everything goes well, once this ‘Architecture’ is stable, when a new customer comes, we just have to get a copy of the generic Framework and the respective domain specific Framework from our software store and spent few days adding customer specific features to it before bundling everything to form the beta version of the release. Isn't that sounds so exciting?
How You Use a Framework?
Let’s examine the picture above. It is a framework and fairly easily you can form a room in it. Then with little more work you can extend the same to form a complex of rooms. Then you can develop few extensions to it to form either a 'Motel', 'Hotel' or a luxury apartment complex. This is how you use a framework to build something quicker. This very same concept is what we are going to use to build our software system too.
This concept of building on Framework is not new. Even the language you use, .Net/C#, is a Framework. Not only that, .Net have their newer Frameworks built on top of older Frameworks too. In the next part of the article you will see how .Net Framework 3.0 is being built on top of the .Net Framework 2.0.
Let's Have a Look at Our Demo Framework

The picture shows how Microsoft has built .Net framework 3.0 on top of .Net framework 2.0. This shows how one framework can be used to build another on top of it.
As the picture above, we will be building our framework on top of .Net framework 4.0. That is the latest version of the .Net Framework family.
How We Build Our Framework Gradually?

This shows a strategy that you can use to develop a ‘Framework’ without spending too much of your time initially. Firstly you need to have guts to say ‘NO’ to the piles of ever growing 'What if' type requirements that people around you want to add to the ‘Framework’. You need to define the boundaries of the Framework. Then you need to have some strategies in place so that you can build this system with a waist of your time.
As you can see in the picture you have to identify a set of core features for the main Framework. Then let the domain specific framework auto-build through every custom implementation.
High Level Design Overview

This is the package diagram of our system. I have used some color coding to make it easier for you to understand.
As you can see there are few modules being used to developed this system. You can see the generic 'Demo.Framework' given right at the top. Then by using that the core business logics as well as the use controls libraries are formed. The ‘Demo.Common’ is common for all and something that we will be sharing across multiple (All) packages. The data access layer (Demo.Data) library is developed using Microsoft ADO.NET Entity Framework.
Let's look at the ‘Demo.Framework’ Module Design
Enough of talking, let's jump right in to the water now. Don't be afraid, I know for a fact that some people are born afraid of system architecture. I too had it for ‘Chemistry’. I know for a fact that I didn’t have the right start to it. In software world too, I have seen overly designed systems with never ending class hierarchies. Some of these systems are far more complicated for their requirements. But here in our design, I have kept it very simple. I wanted everyone to understand, be able to maintain and also upgrade the system easily.
This design has few but valuable 'interfaces'. Do not think that these interfaces make the design look complex or elegant, rather I have added them with valid reasons and to achieve certain things that wouldn’t have been possible otherwise. I will explain those design moves later in this article.

There are seven class interfaces that are being used here. As you may have heard, in general ‘Interface’ separates the definition of an object from its implementation. If you think of an interface as a contract, it is clear that both sides of the contract have a role to play. The publisher (or the framework side of the equation) of the interface agrees never to change that interface, and the implementer (or the custom implementation side of the equation) agrees to implement the interface exactly as it was designed. This helps using the interface across many other generic classes even without knowing their implementation.
IHandler<V> - This is the generic interface that uses to implement all handler classes. The classes of type ‘Handler’ are the ones that used to implement handlings for all business objects of this system.
IMapper<V, D> - This wrap a third party library called Auto-Mapper. The Auto-Mapper (ref http://automapper.codeplex.com/) uses a convention-based matching algorithm to match up source object to its destination object values.
IObject – This interface is what implements the abstract ‘BaseObject’ which used as the base class for all business objects.
Note: ‘V’ stands for the view or business object whereas ‘D’ stands for the data objects or a table of the database.
IDataContainer<V> - This is what used to implement the ‘BaseData<V>’, the generic user control. I must tell you that Microsoft is still not ready to neither support applying object oriented programming concepts for user controls nor developing generic user controls. The IDE start giving you troubles immediately after it sees the ‘<’ mark. The beauty of the generic implementation made me ignore that hassle. As you are following my directions you will also have to face the troubles that this visual studio IDE (I saw this bug was reported around 2003 but still there in VS 2010 too) going to throw at you.
IGridView – This is the interface that uses to implement the generic ‘BaseGridView<H, V>’. In this ‘H’ is of type ‘IHandler<V>’ where as ‘V’ is of type ‘BaseObject’.
This ‘Framework’ is elegantly designed with minimum complexity. I know that some of you still may find it difficult to understand. If you don’t have expertise dealing with generics, interfaces etc, then just find the pattern to using this 'Framework' and start using it, then slowly you will understand it. Even today there are many developers taking advantage of this to build their applications without having a clue about the design of the 'Framework' or neither generics nor interfaces. Once understood, the pattern of the design itself will guide you to use it.
Have a Look at the ‘BaseGridView’ Code Below
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Demo.Framework
{
public abstract partial class BaseGridView<H, V> : BaseControl, IGridView
where H : IHandler<V>
where V: BaseObject
{
public event DataGridViewCellEventHandler DataRowSelected;
public BaseGridView(H h)
{
InitializeComponent();
_hadler = h;
}
private H _hadler;
public virtual void LoadData()
{
this.DataList = _hadler.GetAll();
}
private V _selectedItem;
public V SelectedItem
{
get { return _selectedItem; }
}
private List<V> _originalList = null;
private List<V> _dataList;
public List<V> DataList
{
get { return _dataList; }
set
{
_dataList = value;
this.dataGridViewMain.DataSource = null;
if (_dataList == null) return;
this.dataGridViewMain.Columns.Clear();
this.dataGridViewMain.AutoGenerateColumns = true;
this.dataGridViewMain.DataSource = this._dataList;
OnHideColumns(ref dataGridViewMain);
if (_dataList.Count > 0)
{
int cCount = this.dataGridViewMain.Columns.Count;
foreach (KeyValuePair<string, object> item in _dataList[0].Data)
{
DataGridViewTextBoxColumn mRef = new DataGridViewTextBoxColumn();
mRef.HeaderText = item.Key;
this.dataGridViewMain.Columns.Add(mRef);
}
for (int i = 0; i < dataGridViewMain.RowCount; i++)
{
int c = cCount;
IObject boundItem = (V)dataGridViewMain.Rows[i].DataBoundItem;
foreach (KeyValuePair<string, object> item in boundItem.Data)
this.dataGridViewMain[c++, i].Value = item.Value;
}
}
}
}
protected virtual void OnHideColumns(ref DataGridView dataGridView)
{
dataGridView.Columns["Id"].Visible = false;
dataGridView.Columns["Data"].Visible = false;
dataGridView.Columns["SourcePoco"].Visible = false;
}
public string Heading
{
get { return groupBoxMain.Text; }
set { groupBoxMain.Text = value; }
}
protected List<V> Search(string query)
{
List<V> list = new List<V>();
string[] qItems = query.Split(' ');
foreach (V item in _dataList)
{
for (int i = 0; i < qItems.Length; i++)
if (item.FoundIt(qItems[i]))
list.Add(item);
}
return (list.Count > 0) ? list : null;
}
private void buttonSearch_Click(object sender, EventArgs e)
{
if (_originalList == null)
{
_originalList = _dataList;
return;
}
string s = this.textBoxSearch.Text.Trim();
this.dataGridViewMain.Columns.Clear();
if (string.IsNullOrEmpty(s))
this.DataList = _originalList;
else
this.DataList = this.Search(s);
}
private void dataGridViewMain_RowEnter(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0)
_selectedItem = (V)dataGridViewMain.Rows[e.RowIndex].DataBoundItem;
if (DataRowSelected != null)
DataRowSelected(_selectedItem, e);
}
private void buttonRefresh_Click(object sender, EventArgs e)
{
LoadData();
this.textBoxSearch.Text = "";
}
}
}
I want you to focus on the top part of this generic abstract class named ‘BaseGridView<H>’. In the implementation, the constructor of it is being used to get the implemented version of the handler (H) pass on to it. Since all handlers of our system are implementing ‘IHander’, the system has the advantage of using the built-in methods of handlers via the IHandler interface. Interestingly we do this even without knowing the actual implementation of it.This is a very interesting part of our code. You will be able to understand this design approach more as you read along this article. In the same time I invite you to carefully examine the source code too.
BaseHandler<V, D> - This abstract class implements both ‘IHandler’ and ‘IMapper’ interfaces. It contains the generic implementation for handling business objects. All these classes and interfaces use two generics namely ‘V’ and ‘D’ of type ‘BaseObject’ and ‘EntityObject’. The ‘BaseHandler’ associate with the ‘IRepository<D>’ interface too. That association is used to hide repository implementation. The repository object which passes on to this by the time of implementing specific handler is used to retrieve data related to a particular data object from the database. There the Auto-Mapper is used to map them from ‘D’ of type ‘EntityObject’ to ‘V’ of type ‘BaseObject’ and pass that on to the next layer. The implementation of ‘IRepository<D>’ is done under ‘Demo.Data’ library. I will explain that part later.
The base classes are helping us to do the generic implementation once and reuse them with multiple types.
Let's look at the ‘Demo.Data’ Module Design
The system uses ADO.NET Entity Framework (.NET 4) to generate the data access layer (Ref: http://msdn.microsoft.com/en-us/data/ee712907.aspx). I also went on to use T4 template, but later found that the special generic repository implementation that I am introducing here is not easily doable with t4 template. So I just use the default code that entity framework generate. For your understanding have a look at the generic repository code below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects;
using System.Linq.Expressions;
using System.Data;
using System.Data.Objects.DataClasses;
using System.Data.Common;
using Demo.Framework;
namespace Demo.Data
{
public sealed class DemoRepository<T> : IRepository<T>
where T : EntityObject
{
static readonly DemoRepository<T> instance
= new DemoRepository<T>(new DemoCoreEntities());
private static readonly Object _lockObject = new Object();
static DemoRepository()
{
if (_lockObject == null)
_lockObject = new Object();
}
public static DemoRepository<T> Instance
{
get
{
return instance;
}
}
DemoRepository(ObjectContext repositoryContext)
{
_repositoryContext = repositoryContext ?? new DemoCoreEntities();
_objectSet = _repositoryContext.CreateObjectSet<T>();
}
private static ObjectContext _repositoryContext;
private ObjectSet<T> _objectSet;
public ObjectSet<T> ObjectSet
{
get
{
return _objectSet;
}
}
#region IRepository Members
public IRepository<T> GetRepository()
{
return Instance;
}
public void Add(T entity)
{
lock (_lockObject)
{
this._objectSet.AddObject(entity);
_repositoryContext.SaveChanges();
_repositoryContext.AcceptAllChanges();
}
}
public void Update(T entity)
{
lock (_lockObject)
{
_repositoryContext.ApplyOriginalValues(((IEntityWithKey)entity)
.EntityKey.EntitySetName, entity);
_repositoryContext.Refresh(RefreshMode.ClientWins, _objectSet);
_repositoryContext.SaveChanges();
_repositoryContext.AcceptAllChanges();
}
}
public void Delete(T entity)
{
lock (_lockObject)
{
this._objectSet.DeleteObject(entity);
_repositoryContext.Refresh(RefreshMode.ClientWins, _objectSet);
_repositoryContext.SaveChanges();
_repositoryContext.AcceptAllChanges();
}
}
public void DeleteAll()
{
_repositoryContext
.ExecuteStoreCommand("DELETE " + _objectSet.EntitySet.ElementType.Name);
}
public IList<T> GetAll()
{
lock (_lockObject)
{
return this._objectSet.ToList<T>();
}
}
public IList<T> GetAll(Expression<Func<T, bool>> whereCondition)
{
lock (_lockObject)
{
return this._objectSet.Where(whereCondition).ToList<T>();
}
}
public T GetSingle(Expression<Func<T, bool>> whereCondition)
{
lock (_lockObject)
{
return this._objectSet.Where(whereCondition).FirstOrDefault<T>();
}
}
public IQueryable<T> GetQueryable()
{
lock (_lockObject)
{
return this._objectSet.AsQueryable<T>();
}
}
public long Count()
{
lock (_lockObject)
{
return this._objectSet.LongCount<T>();
}
}
public long Count(Expression<Func<T, bool>> whereCondition)
{
lock (_lockObject)
{
return this._objectSet.Where(whereCondition).LongCount<T>();
}
}
#endregion
}
}
The code you see above is a special one. I am trying to use the 'Generic' repository as effectively as possible. As you can see the repository implemented as a single object. The thread safe version of singleton design pattern is used to implement the repository. In addition to that, an additional protection is done using ‘_lockObject’ to allow concurrent access to repository. Just to demonstrate the usage of SQL command directly inside the repository the ‘DeleteAll()’ method is written.
Let's look at the ‘Demo.Core’ Module Design
The ‘Demo.Core’ is used to implement all business objects and their handling functions. Going along with MVC architecture, I thought of naming all business objects with the ‘View’ suffix. So in the library you will find two types of classes. One is of type handler (which is a variant of Service class of MVC architecture) and other is of view type. Handler type classes are having all operations related to a particular view object. Let’s open the ‘HandlerApplicationUser’ class and see what we find inside it.

The class 'HandlerApplicationView' is as follows. It is pretty much an emtpy class. One requirement is to pass the correct instance of the repository to its base class. In addition to that if you want to do special mappings from ‘data object’ to ‘business object’ or wise versa then you need to update the mapping expression by using either ‘ForwardMap’ or ‘BackwardMap’ methods.
E.g. for 'BackwarMap': If you have ‘FirstName’, ‘Initials’, and ‘LastName’ in a one table and then the respective data object will have three fields with same names. If you want these three fileds of the dataobject to be mapped to a new field, called ‘Name’ of the business object then you can use the provided ‘BackwardMap’ mapping expression. As you can see the method ‘BackwardMap’ has IMappingExpression<ApplicationUser, ApplicationUserView>, which has, in our case, detail to map ‘ApplicationUser’ to ‘ApplicationUserView’.
Are lambda expressions bothering you? That is not difficult at all, you just have to understand the pattern, once understood, there is nothing difficult about lambda expressions.
The ‘LogPrefix’ property find it the handle class below is for log4net. If you look at ‘LoggerBase’ class inside the Common folder of the framework library, where you will understand that ‘Logger Base’ wrapped log4net functions, to gain the flexibility to replaced it with another.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Demo.Data;
using AutoMapper;
using Demo.Core.Models;
using Demo.Framework;
namespace Demo.Core
{
public class HandlerApplicationUser : BaseHandler<ApplicationUserView, ApplicationUser>
{
public HandlerApplicationUser()
: base(DemoRepository<ApplicationUser>.Instance)
{
}
public override void ForwardMap(IMappingExpression<ApplicationUserView, ApplicationUser> mappingExpression)
{
mappingExpression
.ForMember(dest => dest.ApplicationUserId, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Gender, opt => opt.MapFrom(src => (src.SelectedGender.Key == 1) ? true : false))
.ForMember(dest => dest.AdditionalData, opt => opt.MapFrom(src => (src.Data != null) ? src.Data.ToString() : ""));
}
public override void BackwardMap(AutoMapper.IMappingExpression<ApplicationUser, ApplicationUserView> mappingExpression)
{
mappingExpression
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.ApplicationUserId))
.ForMember(dest => dest.SelectedGender, opt => opt.MapFrom(src => (src.Gender) ? new KeyValuePair<int, string>(1, "Male") : new KeyValuePair<int, string>(2, "Female")))
.ForMember(dest => dest.SourcePoco, opt => opt.MapFrom(src => src))
.ForMember(dest => dest.Data, opt => opt.MapFrom(src => AdditionalData.Resolve(src.AdditionalData)));
}
protected override Type LogPrefix
{
get { return this.GetType(); }
}
}
}
Let's look at the ‘Demo.Core.UserControls’ Module Design
This is where we develop all user controls. There again, as I said before, I am using generics extensively. But the design approach is pretty much like as it was with above modules. So I will leave it up to you to look at the source coded and understand it.
ApplicationUserData – This is a user control created by extending ‘BaseData’. This is where you will have textboxes, dropdowns, and labels allowing viewing, editing of business objects. There are few steps that you need to follow when creating such Data Control.
-
Create the business object (name it as ApplicationUserView)
-
Create the handler class to handle the business object (name it as HandlerApplicationUser)
-
Add a new ‘Data Source’ by choosing ‘Object’ as the source (look at the screen below you need to click on 'New Data Source' here). That will lets you choose objects that can be used to generate the required data-bound control. You need to find the correct class library first and then need to select the right business object to create this new ‘Data Source’ (in our case you I have picked 'ApplicationUserView' as the source object).

-
Add a new user control giving a standard name like <name of the business object>Data.cs (in our case it was 'ApplicationUserData').
-
Drag the whole view on to the user control and delete the ones that you don't want the user to view/ edit on to the user control.
-
Navigate to the the source code of this newly added user control and change the parent class from “: UserControl” to “: BaseData<name of the business object>” (in our case it was BaseData<ApplicationUserView>). This change will made you override three methods as explained in “Implementation of Data Controls” section below.
Note: Editing of these user controls using the IDE is not possible as visual studio does not understand generic type user controls. If you want to edit the data controls after they are built then you need to comment all three overridden methods and change the parent class back to “UserControl”. This will allow you to use the IDE for control editing. Once the editing is completed you need to role the changes back to their originals.
ApplicationUserDataGridView – This user control is capable of listing down a set of business objects. It also has a generic search. You can control the name of the columns of the grid by using the ‘DisplayNameAttribute’. Other than that this has nothing much for the one who uses it to do. You can check the source code to understand various things possible to do here. One thing to note here is that you can use the method name “OnHideColumns” if you want to hide a particular column just before displaying.
ApplicationUserManager – This is the main manager control that helps you do all CRUD operations related to a business object. You just have to pass few parameters up to the base class and that will take care of the rest for you.
Implementation of Data Controls
If you have to write any code in this library that is only inside the corresponding Data control. I have given you one such implementation below.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Demo.Framework;
using Demo.Core.Models;
using Demo.Common;
namespace Demo.Core.Controls.ChildControls
{
public partial class ApplicationUserData : BaseData<ApplicationUserView>
{
public ApplicationUserData()
{
InitializeComponent();
}
public override void SetBindingSource(ref BindingSource DemoBindingSource)
{
DemoBindingSource = this.applicationUserViewBindingSource;
}
public override ApplicationUserView MapIt(int Id)
{
ApplicationUserView apView = this.BindedView;
apView.Id = Id;
apView.UserName = this.userNameTextBox.Text;
apView.Password = this.passwordTextBox.Text;
apView.CustomerId = NullHandler.ConvertToInt(this.customerIdTextBox.Text);
apView.SelectedGender = (KeyValuePair<int, string>)this.genderCombo1.SelectedItem;
apView.FirstName = this.firstNameTextBox.Text;
apView.ShortName = this.shortNameTextBox.Text;
apView.MiddleName = this.middleNameTextBox.Text;
apView.LastName = this.lastNameTextBox.Text;
apView.StatusTypeId = NullHandler.ConvertToInt(this.statusTypeIdTextBox.Text);
apView.LastLoginDateTime = this.lastLoginDateTimeDateTimePicker.Value;
apView.Email = this.emailTextBox.Text;
apView.Phone = this.phoneTextBox.Text;
return apView;
}
protected override void _DemoBindingSource_DataSourceChanged(object sender, EventArgs e)
{
if (this.BindedView != null)
this.genderCombo1.SelectedItem = this.BindedView.SelectedGender;
}
}
}
Final Note
The demonstration provided here "as is" without warranty of any kind. I make no warranties, express or implied, that the source code given here is free of error or that they will meet your requirements for any particular application. It is up to you to evaluate learn review and use it as applicable.
History