Click here to Skip to main content
15,881,248 members
Articles / Desktop Programming / Win32
Article

Model View Presenter Using Dependency Injection and Threading Support

Rate me:
Please Sign up or sign in to vote.
4.73/5 (30 votes)
8 Jan 2008CPOL16 min read 113.2K   976   143   31
How to code the MVP pattern using Spring.Net.

Introduction

In this article, I will show how to use the Model View Presenter pattern to remove logic from the UI and move it to a Controller class. Furthermore, I will show an easy way to enable threading within the application and follow the rule of "no business work should be done in the UI thread". By applying threading correctly, you should never have a situation where the UI is blocked while processing some business work.

Logon Example

To show the MVP pattern and all its glory, I decided to use the logon screen example. We are all familiar with logon: a screen shows up asking for the user name and password. When the user presses the "Logon" button, work is initiated to validate if the user is authenticated on the system. Normally, logon is quick; however, in complex systems, logging-on can take a significant amount of time. I am going to show how to handle long operations with MVP, using a tiny framework which is based on the thread pool. But, first things first, let's define our screen.

Creating the View

Views are really our UI layer. Normally, these would be web pages or WinForms. In this example, I will use WinForms. Using MVP, the View should really expose no business logic whatsoever. In fact, if you need to add an if statement, you should ask yourself if it belongs in the View or the Controller; unless your if statement relates with UI work, it probably belongs in the Controller. The way I see Views in MVP, are as classes that, at the core, expose only properties and events - nothing else. There could be exceptions to this rule, but the goal is to make the View very simple. The View knows how to get data, and how to set data, but it doesn't know in which sequence or why. It is like a dummy puppet that provides all the ropes but without knowing the actual act.

Suppose our logon screen will have a user name field, password field, a status field (indicating if there is an error), and a button for performing the actual log-on. We can define the interface for the View without too much trouble.

C#
public interface ILogonView
{
  event EventHandler LogonEvent;
  void Notify(string notification);
  string Password { get; }
  string UserName { get; }
}

Notice, the interface contains a property with a setter and getter for each field, and an event for pressing the log-on button. I have also added the Notify method, so I can notify my View from the outside. This method is used to setup the status field indicating if the logon is successful or not. I will talk about this method later. Using the ILogonView interface, we get the extra benefit of having a View implemented in different ways; it can be a web page, or it can be a WinForm. It can even be a regular class - just used for testing.

C#
public partial class LogonForm : Form, ILogonView
{
  public event EventHandler LogonEvent;
  public LogonForm()
  {
     InitializeComponent();
  }

  /// <summary>
  /// Get the User Name from the username text box
  /// Trim the user name, and always return lower case
  /// </summary>
  public string UserName
  {
     get { return mTextBoxUserName.Text.Trim().ToLower(); }
  }

  /// <summary>
  /// Get the password from the password textbox
  /// </summary>
  public string Password
  {
     get
     {
        return mTextBoxPassword.Text;
     }
  }

  /// <summary>
  /// Update the screen with a message
  /// </summary>
  /// <param name="notification">Message to show on the status bar</param>
  public void Notify(string notification)
  {
     mToolStripStatusLabelStatus.Text = notification;
  }

  private void mButtonLogon_Click(object sender, EventArgs e)
  {
     // fire the event that the button was clicked.
     if (LogonEvent != null)
        LogonEvent(this, EventArgs.Empty);
  }
}

The Controller

The job of the Controller (or the presenter) is to handle the events coming from the View, and use the View getter and setter properties to define the behavior of the View. Think of the View as a data source; just like a data layer, you can query the View for information, and you set information to the View. The Controller is the only component that knows exactly how to manipulate the View, and how to call the setters and getters in the right sequence.

C#
public class LogonController
{
  private ILogonView mView;
  public LogonController(ILogonView view)
  {
     // register the view
     mView = view;

     // listen to the view logon event
     mView.LogonEvent += new EventHandler(mView_LogonEvent);
  }

  void mView_LogonEvent(object sender, EventArgs e)
  {
     string userName = mView.UserName;
     string password = mView.Password;

     if ((userName == "mike") && (password == "aop"))
     {
        mView.Notify("User Logged On");
     }
     else
     {
        mView.Notify("Invlid user name or password");
     }
  }
}

The Controller is listening to the logon event that the View fires. When a logon event handler is triggered, the Controller queries the user name and user password, and validates if the user name and password are correct. If there is an error message, the controller sets the status message on the View with a message. However, there are a few problems... First, I don't know if you noticed, when I first showed the code of the View, it did not contain a reference to the Controller. Therefore, I need to modify my Form to include a knowledge of the Controller.

C#
public partial class LogonForm : Form, ILogonView
{
  public event EventHandler LogonEvent;
  private LogonController mController;
  public LogonForm()
  {
     InitializeComponent();
     mController = new LogonController(this);
  }

// rest of the class unchanged

So far, we are classic MVP. If you have understood everything to this point, you have just understood MVP. Anything above this point is just little things that bug me.

  • The Controller is doing all the work. Normally, the logon work should not be done directly by the Controller but delegated to a business layer class, ideally, a logon service.
  • What if the logon takes 10 minutes to process, we can not keep the view frozen.
  • What if we would like to send the status back to the View during a business operation outside of the Controller. How do we handle that?
  • To be honest with you, I still feel the View should be beyond stupid and not know anything. But, as you can see, the View knows about its Controller and how to create it.

Using a Service Layer

OK. One problem at a time. The first task would be to remove the logon processing from the Controller and move it to a Service layer. Let's create a LogonService class, which accepts a user name and a password, and validates if the user is valid. Now, we will use our Service layer from the Controller to perform the logon operation. Same idea, the Controller handles the logon event handler and delegates the work to the Service layer. The Service layer actually does the work of logging, and updates the status message on the screen with the outcome of the operation.

We can create a simple Service that looks like this:

C#
public class LogonService
{
  public bool Logon(string userName, string password)
  {
     bool rc;
     if ((userName == "mike") && (password == "aop"))
     {
        rc = true;
     }
     else
     {
        rc = false;
     }
  }
}

But, here again, we have some problems. What if our Service is a long running service, and maybe executes many steps to log a user? This example is simple, but let's face it, it is never that simple in production code. We normally have to go to the database to validate a user. I would like to provide the service in some way to report the status back to the View. After all, our Controller can do this, so should the Service... An idea would be to introduce an interface to allow the Service to report the status, something like an INotify.

C#
public interface INotify
{
  void Notify(string notification);
}

Now, this should look familiar, our View has this method... (take a look).

C#
public interface ILogonView
{
  event EventHandler LogonEvent;
  void Notify(string notification);
  string Password { get; }
  string UserName { get; }
}

So, let's break this into two interfaces...

C#
public interface INotify
{
  void Notify(string notification);
}

public interface ILogonView : INotify
{
  event EventHandler LogonEvent;
  string Password { get; }
  string UserName { get; }
}

Notice that ILogonView inherits from INotify. Now, we need to pass this INotify to the Service, so let's modify our Service. Other than that little change, there is nothing new with our View. Let's look at the Controller using the LogonService class.

C#
public class LogonController
{
  private ILogonView mView;
  public LogonController(ILogonView view)
  {
     // register the view
     mView = view;

     // listen to the view logon event
     mView.LogonEvent += new EventHandler(mView_LogonEvent);
  }

  void mView_LogonEvent(object sender, EventArgs e)
  {
     string userName = mView.UserName;
     string password = mView.Password;

     LogonService logonService = new LogonService(mView);
     logonService.Logon(userName, password);
  }
}

public class LogonService
{
  private INotify mNotifier;
  public LogonService(INotify notifier)
  {
     mNotifier = notifier;
  }
  public bool Logon(string userName, string password)
  {
     bool rc;
     if ((userName == "mike") && (password == "aop"))
     {
        mNotifier.Notify("Logon Successful");
        rc = true;
     }
     else
     {
        mNotifier.Notify("Invliad User or Password");
        rc = false;
     }

     return rc;
  }
}

Notice a few things:

  • The Service is created as a local variable to the event handler. It might be better to create the Service once and keep it during the lifetime of the controller, but then we have the problem of who creates the Service? It could be passed to the Controller on the constructor, or created directly in the Controller, but then, it might not be re-used by other Controllers. The best approach is to pass the Service to the constructor, but I am not willing to have the view create it and pass it. I will deal with this issue soon.
  • The Service is passing the view as an INotify. There is really nothing wrong with that, but if you think about it, it allows the Service direct access to the UI. A better way would be to have the Service talk to the Controller via INotify. Then, we are respecting the role of the Controller... (so I am going to make this change by allowing the Controller to implement INotify).
  • The goal was to provide the Service a channel to communicate to the View, so now it is possible for a long running service to report status. But, what if we don't want to report anything, and would like to run the Service without a View and without a UI? Notice that you can't create the Service without INotify. So, here is another problem, which I will deal with later. At least, we can say the logic of the logon is now outside of the Controller, and our Service is a little more powerful with its capabilities.
  • A note about DDD (Domain Driven Design). Notice, my Service is not very domain driven; it is normally at the Service level that we should see classes such as User and Authentication. For example, Authentication.Authenticate(User user), but I am leaving the domain model out of this article, because already I have a lot of problems to solve, and DDD deserves an article on its own.

Let's do a bit of re-factoring. First step, let's create the Service as a member variable of the Controller class. Let's also have the ability to pass the Service to the constructor.

C#
public LogonController(ILogonView view)
{
 // register the view
 mView = view;

 // listen to the view logon event
 mView.LogonEvent += new EventHandler(mView_LogonEvent);

 mLogonService = new LogonService(this);
}

// set the serivce by the client (but not used for now).
public LogonController(ILogonView view, LogonService logonService)
{
 // register the view
 mView = view;

 // listen to the view logon event
 mView.LogonEvent += new EventHandler(mView_LogonEvent);

 mLogonService = logonService;
}

void mView_LogonEvent(object sender, EventArgs e)
{
 string userName = mView.UserName;
 string password = mView.Password;
 mLogonService.Logon(userName, password);
}

// used to implemnt INotify
public void Notify(string notification)
{
 mView.Notify(notification);
}
  • The controller implements INotify.
  • The Service can be passed to the Controller, it's more flexible. But I am not changing the View, so this constructor will not be used.
  • I created the Service as a member variable if it is not passed.

OK. Let's step back a little. Most of you might be happy with this implementation of MVP, and it is kind of by the book. But, I am still not happy with it. First of all, who is going to create the LogonService. It should really be set outside of the Controller, or supplied to the Controller (but I hate having the View create the Service). Same problem with the Controller being created by the View. The View should have no knowledge of how to create a Controller; it would be nice if the Controller can be supplied to the View. The solution is to have a factory pattern to create the Controller, Service, and even the View! The solution to all these problems can be addressed with Dependency Injection (DI).

The View depends on the Controller, and my Controller depends on a Service. I can create a Factory that will create my View, addressing all the dependencies, and what do you know, there is a framework out there just for that - Spring.Net.

Using Spring.NET for Dependency Injection

To tell you all the truth, I am new to Dependency Injection. However, it was not complicated to learn, and it solves the issues of object creation and dependencies. I chose to resolve all dependencies using setter properties. I have also created interfaces to all the classes that require dependency injection. The Controller interface:

C#
public interface ILogonController : INotify
{
  ILogonView LogonView { get; set; }
}

Setting the View to the Controller, rather than passing the View as parameter into the constructor. Now, the Controller can have an empty default constructor. Let's see the newly modified Controller:

C#
public class LogonController : INotify, ILogonController
{
  private ILogonView mView;
  private ILogonService mLogonService;

  public LogonController()
  {

  }

  public LogonController(ILogonView view)
  {
     // register the view
     mView = view;

     // listen to the view logon event
     mView.LogonEvent += new EventHandler(mView_LogonEvent);

     mLogonService = new LogonService(this);
  }

