I am delighted to find others who share the idea that we need MVC to organize our GUI apps.
Since early posts we have built several apps using the MVC strategy described and it's working nicely (though it still makes us uneasy that its is quite different than the pattern described in Head First Design Patterns-one of the funnest and best programming books of all time).
We have one app that is like Notepad that I would be happy to zip up and send out. Even better would be to get a wizard like Eric Claybergh to massage and bless it first. If we get a little more serious about this, I'll build a tutorial around it. If not here, there's a website that is gathering such specifically for SWT (
http://www.zikal.com). They don't compete with instantiations so I hope it's OK for me to plug them.
For now, here's some ideas, tips and preferences from recent MVC experience:
1. We like to start with a POJO that calls its self
AppnameApp. It only has the static main method in it. It's job is to instantiate a class, typically named MainController. This implements the MvcControl interface.
2. The nice thing about starting the above way is this: what if you decide to add a splash screen or do something else before bringing up your main window?
3. MainController creates the MainWindow which was designed by SWT-Designer. It also creates an Object named MainModel where we tend to put all of the "model" stuff, but it doesn't have to be this way.
3b. Note that you have to call create or similarly named method on gui's when they are instantiated or shortly thereafter. The controller classes need to see all the widgets up and instantiated so they can add listeners to handle events.
4. We tend to have one controller per view. A complex main window will consist of various subviews which are discretely designed composites, brought into the mainwindow using Choose Composite. Each of these has its own controller. Every controller tend to have a constructor which passes it references to the parent controller and the view (composite, etc.) that the controller must control.
4. Every controller has a createEventHandlers method that adds listeners with event handling code for all widgets of interest. I think someone mentioned it's a lot easier to let the GUI designer generate the event code, and they're right. Initially we did that, but then transplanted it to controllers.
5. Another tip about generating event handler code: All you need to remember is that you are adding a some kind of listener. For example, just type:
view.get
NameOfWidget().addSelectionListener()
Eclipse will want you to put a SelectionListener in as the argument, so you add new SelectionListener(){}
After that Eclipse will complain about unimplemented methods, so hit ctrl+1 to get those.
At this point you're handling the event using a fully built out SelectionListener. But notice that the SWT-Designer people use SelectionAdapter instead. Since this is a class rather than an interface, it allows you to just override the (usually only) one method of interest. So what we do it change "Listener" to "Adapter" after getting the IDE to do most of the coding for us through lots of use of ctrl+space and ctrl+1.
6. Looping. It's really easy to have an event cause a call to updateViewFromMethod. Consequently, a widget gets updated and it fires an event. This leads to another updateViewFromMethod, and there you are in a death loop. In a snap you're out of stack space and the app just goes poof and disappears.
We experiemented with disabling all widgets during updates. This kept them calm and not firing events while they got updated. But we generally don't recommend this approach. One of its problems is that the "focus" is lost- you'd have to save which widget had the focus and then restore it after re-enabling controls.
What we practice now is this: when in an updateViewFromModel routine, plan on updating widgets
only if their current value differs from model data. It's a hassle to have to test this and if you know it's not necessary, you can avoid it.
We are toying with the idea of having some sort of flag that says "attention all widgets, do not fire events because you were just hit by code, not the user." Then, if this flag was on, we could simply abort events with event.doit=false.
But, a challenge with this is that this flag's management is not as simple as turning it on and off at the beginning and end of the updateViewFromModel routine. Instead, what is needed is to have it turned off after all the widgets have had a chance to react and have their events aborted. In the world of windows programming we would do a SendMessage at the end of the updateViewFromModel method. We would receive it at just the right time and then switch off the flag.
With SWT, we're not sure how to do this. One hokey idea comes to mind: we could create a phony widget and beat on it at the end of updateViewFromModel. Then, when it's event popped up, we'd know that all the other updated widgets had already happened (unless they send messages to themselves).
Anybody got any ideas on this?
One other idea: whatever we do here, we do not want to prevent being able to do JUnit testing of GUI's. We want our unit tests to be able to bang on buttons and then test if the right thing happened.
7. JFace viewers. The general pattern we've found in developing MVC is that JFace ironically is more hindrance than help. We found the JFace Dialog class to be useless and dumbed down too much. The Action class glaringly lacked the ability to add listeners, so we made a wrapper to fix that. We're now looking at how JFace Viewers work with MVC and getting some mixed results.
The first thing we did was ditch the JFace TableViewer. Our motivation was that we could not find decent descriptions of how its virtual mode works. Anybody want to query 200 million records from a database and call setInput with them in an array? Nope, virtual mode is needed. If anyone's interested, I'll post something or a link to the guys who explain it (the authors of "Definitive Guide to SWT and JFace").
8. With the JFace TreeViewer, however, we are not having MVC problems, so it's a keeper so far. Invariably, we are showing a JDOM object with it, so we've got some label and content provider code that we reuse a lot and would be happy to share. We just put the LabelProvider and ContentPriovider classes into our controller classes. Again, the GUI designer teaches us how. Double click in the Properties editor and it makes nice inner classes for you in the GUI class. Just cut and paste those into the controller class and set them into the viewer there. With this approach, all your gui class has to expose is its viewer object since everything can be done against that.
9. Reusability. Now that were making GUI subpanels, we happily find ourselves reusing them alot. For example, we have a composite that uses the FillLayout and all it has in it is a JFace TreeViewer. Another is like that but only has an SWT table created using SWT.VIRTUAL in its style.
So now we wish we could have more reuse of controller classes. But, our early controllers tended to be very "hard wired" into their surroundings. Subpanel controllers tend to get instantiated with a reference to their parent controller and call back to it to get model information. Sometimes we had subcontrollers reaching back through several layers of parent controllers. This didn't seem right.
We're contemplating a couple of ways to do this. One way would be that the controller would be told by a higher authority where its model resided, When updateViewFromModel was called, it would already have a reference to the model and use it.
The other way is to use interfaces more and give the controller a reference to an object that implements the HasTheStuffIWant interface. We consider this slightly better because it's programming against an interface and allows the holder of the model data to service your request with a method, as opposed to a reference which may no longer be valid.