Home > Articles > Programming > Windows Programming

  • Print
  • + Share This
This chapter is from the book

Handling UI Events

Another point of departure among these three patterns is how the UI events are handled. For example, consider a button push. In strict MVC, there is no UI event logic in the code behind file for a view; all processing happens in the controller. This means that the view has to expose button press events and any other UI events to the controller for processing. The controller has to attach a handler to each event that it considers significant and process that event accordingly.

In MVP, the code behind for the view preprocesses UI events before passing them on to the controller for processing. If a request is strictly UI-related with no business logic, the view can handle the request and return without involving the presenter.

In MVVM, again this issue is sidestepped due to the capability of XAML-based applications to bind to commands the view model provides.

Let’s look at an example of the difference between MVC and MVP. Assume your application is up and running, displaying a grid of data. The user wants to see the details of one of the rows, so he presses the Show Details button to open a dialog window with more detailed data. In our UI, it is also possible to show details by right-clicking the context menu. Listing 2.4 shows an example of the MVC version.

Listing 2.4. An MVC View Exposing Events to Controller

using System;
using System.Windows.Forms;

namespace CodeSamples.Ch02_ApplicationArchitecture.Listing04
{
  public class SampleForm : Form
  {
    private DataGrid Grid;
    private Button showDetailsBtn;
    private Button maximizeWindowBtn;
    private ContextMenu mnu;

    public event EventHandler ShowDetailsButtonClicked;
    public event EventHandler MaximizeWindowButtonClicked;
    public event EventHandler ContextMenuItemClicked;

    public SampleForm()
    {
      Grid = new DataGrid();
      showDetailsBtn = new Button();
      maximizeWindowBtn = new Button();
      mnu = new ContextMenu();

      showDetailsBtn.Click += showDetailsBtn_Click;
      maximizeWindowBtn.Click += maximizeWindowBtn_Click;
      var mnuItem = mnu.MenuItems.Add("Details");
      mnuItem.Click += mnuItem_Click;
    }

    void maximizeWindowBtn_Click(object sender, EventArgs e)
    {
      if (MaximizeWindowButtonClicked != null)
        MaximizeWindowButtonClicked(this, e);
    }

    void mnuItem_Click(object sender, EventArgs e)
    {
      if (ContextMenuItemClicked != null)
ContextMenuItemClicked(this, e);
    }

    void showDetailsBtn_Click(object sender, EventArgs e)
    {
      if (ShowDetailsButtonClicked != null)
ShowDetailsButtonClicked(this, e);
    }

    public void Maximize()
    {
      //maximize window
    }
  }

  public class SampleFormController
  {
    private SampleForm _form;
    public SampleFormController()
    {
      _form = new SampleForm();
      _form.ShowDetailsButtonClicked += _form_ShowDetailsButtonClicked;
      _form.ContextMenuItemClicked += _form_ContextMenuItemClicked;
      _form.MaximizeWindowButtonClicked += _form_MaximizeWindowButtonClicked;
    }

    void _form_MaximizeWindowButtonClicked(object sender, EventArgs e)
    {
      _form.Maximize();
    }

    void _form_ContextMenuItemClicked(object sender, EventArgs e)
    {
      ShowDetailsForm();
    }

    void _form_ShowDetailsButtonClicked(object sender, EventArgs e)
    {
      ShowDetailsForm();
    }

    private void ShowDetailsForm()
    {

    }
  }
}

In MVC, the controller receives the button press event directly and executes the appropriate logic. It also attaches a separate handler to the context menu event. All of these event handlers can call the same method to do the work to actually process the event, but there is still the need to have multiple event handlers. If yet another UI method for viewing details is added (such as a hotkey), the view needs to be updated to expose this additional event handler, and the controller needs to be updated to process the event. Also note that the user request to maximize the window is sent out to the controller, which calls the proper method on the form.

Now consider the equivalent example but in an MVP configuration, as demonstrated in Listing 2.5.

Listing 2.5. An MVP View Exposing Events to Presenter

using System;
using System.Windows.Forms;

namespace CodeSamples.Ch02_ApplicationArchitecture.Listing05
{
  public class SampleForm : Form
  {
    private DataGrid Grid;
    private Button showDetailsBtn;
    private Button maximizeWindowBtn;
    private ContextMenu mnu;

    public event EventHandler ViewDetailsRequested;
    public event EventHandler MaximizeWindowButtonClicked;

    public SampleForm()
    {
      Grid = new DataGrid();
      showDetailsBtn = new Button();
      maximizeWindowBtn = new Button();
      mnu = new ContextMenu();

      showDetailsBtn.Click += showDetailsBtn_Click;
      maximizeWindowBtn.Click += maximizeWindowBtn_Click;
      var mnuItem = mnu.MenuItems.Add("Details");
      mnuItem.Click += mnuItem_Click;
    }

    void maximizeWindowBtn_Click(object sender, EventArgs e)
    {
      //maximize window
    }

    void mnuItem_Click(object sender, EventArgs e)
    {
      if (ViewDetailsRequested != null)
        ViewDetailsRequested(this, e);
    }

    void showDetailsBtn_Click(object sender, EventArgs e)
    {
      if (ViewDetailsRequested != null)
        ViewDetailsRequested(this, e);
    }

  }

  public class SampleFormPresenter
  {
    private SampleForm _form;
    public SampleFormPresenter()
    {
      _form = new SampleForm();
      _form.ViewDetailsRequested += _form_ShowDetailsButtonClicked;
    }

    void _form_ShowDetailsButtonClicked(object sender, EventArgs e)
    {
      ShowDetailsForm();
    }

    private void ShowDetailsForm()
    {

    }
  }
}

This MVP example implements an event filtering process in the view to abstract away the various ways for a user to request an action. Instead of the presenter adding event handlers for the button and context menu events, the code behind in the view does that. The view also defines a new event called ViewDetailsRequested. Each of the event handlers in the view subsequently raise the same ViewDetailsRequested event, which the presenter creates a handler for. The view filters the individual UI requests into a general action event for the presenter to handle. With this structure, if we add our hotkey to show details only, the view needs updating to handle the additional functionality. The interface and presenter remain unchanged.

Also note in this implementation that the view itself takes care of the window maximize request. Because no business logic is required to fulfill this request, the view performs the requested action without involving the presenter. If the presenter needs to know of the request for some reason, we can always refactor to send this message out to the presenter.

  • + Share This
  • 🔖 Save To Your Account