  // set the serivce by the client (but not used for now).
  public LogonController(ILogonView view, LogonService logonService)
  {
     // register the view
     mView = view;

     // listen to the view logon event
     mView.LogonEvent += new EventHandler(mView_LogonEvent);

     mLogonService = logonService;
  }

  void mView_LogonEvent(object sender, EventArgs e)
  {
     // make sure the view is attached
     Debug.Assert(mView != null, "view not attached");

     string userName = mView.UserName;
     string password = mView.Password;

     mLogonService.Logon(userName, password);
  }

  // used to implemnt INotify
  public void Notify(string notification)
  {
     mView.Notify(notification);
  }

  public ILogonService LogonService
  {
     set
     {
        mLogonService = value;
        mLogonService.Notifier = this;
     }
     get
     {
        return mLogonService;
     }
  }

  public ILogonView LogonView
  {
     set
     {
        mView = value;
        mView.LogonEvent += new EventHandler(mView_LogonEvent);
     }
     get
     {
        return mView;
     }
  }
}

Notes:

  • Now, when setting the LogonView property, the View is attached to the Controller. Spring.Net will handle this for us.
  • Because this Controller can be created using an empty constructor, I added an Assert, to make sure the view is attached when handling the logon event handler.
  • Notice there is a setter property for the LogonService. After setting the Service, the Controller sets the Notify property of the Service.

In order to inject the Service into the Controller, I needed to first make an interface to the Service. Let's take a look at the Service interface and the Service class:

C#
public interface ILogonService
{
  bool Logon(string userName, string password);
  INotify Notifier { get; set; }
}

public class LogonService : ILogonService
{
  private INotify mNotifier;
  public LogonService()
  {
     // instead of having it as null
     mNotifier = new EmptyNotify();
  }
  public LogonService(INotify notifier)
  {
     mNotifier = notifier;
  }
  public bool Logon(string userName, string password)
  {
     bool rc;
     if ((userName == "mike") && (password == "aop"))
     {
        mNotifier.Notify("Logon Successful");
        rc = true;       
     }
     else
     {
        mNotifier.Notify("Invliad User or Password");
        rc = false;
     }

     return rc;
  }
  public INotify Notifier
  {
     set { mNotifier = value; }
     get { return mNotifier; }
  }
}

Notes:

  • The logon service interface now allows an INotify to be specified as a setter (so if I wanted, it could have been initialized using DI as well, but I am not doing it in this example).
  • I provide an empty constructor, so now, I don't need to have a notify object for creating or running the Service. However, in case the INotify is not passed to the constructor or not set by the setter, I provide an EmptyNotify, which is similar to having the mNofity set to null. However, because this empty class implements the interface, I don't need to check in my code if the INotify object is null, or if it is passed.

Here is a quick look at the EmptyNotify (which is like a null):

C#
public class EmptyNotify : INotify
{
  public void Notify(string notification)
  {
     return; // do nothing.
  }
}

Let's see how the View is changed.

The nice thing about the new version of the View is that it is now simpler. The goal was always to keep the View unaware of the business work, and even something simple as creating the Controller is now outside of the View. Like anything in MVP, there is a property to set the Controller.

C#
public ILogonController LogonController
{
 set
 {
    mController = value;
    mController.LogonView = this;
 }
}

Notice that right after the Controller is set, I assign the View to the controller LogonView property. Here is the full source code of the View after the modification:

C#
public partial class LogonForm : Form, ILogonView
{
  public event EventHandler LogonEvent;
  private ILogonController mController;
  public LogonForm()
  {
     InitializeComponent();
  }

  /// <summary>
  /// Get the User Name from the username text box
  /// Trim the user name, and always return lower case
  /// </summary>
  public string UserName
  {
     get { return mTextBoxUserName.Text.Trim().ToLower(); }
  }

  /// <summary>
  /// Get the password from the password textbox
  /// </summary>
  public string Password
  {
     get
     {
        return mTextBoxPassword.Text;
     }
  }

  public ILogonController LogonController
  {
     set
     {
        mController = value;
        mController.LogonView = this;
     }
  }

  /// <summary>
  /// Update the screen with a message
  /// </summary>
  /// <param name="notification">Message to show on the status bar</param>
  public void Notify(string notification)
  {
     mToolStripStatusLabelStatus.Text = notification;
  }

  private void mButtonLogon_Click(object sender, EventArgs e)
  {
     // fire the event that the button was clicked.
     if (LogonEvent != null)
        LogonEvent(this, EventArgs.Empty);
  }
}

Setting Spring.Net

So far, I have tried to avoid talking about Spring.Net, but at this point, we are ready to deal with Dependency Injection. The first step is to get Spring.Net and install it. Don't worry, it is safe. I have done it many times, it will not break anything on your computer. You can download Spring.Net from here.

This example is using version 1.1 of Spring.Net.

The next step is adding a file reference to Spring.Core.dll into the .NET project.

Now that we have Spring.Net, all that is left is to specify the dependencies for creating the objects (View, Controller, and Service). To do this, I have used the app.config to specify the dependencies in XML. The XML is simple to understand. Jere is a copy of my App.Config for the Logon MVP example:

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <configSections>
      <sectionGroup name="spring">
         <section name="context" 
            type="Spring.Context.Support.ContextHandler, Spring.Core"/>
         <section name="objects" 
            type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
      </sectionGroup>
   </configSections>
   <spring>
      <context>
         <resource uri="config://spring/objects"/>
      </context>
      <objects xmlns="http://www.springframework.net/">
         <object id ="LogonService" type="MvpExample.LogonService, MvpExample"/>
 
         <object id="LogonController" type="MvpExample.LogonController, MvpExample">
            <property name="LogonService" ref="LogonService"/>
         </object>
 
         <object id="LogonView" type="MvpExample.LogonForm, MvpExample">
            <property name="LogonController" ref="LogonController"/>
         </object>                                        
      </objects>
   </spring>
</configuration>

Notes:

  • The Object ID tag is just a logical name
  • I have setup the LogonView to have its property LogonController set to the object definition of "LogonController"
  • Similar case with setting the LogonService logical name into the LogonController property "LogonService"

All that is left is just to create the root object, the View within my application. Notice the changes to Program.cs:

C#
static class Program
{
  /// <summary>
  /// The main entry point for the application.
  /// </summary>
 
