Control GUI Action in VB6 Using the Command Behavior Pattern

n real life, sequences aren’t easy to control. Once something is set in motion it is difficult to manipulate discrete portions of that movement. And of course, in real life it’s impossible to undo a movement. But in programming, sequences aren’t so elusive. If you plan correctly, you can define actions and then control them however you like. And one very useful tool in doing so is the Command Behavior pattern.

The Command Behavior pattern is a simple pattern to use. It is especially good at objectifying the concept of an action and an undo action. Converting an action into an object is also a very orderly way to ensure that each behavior converges on a single chunk of code that implements that behavior.

In this article we will investigate the uses of the Command Behavior pattern and we will have a little fun along the way. I added a ball image to a form and implemented commands that represent movements of the ball. Each ball command is placed in a stack permitting you to undo the ball movement or rewind all of the movements. After a bit of tinkering with this code you will see that converting presentation layer (GUI) behaviors into command objects makes it much easier to encapsulate, organize, track, undo, and invoke behaviors, using multiple metaphors, such as buttons and menus.

Defining the Command Classes
A common way to implement the Command Behavior is to define a base class (or interface) with a Do and Undo method. Undo performs the opposite action of the Do behavior. It doesn’t really matter what the Do behavior is; it can be anything. (‘Do’ is a reserved word in VB6, so I will substitute the word ‘Execute’ as my method name.)

For my example I implemented four movement commands. Each command invokes a move operation in one of four directions: up, down, left, or right. The Undo behavior for each of these commands invokes a move command in the opposite direction. Obviously, I am not limited to two-dimensional, linear directions. I could simulate a third dimension or base movements on trigonometric algorithms. For now I’ll focus on the command classes.

The reason for a common base class is that the code then can polymorphically invoke either the Do or the Undo action indifferent to the specific instance of the command object. Listing 1 shows the base command and implementation of all four derived command classes. Because VB6 does not support class inheritance, interface inheritance is employed.

Note that in Listing 1 an I-prefix is used for the ICommand interface. This is a popular prefix notation for interfaces and is used widely in several languages. It was devised to help readers recognize that the module contained definitions only. Also note that the Implements statement was used in all of the directional commands. This ensures that each class has the ICommand interface at a minimum. The result is that I can declare an ICommand variable and assign it to any instance of a class that implements ICommand.

Finally, it is worth pointing out that each command maintains a reference to a specific Form, Form1. The reason is that Form1 contains information about its boundaries and the control I want to manipulate with commands.

Creating the GUI
The GUI, represented by Form1, contains an image control with a bitmap of a ball on it. The form also contains a collection object that I will treat like a stack of prior commands. By adding each command to the end of the collection and “popping” from the end of the collection I can trace backward in my command execution. In essence, the collection plays the role of a stack. I push commands after execution and pop to invoke the Undo behavior.

To demonstrate the Undo behavior I defined an Edit|Undo menu and an Edit|Rewind behavior. Undo pops one command from the stack and calls Undo. Rewind pops all commands, calling Undo on each. Listing 2 contains the code for Form1.

Figure 1. Classes in Command: A UML class diagram that depicts the relationships in our implementation of the Command pattern.

Next I want to tie the relevant directional arrow keys to each command by setting the Form’s KeyPreview property to True and creating the command object associated with that key. For example, vbKeyUp should create an UpCommand object. Then I process the command. (I could also use the Factory Creational pattern to move creation of the command objects into the command class itself.)

ProcessCommand is defined to assign a reference to Form1 to the command’s Form property, to execute the command, and then to push the command on the collection-cum-stack. You don’t have to implement a stack, but if you don’t keep track of the commands in the order they were executed then it will be impossible to use the Undo behavior.

Figure 1 shows a UML model illustrating the relationship between the various classes in the sample program.

Checking Your Work in UML
If you know UML then you’ll recognize that Figure 1 basically summarizes the article thus far. If you don’t know UML (it is a good skill to acquire) simple class diagrams like this one are not that hard to follow. Lines represent relationships. Diamonds represent whole and part relationships, that is, aggregation. The whole has the diamond attached to it and the part does not. Directional arrows represent associations, which are roughly similar to aggregate relationships, and triangles represent inheritance, where the thing inherited from has the triangle attached to it. Dashed, directional lines indicate a dependency or something needed.

Reading Figure 1, then, logically, LeftCommand, RightCommand, DownCommand, and UpCommand inherit from ICommand. Commands have an association to but don’t own the Form?represented by their Form properties. The Form is dependent on commands because that is how the Form’s behaviors are invoked, and the Form has an aggregate relationship with the collection because the Form is responsible for the life of the collection.

These definitions are not precisely technical in the UML. For example, we can differentiate between inheritance, called generalization, and interface inheritance, called realization, but the explanation is pragmatically correct.

Author’s Note: For more information on UML basics, pick up a copy of my upcoming book UML DeMystified from McGraw-Hill/Osborne coming in the spring of 2005.

The UML class diagram in Figure 1 shows me only a static view of the classes involved. A sequence diagram could be used to represent interactions between these elements. To grasp the behavior, try stepping through the code in the VB6 IDE debugger.

Reviewing the Simple Stack Representation
The techniques in this article require you some familiarity with the stack representation. When I was at Michigan State University as an undergrad we had to implement stacks, and they seemed very hard at the time. Beginning programmers will benefit from a quick review of stacks here; they are easily devised in VB6 from a collection and can be very useful.

A stack is a data structure that follows a last in first out rule (pronounced LIFO) supported by two basic behaviors called push and pop. To add something to a stack you push it, and to remove something you pop it. The last thing you added by calling push will be the first thing you get when you call pop. For example, if we have a stack of integers and pushed 1, 2, 3, 4, 5, and then called pop five times we would get 5, 4, 3, 2, and 1. Hence, stacks are good data structures for back tracking and, interestingly enough, are fundamental to the way your CPU tracks the path your code takes as it runs.

However, a collection can easily be a stack simply based on where you insert at and remove items from. By inserting and removing at the end of the collection very little support code is needed to devise a stack. For example, to implement push call collection.Add; to implement pop call collection.Remove(collection.Count). Simply using the tail end of the collection as the front of the data structure effectively converts a collection into a simple stack. You can see an example of this in Listing 2 in the PushStack and PopStack methods.

Finally, there are several excellent revisions you could make to the example code in this article. Moving the collection, PushStack and PopStack to a Stack class would clean up the form and make a nice general Stack class. Improving the design of code like this is called refactoring. Learning to refactor code is an excellent complement to learning about patterns. You can learn more about Refactoring in Martin Fowler’s Refactoring: Improving the Design of Existing Code from Addison-Wesley and more about patterns in Erich Gamma and company’s book Design Patterns, also from Addison-Wesley.

The Quality of Your Tools
In this article you learned about a tool, the Command behavior pattern. If you find that invoked behaviors need to be undoable, or that your invocation code is becoming too complex then I hope you remember this simple but powerful tool. Other concepts discussed here?UML, patterns, and refactoring?these, too, are tools.

There is no one perfect tool for every job. (Remember the adage ‘if all you have is a hammer then every problem is a nail’?) My dad used to say that a craftsman is known by the quality of his tools. In other words, the more tools you have the more you can build.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: