*
MSDN*
Results by Bing
|Developer Centers|Library|Downloads|How To Buy|Subscribers|My MSDN
 
Centralized UI event handling in Windows Forms
By : Rakesh Rajan
 
Article Posted: November 26, 2004
 
Introduction
 
Most Windows Forms projects consist of a basic set of UI controls like the main menu, toolbar and other buttons. The user will be able to accomplish the same action by using different controls – he/she could either click a button, a toolbar button, or the respective main menu item. A good example would be saving a document in Microsoft Word – the user could accomplish this by either clicking on the Save icon in the toolbar, or selecting Save in the File menu or by just pressing Ctrl+S.
 
You could handle these actions by attaching separate event handlers for each of the controls. This means that for the same action, you will have to duplicate the code at say the toolbar button click event, the button click event, and the main menu item click event. Before long, you would end up writing a lot of redundant and messy code which would be tough to manage.
 
This article takes you through an implementation that would centralize user-interface event handling code irrespective of the control from which the event was raised. The accompanying code illustrates the implementation.
 
The design
 
Let’s consider a simple Windows Forms project, where the user can execute a few actions namely – New, Save, Exit and About. Assume that we have a main menu, a toolbar and a number of buttons with which the user can execute any of these.
 
Define an action enumeration
 
The first step to achieve centralization is to define a list of all actions that can be performed in the UI using the controls. The best approach for this is to use an enumeration.
 
protected enum Action
{
   New,
   Save,
   Exit,
   About,
}
 
Define a control-to-action map
 
Each of the UI controls map to only a single action. For example, a ‘New’ button maps to only Action.New. To identify which action goes with which control, we define a control to action map by using a Hashtable.
 
protected Hashtable _controlActionMap = null;
 
The keys for this Hashtable will be the instances of the controls, and the values will be one among the Action items. We populate the Hashtable in the form load event handler with the necessary entries.
 
private void Form1_Load(object sender, System.EventArgs e)
{
     _controlActionMap = new Hashtable();

     _controlActionMap.Add(NewMenuItem, Action.New);
     _controlActionMap.Add(SaveMenuItem, Action.Save);
     _controlActionMap.Add(ExitMenuItem, Action.Exit);
     _controlActionMap.Add(AboutMenuItem, Action.About);

     _controlActionMap.Add(NewButton, Action.New);
     _controlActionMap.Add(SaveButton, Action.Save);
     _controlActionMap.Add(ExitButton, Action.Exit);
     _controlActionMap.Add(AboutButton, Action.About);

     _controlActionMap.Add(NewTButton, Action.New);
     _controlActionMap.Add(SaveTButton, Action.Save);
     _controlActionMap.Add(ExitTButton, Action.Exit);
     _controlActionMap.Add(AboutTButton, Action.About);
}

 
Centralize the event handlers
 
Next, we define a generic method HandlerUserAction which would handle the click event of all the controls. We attach handlers to the click event of the controls pointing to this method.
 
private void HandleUserAction(object sender, System.EventArgs e)
 
Unfortunately, the ToolbarButton click handler does not share the same method signature since it uses ToolBarButtonClickEventArgs as its event handler argument type. This can be easily resolved by adding an overload to the HandleUserAction method with ToolBarButtonClickEventArgs as the argument type. We then simply invoke the second HandleUserAction method backward casting argument to System.EventArgs.
 
private void HandleUserAction(object sender,
System.Windows.Forms.ToolBarButtonClickEventArgs e)
{
   HandleUserAction(sender, (System.EventArgs)e);
}
 
The task for HandlerUserAction method is to figure out the sender and the action to be executed. We can easily identify the type of the sender by using the GetType method. But if this event was raised by a toolbar button, then the actual sender is the Button instance passed in the ToolBarButtonClickEventArgs object. To handle this, we add a little condition block.
 
private void HandleUserAction(object sender, System.EventArgs e)
{
     object senderControl = sender;
     if(e.GetType()== typeof(
     System.Windows.Forms.ToolBarButtonClickEventArgs))
     {
          senderControl = (
     (System.Windows.Forms.ToolBarButtonClickEventArgs)e)
     .Button;
     }
}
 
Now, we need to find out which action is to be executed. We use the Hashtable.ContainsKey method to find out whether an action has indeed been defined for this object.
 
private void HandleUserAction(object sender, System.EventArgs e)
{
     object senderControl = sender;
     if(e.GetType()== typeof(
     System.Windows.Forms.ToolBarButtonClickEventArgs))
     {
          senderControl = (
     (System.Windows.Forms.ToolBarButtonClickEventArgs)e)
     .Button;
     }

     if(senderControl!=null && _controlActionMap.ContainsKey(senderControl))
     {
          // execute the action
     }
}
 
Though it’s possible to add code to execute actions in the HandlerUserAction method itself, we add another method DoAction to keep things organized. This method would be where the final actions will be carried out. Obviously, this method would take an Action as the argument.
 
Hence, our HandlerUserAction method would invoke DoAction passing the Action type.
 
private void HandleUserAction(object sender, System.EventArgs e)
{
    object senderControl = sender;
    if(e.GetType()== typeof(
    System.Windows.Forms.ToolBarButtonClickEventArgs))
    {
        senderControl = (
    (System.Windows.Forms.ToolBarButtonClickEventArgs)e)
    .Button;
    }

    if(senderControl!=null && _controlActionMap.ContainsKey(senderControl))
    {
        DoAction((Action)_controlActionMap[senderControl]);
    }
}
 
Define the central action method
 
The DoAction method, would a simple switch-case block where we would finally code for executing the required action.
 
private void DoAction(Action actionType)
{
     switch(actionType)
     {
          case Action.New:
               MessageBox.Show("You clicked 'New'");
               break;

          case Action.Save:
               MessageBox.Show("You clicked 'Save'");
               break;

          case Action.Exit:
               MessageBox.Show("You clicked 'Exit'");
               break;

          case Action.About:
               MessageBox.Show("You clicked 'About'");
               break;
     }
}

 
To summarize
 
Let’s have an overall look on the steps required to implement this design.
 
1. We define an enumeration with all the actions that can be carried out.
2. We define and populate a Hashtable to map our UI controls to the respective actions.
3. We define a user handler method and add event handlers to all the controls pointing this method.
4. Additional overloads for the user handler method are defined if necessary.
5. The method would identify the proper sender object, and the action to be executed. It finally invokes the central action method passing the action enumeration type.
6. The central action method would execute the actions steps.
 
Having developed the foundation, adding new actions and controls would be quite simple. You just need to point the new control’s event handler to your user handler method, add another entry in the control-to-action map Hashtable, and code for the action in central action method. As keyboard shortcuts form part of menu items, they would work fine with no extra code.
 
Parting thoughts
 
With Whidbey, you could achieve more type-safety and performance by using generics for the control-to-action map.
Using the Command design pattern coupled with the DoAction method could provide you a good deal of flexibility including support for undo.
 
 
Download the source code
(After downloading change the extension of the file to .zip)
 
 
 

©2009 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy Statement
Microsoft