January 31, 2012

How to implement MVC (Model View Controller) Pattern with Swing – Part 3

In my third blog about implementing MVC pattern in Swing I will look at how to implement global actions, context sensitive global actions, updating status bar and popup dialogs. But before delving into these issues and I would like again to repeat each class responsibility in the MVC pattern:

Model:
  1. Simple POJO model.
View:
  1. Layout out the Swing components.
  2. Responds to user action by sending request to Control.
  3. Responds to Controller responds.
Controller:
  1. Receives request from the View.
  2. Do logic.
  3. Sends Responds to View(s).
And a last reminder the Request and Responds to and from the Controller should be TOTALLY view technique neutral as the Model. The view specific code, in our case Swing code, ends in the view. Handling all Swing code is the responsibility of each view.

In my previous blog we have seen that each View is only responsible of its Swing component and what happens in other view is it totally unaware of. Try to think of separation of concern. It is the responsibility of the Controller to call the Response methods in each Views that should be updated.

Example:


And the corresponding Controller code:


The Toolbar is yet Another View

So now lets start with looking how to add a JToolBar to our Swing client. If you think of it for a while you will realize that the toolbar is yet another view. So lets write a new class that extends from our AbstractView.



I have here used javax.swing.Action for base class, since they contain all graphical properties such icon, label and tooltip. But also javax.swing.Action can be used for JButton and in JPopupMenu and JMenuItem.

And I have also deliberately not created a abstract class for our action, which could have loaded our images, since I do not believe that such extra abstraction class will not bring any extra to our code nor reduce the number of lines. It will only make our client code more less understandable. But what would justify an extra abstraction layer is if abstract action class called the Controller in thread safe ways and change cursor to busy and in case of failure show a generic exception dialog. That would really bring something to our code, but not loading icons, using Action is quite straightforward and I like to see directly what my code does.

Handling Context Sensitive Global Actions

So after realized that the toolbar is just another view, lets move onto how to make the toolbar context sensitive, i.e. when switching active panel/window the save actions is replaced with the active panel/window save action. But before we must first decide where the concrete Swing save action should be located. It clearly belong to the concrete view. Think again of separation of concern. But what we must do is to expose the global actions so we can send them to the toolbar view. And lastly where do we wire the views together? In the Controller of course.

Let first add an extra method in our abstract view:


Then in our controller we wire the global actions from the view to the toolbar view.


And in our toolbar view:



The last thing we need is to do is to switch back to the default when changing to other views. Here it can be justified to introduce some kind of View lifecycle mechanism, so the programmer does not have to concern about setting the correct toolbar action each time a new View is retrieved.

Managing a Statusbar

After thinking the GUI as a composition of different views, it should not be surprising to think of the statusbar as yet another view as well. And where is the respond sent to update this view. In the Controller of course. Here is a simple example how to implement a statusbar with swinglabs JXStatusBar.

se.msc.mvcframework.demo.view.StatusBarView


Managing Modular Popup Windows

The modular popup windows is also yet another view. The only difference it needs a parent JFrame to show from. And the solution to that is simple. Each View has a reference to the AbstractFrame that holds the JFrame via getFrame().

No comments: