MVC Widgets - List

Last Updated: May 5, 1996

Previous Lesson: Text Field with Any Model
Next Lesson: List Selections

Status quo

Nothing causes me as much grief in AWT as managing a List component. There are a whole mess of limitations that an MVC programmer has to work around, such as:
  • AWT Lists can only deal with Strings. It's not very often when I need to show a list of strings - usually, I'd like to show a list of Objects that can be displayed as Strings. Doing this with List often required mirroring the collection of Strings with a collection of Objects and using the selected index to find the appropriate object. It's not only a pain, it's brittle.
  • A container has to listen for events that come from the List: LIST_SELECT and LIST_DESELECT. Often, the user will listen for these events and do the same kind of model synchronization code over and over.
  • A List doesn't post any events if items are programmatically selected and deselected, so the container isn't notified. This puts a lot of responsibility on the object which doing this selection to also properly notify the container. Not good.
Those are just a few of the problems you will encounter once the component is used in anything but the most trivial applications. This looks like a job for MVC!

The Smalltalk list widgets (called SequenceView and MultiSelectionSequenceView) natively implement a lot of what we're talking about here.

The Model for List: SelectionInList

So if we want to somehow convert Lists to the MVC framework, what would the model look like? Well, a List has two pieces of information that we are concerned about: the elements in the list and the selection(s). So both of these things should be captured as the model behind a List.

We call the model a SelectionInList. All it really does is hold onto two ValueModels: one for the list and one for the selectionIndex(es). This is all the information we need. The class gives a whole bunch of utility methods that help out but fundamentally that's all the information we need. We'll take a look in this lesson at the list part, then next lesson we'll see how the selection mechanism works.

In Smalltalk, this model represented by classes called SelectionInList and MultiSelectionInList. The Java version of SelectionInList plays both these roles.

The list behind MVCList

Just like an MVCTextField always reflects the underlying model, the contents of an MVCList always relfect the contents of the ValueModel which represents the list. Huh? Let me try that again. Actually, let's look at an example first.

Example 6: An MVCList of Strings

Our theme for the examples on this page is that we want to display a list of basketball players. Since I'm from Chicago, we'll look at two teams: the Orlando Magic (arch-nemesis du jour) and your Chicago Bulls. In this initial example, we just want to display the names of the players. We'll have two button which will populate the list: one for the Magic and one for the Bulls. Look at the source code to see how this works.

The Smalltalk version of this example can be found in Example6.st and can be started by evaluating "Example6 open".


Example 6

Lists of Objects

That's fine, but most of the time I want to be dealing with lists of Objects, not plain old Strings. We need something that converts the Objects we deal with in the model into Strings that the component can display. Hmmm, sounds like the PrintConverter we used for text fields. Almost, but not exactly. Here's the story.

The PrintConverter interface is actually just a composite of two other interfaces: ObjectPrinter and ObjectReader. This is so we can convert objects to strings (ObjectPrinter) and strings to objects (ObjectReader). We need to do both for something like a text field. For the list widget, however, we are only concerned about converting objects to strings, so the widget actually needs an ObjectPrinter. The cool thing is that we can use a PrintConverter as well, since it implements the ObjectPrinter interface.

Anyway, if you don't give MVCList an ObjectPrinter explicitly, it will create one for you behind the scenes called DefaultObjectPrinter. All this does is send the method toString() to whatever object you want to display. If you want some other behavior to get the representation of an object, then you have to assign (and probably code) a new ObjectPrinter. So now we see there are two ways to get basketball players to show up in a list.

  1. Quick & dirty: override toString() on the object to return the player name or whatever
  2. Flexible: create a PlayerObjectPrinter class which returns a String and give it to MVCList.
Let's look at both of these methods in the next example.

By default, Smalltalk will send the message #displayString to the Objects to get a string representation. You can override this behavior by sending #displayStringSelector: to the SequenceView, or even setting a visual block that can represent the Object in a graphical manner. We don't have quite that much flexibility in AWT - yet!

Example 7: An MVCList of Players

This time, instead of having a list of Strings which represent basketball players, I am going to have a list of Player objects. For this example, a Player object is very simple: a Player has a first name (firstName) and a last name (lastName). The class has only one behavior right now, which is the toString() method, which will display the first name, a space, then the last name (which seems to be a reasonable default representation). There is a description of the object model if you are interested in more details.

We have the same two teams we had before with the same two population buttons. This time we have two list components instead of one. The interesting thing, though, is that they share the same underlying model, so the lists and selections will always be in sync. The left list shows the default behavior of MVCList: it sends toString() to each object in the list to get the strings to display. This is the quick & dirty method. The right list has a special ObjectPrinter associated with it that prints the last name first and the first name last. The source code should be easy enough to follow along.

The Smalltalk version of this example can be found in Example7.st and can be started by evaluating "Example7 open". Note that we have to go through quite a few hoops to get the desired behavior for the right-side list. This is one case where Java is easier than Smalltalk!


Example 7

MVCList limitations

A lot of things become a lot easier when you deal with the model instead of the view. Not only is this encouraged, but there are some implementation limitations you should be aware of when dealing directly with an MVCList.
  1. While MVCList is subclassed off of List and responds to all the same methods, avoid calling methods which affect the list contents directly, like addItem(), replaceItem(), and delItem(). Modify the model instead, otherwise the model will get out of sync with the widget.
  2. Speaking of modifying the model, you can't add/remove items to the list behind the covers and expect them to show up in the view. The only time the view will be updated is when setValue() is sent to the model.listHolder(). This is because a Vector is not an Observable that announces when it has changed. There are two ways around this problem:
    • Whenever you modify the list, always create a new Vector and use model.listHolder().setValue(newList).
    • Go ahead and modify the list directly, but when you want the changes to be reflected in the widget call updateList() on MVCList.

Smalltalk has many of the same limitations, except that it also provides a collection class like Vector called List which does announce when it has changed. This often proves useful and allows you to forget about the view just a little bit more.

Coming up next...

We have looked at one half of the List equation: the list of Objects. Next we'll look at how to handle selections using the Model-View-Controller approach.

Previous Lesson: Text Field with Any Model
Next Lesson: List Selections


Go back to the tutorial introduction.