  [STAThread]
  static void Main()
  {
     Application.EnableVisualStyles();
     Application.SetCompatibleTextRenderingDefault(false);


     IApplicationContext ctx = ContextRegistry.GetContext();
     Form logonForm = ctx.GetObject("LogonView") as Form;
     Application.Run(logonForm);
  }
}

The most important part is the Spring.Net application context:

C#
IApplicationContext ctx = ContextRegistry.GetContext();
Form logonForm = ctx.GetObject("LogonView") as Form;

Here, we are asking Spring.Net to create our View object, by specifying its Logical Name LogonView; the name must match the name in the app.config.

XML
<object id="LogonView" type="MvpExample.LogonForm, MvpExample">
      <property name="LogonController" ref="LogonController"/>
</object>

All the other dependencies are taken care of by using Spring.Net. At this point, our Logon application is initialized with the correct version of a View, Controller, and Service. Notice that this allows us to "switch" the implementation of a dependency, just by providing a new implementation of an interface and configuration change. For example, to provide another version of the LogonService that will contain additional work, we just need to tell Spring.Net which version to "inject". At this point, I wanted to finish my article, but I noticed that there is one more issue with this MVP pattern. The case of having a long running service, and the risk of freezing the UI while processing the Service. To solve this problem, I have introduced a simple yet powerful little threading framework.

The UI Should Never Freeze

The main problem with UI and threads, is that the UI is not allowed to be accessible from any other thread than the thread it was created in. This means that if our application starts to create threads, and performs processing in threads, it is not legal to have these threads update the UI (via an interface or not). In fact, trying to update the UI from another thread will cause a runtime exception in .NET 2.0, and unpredictable results in version 1.1.

So, our first goal is to make sure the UI is able to update correctly, no matter from which thread it is called on. To do this, I have added a base class to my LogonForm, called View. My base class contains only one method, UpdateUI; this method accepts a delegate of type MethodInvoker, and makes sure this delegate is executed on the UI thread.

C#
public class View : Form
{
  protected void UpdateUI(MethodInvoker uiDelegate)
  {
     if (InvokeRequired)
        this.Invoke(uiDelegate);
     else
        uiDelegate();
  }
}

I am planning to use the same delegate for all my UI activities. This should make you wonder how a delegate that takes no arguments and no return values is able to satisfy all UI operations.

Here is a trick... I use anonymous methods to wrap all UI operations... let's look at a simple example:

Getting the user name from the UI should be done on the UI thread, so I would like to wrap the code that gets the user name from the textbox into a MethodInvoker delegate.

Before:

C#
public string UserName
{
 get
 {
    return mTextBoxUserName.Text.Trim().ToLower();
 }
}

After:

C#
public string UserName
      {
         get
         {
            string value = null;
            MethodInvoker uiDelegate = delegate
            {
               value = mTextBoxUserName.Text.Trim().ToLower();
            };
            UpdateUI(uiDelegate);
            return value;
         }
      }

Notice: for the getters, I needed to store the return value outside of my anonymous method. This is because my delegate does not accept a return value. This is an issue I plan to resolve in a second part of the article... (using AOP to handle UI threads safely). Now, we have to surround all our View public methods with this uiDelegate. To make the job simpler, I made a C# snippet that allows you to select the code within the property, and then apply the "view thread safe" snippet. Here is the snippet, if you want to use it:

XML
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
 <CodeSnippet Format="1.0.0">
  <Header>
   <Title>View Thread Safe</Title>
   <Shortcut>view</Shortcut>
   <Description>Code snippet for creating thread safe view code</Description>
   <Author>Atrion Corporation</Author>
   <SnippetTypes>
    <SnippetType>Expansion</SnippetType>
    <SnippetType>SurroundsWith</SnippetType>
   </SnippetTypes>
  </Header>
  <Snippet>
   <Declarations>
    <Literal>
     <ID>delegate</ID>
     <ToolTip>Delegate to call</ToolTip>
     <Default>uiDelegate</Default>
    </Literal>
    <Literal>
     <ID>method</ID>
     <ToolTip>Function to handle the threading</ToolTip>
     <Default>UpdateUI</Default>
    </Literal>
     
   </Declarations>
   <Code Language="csharp"><![CDATA[
        MethodInvoker $delegate$ = delegate
        {
         $selected$ $end$
        };
        $method$($delegate$);
 $end$]]>
   </Code>
  </Snippet>
 </CodeSnippet>
</CodeSnippets>

Let's look at our new View method:

C#
public partial class LogonForm : View, ILogonView
{
  public event EventHandler LogonEvent;
  private ILogonController mController;
  public LogonForm()
  {
     InitializeComponent();
     //mController = new LogonController(this);
  }

  /// <summary>
  /// Get the User Name from the username text box
  /// Trim the user name, and always return lower case
  /// </summary>
    
  public string UserName
  {
     get
     {
        string value = null;
        MethodInvoker uiDelegate = delegate
        {
           value = mTextBoxUserName.Text.Trim().ToLower();
        };
        UpdateUI(uiDelegate);
        return value;
     }
  }

  /// <summary>
  /// Get the password from the password textbox
  /// </summary>
  
  public string Password
  {
     get
     {
        string value = null;
        MethodInvoker uiDelegate = delegate
        {
            value = mTextBoxPassword.Text;
        };
        UpdateUI(uiDelegate);
        return value;
     }
  }

  public ILogonController LogonController
  {
      set
      {
        mController = value;
        mController.LogonView = this;
      }
  }

