MVC Widgets - Text Field with Any Model

Last Updated: May 5, 1996

Previous Lesson: Text Field with String Model
Next Lesson: Lists

Introduction

In the last lesson, we saw how we could interact directly with a model (which was a String) and not have to worry about how it is displayed in AWT components. This lesson, we'll see how we can do the same thing with models that aren't Strings: things like integers, dollars, points, employees, and basketball players.

In Smalltalk, input fields handle a lot of this stuff natively, as we'll see throughout this lesson.

The PrintConverter

So if text fields have to deal with Strings and we want to deal with some other object, how do we convert between them? That's the job of a PrintConverter. PrintConverter is actually an interface which defines two methods: Objects which implement this interface (usually utility or service objects) can be used by our MVCTextField to then deal with objects other than Strings. The diagram below illustrates the relationship between the model, the PrintConverter, and the text field.

Starting with VisualWorks 2.0, Smalltalk also has a class which fills this role called PrintConverter. The methods are actually called #toPrint: and #toRead:.

PrintConverter diagram

I can hear some of you in the back saying: "Java already does this: Object has a toString() method that anyone can override. Why duplicate that?" Well, toString() is really more of a debugging method and suffers from a number of limitations, the greatest being that an object can only represent itself in one way. If I have a float, how can I alter the precision in which it displays itself? By encapsulating the behavior into a separate service object, we open the doors to a wide range of custom representations.

Example 4: Using an MVCTextField with a float

Okay, this example is actually in three parts, each progressively easier. We're going to start with the most explicit method of associating a float with a text field, but then we'll start to use more of the capabilties of MVCTextField. Sounds like fun, huh? In each iteration we're trying to do the same thing: have a float model and to be able to display and change the model with a text field. Each example will share the following features:
  • The model (which is holding onto our float) is kept in an instance variable called model. Note that each of the below applets has their own model: they don't share a model like Example 3 (but they could!)
  • When we change the value with the text field, we will show two times the value in the status bar (if run as an applet) and the Java console.
  • There is a button named "New Value" which will stick a random number into the value so we can see the text field update.
One thing to pay attention to in this example is how the applet gets notified when the model changes. We set ourselves up as an Observer to the model, so our update() method is called whenever the model changes. Very useful.

Enough talking - open up that Java Console and let's go!

The Smalltalk version of this example (only one iteration, not three) can be found in Example4.st and can be started by evaluating "Example4 open".

Iteration 1

The running applet is displayed below. Play around with it so you're familiar with the functionality. Since all the iterations should act exactly alike, you're really going to want to look at the source code to see what'd going on.


Example 4-1
Here are some comments on this example:
  1. If you are running on a Windows machine, there is a bug where the text field component does not receive the GOT_FOCUS and (more importantly) LOST_FOCUS events. Until this is fixed, make sure you press Enter to accept the value.
  2. Notice how the input is formatted when you accept the value. For instance, if you enter ".9", it is automatically converted to "0.9" for display. This is a nice side effect of using MVC.
  3. Input is filtered, so it throws away characters that don't make sense. It still allows you to enter scientific notation, though! Try "1.23e-3" as a value.
  4. Also notice how our applet's update() method gets called even when we are setting the new value with the "New Value" button. This is desireable behavior that we didn't have to think of explicitly. That's the nice thing about MVC - things seem to work the way you think they should.

Iteration 2

Okay, this should really be called iteration 1.5 - there is only one line of code that changed from the first iteration. If you look at the source code, you can see that we don't explicitly create a PrintConverter. Instead, we tell the field that it has a type of TYPE_FLOAT, which will automatically create the appropriate PrintConverter for us. Check out the setType() method for the other built-in types. Note that while we gain a little convenience, we lose some flexibility in being able to choose the exact PrintConverter to use.


Example 4-2

Iteration 3

The one big wart in both of the previous iterations has been the funny business with the float. Since ValueModel only knows how to deal with Objects, we keep having to create temporary Float objects and do some casts, which we'd like to avoid if we could. None of this is really a show-stopper - it's just kind of inconvenient.

While we can't change the fact that ValueModels have to deal with Objects, we can use a special kind of ValueModel which presents a much more attractive interface for dealing with base numbers: the NumberValueHolder. Check out how much more straight-forward this source code is.


Example 4-3

Chaining ValueModels

One thing that we have found to be useful is "chaining" together models, or having one model adapt another. You could draw a loose analogy with Streams: there are Streams and there are StreamFilters, and you can hook them together to get all sorts of interesting behavior. We can do the same thing with models. The abstract class that helps us do it is ValueAdaptor.

In Smalltalk, you can do this with PluggableAdaptors or by using a subjectChannel with your protocol adaptors (like AspectAdaptor).

ValueAdaptor diagram

Example 5: Using an MVCTextField with a ValueAdaptor

This example will show how to chain these models together in a very basic fashion. You can imagine extrapolating this to a complex network of models if your application demands it. Here, we're going to create a ValueAdaptor called a TimesTwoValueAdaptor. Basically, you give a TimesTwoValueAdaptor a model to "adapt to" and its value always appears to be twice that of the "reference" model. So we'll toss up one text field that displays the reference model, and another (read-only) text field that displays the ValueAdaptor. As always, check out the source code and follow the comments to get the inside scoop.

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


Example 5

Moving on...

That's pretty much the scoop with text fields. You should now be able to use MVCTextField to deal with all different kinds of objects without having to micro-manage the view of those objects. But there's more to AWT than text fields. Next lesson we'll tackle the (IMHO) most annoying component, the List.

Previous Lesson: Text Field with String Model
Next Lesson: Lists


Go back to the tutorial introduction.