  /// <summary>
  /// Update the screen with a message
  /// </summary>
  /// <param name="notification">Message to show on the status bar</param>
  public void Notify(string notification)
  {
     MethodInvoker uiDelegate = delegate
     {
        mToolStripStatusLabelStatus.Text = notification;
     };
     UpdateUI(uiDelegate);
  }
  private void mButtonLogon_Click(object sender, EventArgs e)
  {
     // fire the event that the button was clicked.
     if (LogonEvent != null)
        LogonEvent(this, EventArgs.Empty);
  }
}

Now, our View is able to update itself, no matter from which thread it is being called from. The next step is to actually use threads within the Controller. Suppose the logon takes 5 seconds to logon. To enable threading at the Controller level, I can use the same approach I used for the View. Using delegates... Notice the new base Controller class.

C#
public class AsyncController
{
  public delegate void AsyncDelegate();

  // must call end invoke to clean up resources by the .net runtime.
  // if there is an exception, call the OnExcption which may be overridden by
  // children.
  protected void EndAsync(IAsyncResult ar)
  {
     // clean up only.
     AsyncDelegate del = (AsyncDelegate)ar.AsyncState;
     try
     {
        del.EndInvoke(ar);
     }
     catch (Exception ex)
     {
        OnException(ex);
     }
  }

  protected void BeginInvoke(AsyncDelegate del)
  {
     // thread the delegate, as a fire and forget.
     del.BeginInvoke(EndAsync, del);
  }

  protected virtual void OnException(Exception ex)
  {
   // override by childern
  }
}

Notes:

  • By calling BeginInvoke on a delegate, I am using the thread-pool.
  • I don't really care for output values or return code, and this is mostly due to the MVP pattern. When I implement my Controller function, I can know when to set values to the View.
  • Notice that I still make sure EndInvoke is called. This is for two reasons: first, to make sure I get exceptions, and second, making sure there is no resource leak. Calling BeginInvoke without EndInvoke may cause resources to leak.
  • If there is an exception, I let the child Controller to take care of it.

I modified the logon service to simulate a long running operation, by sleeping for 5 seconds.

C#
public bool Logon(string userName, string password)
{
 // simulate a long operation, wait 5 seconds.
 System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));

 bool rc;
 if ((userName == "mike") && (password == "aop"))
 {
    mNotifier.Notify("Logon Successful");
    rc = true;
 }
 else
 {
    mNotifier.Notify("Invliad User or Password");
    rc = false;
 }

 return rc;
}

Let's see the new logon event handler using threads:

C#
void mView_LogonEvent(object sender, EventArgs e)
{
 // make sure the view is attached
 Debug.Assert(mView != null, "view not attached");

 AsyncDelegate asyncOperation = delegate
 {
    mView.Notify("About to perform logon");
    string userName = mView.UserName;
    string password = mView.Password;

    mLogonService.Logon(userName, password);

 };
 base.BeginInvoke(asyncOperation);
}

Just one note: now, when you execute the program, the UI will not freeze when pressing logon (the way it should be). However, that doesn't stop the user from keeping on pressing the logon button. It is important to add to the View additional functions to enable and disable the button. I didn't do it in this example; however, it works well with MVP, but provide a setter method to enable or disable the logon button.

Conclusion

We went a long way from our simple logon application. We have removed most of the logic from the View, keeping the View very simple. We have used DI to allow the application to inject the Controller and Service into our view. Finally, I have showed a way to enable threading without making major changes. The idea is to wrap UI functions with thread safe code, allowing all UI code to be marshaled to the UI thread. The only last re-factoring left to do is to remove the thread safety wrappers, and find a way to have them done with an Advice using Spring.Net. That way, we can keep the View simple, not even knowing about the threading work.

However, I think this will be done in my next article. I hope you enjoyed this article - Happy .NETting.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
I am currently working as a team leader with a group of amazing .NET programmers. I love coding with .NET, and I love to apply design patterns into my work. Lately I had some free time, so I decided to write some articles, hoping I will spare someone frustration and anxiety.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Md. Rashidul Hasan Masum7-Feb-11 6:15
professionalMd. Rashidul Hasan Masum7-Feb-11 6:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.