Command
Home
Introduction
Philosophy
General techniques
Sorting
Searching
Factory
Persistence
Logging
Streaming
Tokenizers
Parsing
File Searching
Command
PseudoPatterns
Compiling
Downloads
FeedBack

By your Command

This article discusses a possible implementation for the command pattern (see GoF) in visual basic. It is written primarily to bring up some ideas.

The idea of the “commands” in this article is that they allow for a central place to code “command execution logic”. The easiest way to explain this is by giving a simple example. Suppose you have a GUI application that puts up a screen.  In the screen there are various widgets that allow the user to execute a certain command. In a list screen, it is not uncommon that there are multiple ways to accomplish the same.  P.e. to open up a detail screen, a user might click a commandbutton “Open” at the bottom of the screen, double click on a given item in the list, right click on the list and select “open” from the popup menu, click a toolbar icon or click an item on a menu.

The first problem is that all of these ways should perform the same code.  The second problem is that all widgets should display consistently.  If a given action is unavaible, it should be unavailable “everywhere on the screen”. 

So how could we do this ?  First we need to decide how we will represent commands.  There are basically two ways : either command is a fixed class or it is an interface. 

Making commands a fixed class is more appealing as it might seem to be at first. Commands could expose an event to execute them.  This event code could be put into the form.  To an average programmer, the code would look just the same as normal form event handling code.  It would only differ in the wiring of the components.  We could event present this class as a non-visual non-hWnd activeX control, which would have the benefit that the visual design-time image of your form contains a control for every command we need.

Stubborn as I usually am, I decide to make command an interface. It has two benefits.  First of all, it allows for the first approach too (class based). So it really “contains” the first approach as a sub-solution.  But maybe more important is that by putting each command in its own seperate class there might be a tendency to reuse commands. Since code reuse is usually considered A Good Thing, I decided to go for this approach. 

ICommand

Here is what my interface looks like :

Interface iCommand
' The command has two reponsibilities. The main
' functionality is making sure actions gets done
Public Sub Execute()

' A secundary responsibility is the provision of visual information
' Since this is communicated via a events and since command is
' an interface, this is done via an event channel (cCommandEvents)
Public Property Get Events() As cCommandEvents
Public Property Get Caption() As String
Public
Property Get TooltipText() As String
Public
Property Get Enabled() As Boolean

The “Execute” method is obvious. It just executes the command.  The other properties are less clear. Having a ‘Caption’, ‘TooltipText’ and ‘Enabled’ property seems odd for something that is supposed to be UI independent.  The short story is that my commands are not in the business layer but in the window layer.  If I would need business layer commands, there would be a separate interface (IBusinessCommand ?) with only the execute method.

Since I want my commands to be observable (notify others of any changes) and since ICommand is an interface, I need an event channel object cCommandEvents which is used for sending events to observers.  (See also interfaces withevents for other possibilities). Here is what the cCommandEvents class looks like :

Public Event CaptionChanged(ByVal strNewCaption As String)
Public Event TooltiptextChanged(ByVal strNewTooltip As String)
Public Event IsEnabledChanged(ByVal bNewEnabled As Boolean)

Public Sub DoCaptionChanged(ByVal strNewCaption As String)
   RaiseEvent CaptionChanged(strNewCaption)
End Sub

Public
Sub DoTooltiptextChanged(ByVal strNewTooltip As String)
   RaiseEvent TooltiptextChanged(strNewTooltip)
End Sub

Public
Sub DoIsEnabledChanged(ByVal bNewEnabled As Boolean)
   RaiseEvent IsEnabledChanged(bNewEnabled)
End Sub

Links

This could be all there is to a command but unfortunately it is not. It would be quite unlogical not to have the common “binding” plumbing available. After all, we want to link these command objects to UI objects such as command buttons and menu items. Therefore we created some support classes.

The following is the code for cLinkCommandoButton. It will keep a command button and an ICommand implementation synchonized :

Private WithEvents mButton         As CommandButton
Private WithEvents mCommandEvents  As cCommandEvents
Private mCommand                    As iCommand

Friend Sub Initialize(ByVal objButton As CommandButton, ByVal objCommand As iCommand)
   Debug.Assert Not (objButton Is Nothing)
   Debug.Assert Not (objCommand Is Nothing)
   Set mButton = objButton
   Set mCommand = objCommand
   Set mCommandEvents = mCommand.Events
   With mButton
       .Caption = mCommand.Caption
       .TooltipText = mCommand.TooltipText
       .Enabled = mCommand.Enabled
   End With
End
Sub

Private
Sub mButton_Click()
   mCommand.Execute
End Sub

Private
Sub mCommandEvents_CaptionChanged(ByVal strNewCaption As String)
   mButton.Caption = strNewCaption
End Sub

Private
Sub mCommandEvents_IsEnabledChanged(ByVal bNewEnabled As Boolean)
   mButton.Enabled = bNewEnabled
End Sub

Private
Sub mCommandEvents_TooltiptextChanged(ByVal strNewTooltip As String)
   mButton.TooltipText = strNewTooltip
End Sub

Private
Sub Class_Terminate()
   Set mCommand = Nothing
   Set mButton = Nothing
   Set mCommandEvents = Nothing
End
Sub

 

One remark before we continue : the line “mButton.Enabled = bNewEnabled” could also be “mButton.Visible = bNewEnabled”. In that case you would hide the button when the command is not available.

As normal, I also wrote a little “constructor” for this class :

Public Function CreateCommandToButtonLink(ByVal aButton As CommandButton, ByVal aCOmmand As iCommand) As Object
   Dim objResult As cLinkCommandToButton
  
   Set objResult = New cLinkCommandToButton
   objResult.Initialize aButton, aCOmmand
   Set CreateCommandToButtonLink = objResult
End Function

The only strange thing about this constructor is that it actually returns an “Object”.  This has nothing to do with late binding. It is only one of those rare cases where the actual class that gets created is totally irrelevant. As a matter of fact, the class cLinkCommandToButton is a private class !

Needless to say, I also wrote a class cLinkCommandToMenuItem.

An Example

Here is a simple demo command class. It has the very useful (?) functionality of appending an asterix to the caption of a form :

Private mForm       As Form
Private mEvents    As cCommandEvents

Implements iCommand

Friend Sub Initialize(ByVal aForm As Form)
   Set mForm = aForm
   Set mEvents = CreateCommandEvents
End Sub

Private
Property Get iCommand_Caption() As String
   iCommand_Caption =
"Set caption to " & mForm.Caption & "*"
End Property

Private
Property Get iCommand_Enabled() As Boolean
   iCommand_Enabled = Not (mForm Is Nothing)
End Property

Private
Property Get iCommand_Events() As SvcCommand.cCommandEvents
   Set iCommand_Events = mEvents
End Property

Private
Sub iCommand_Execute()
   mForm.Caption = .Caption &
"*"
   With mEvents
       .DoCaptionChanged iCommand_Caption
       .DoTooltiptextChanged iCommand_TooltipText
   End With
End
Sub

Private
Property Get iCommand_TooltipText() As String
   iCommand_TooltipText =
"This will really Set caption to " & mForm.Caption & "*"
End Property

If we now have a form with both a menu and a Commandbutton, we could have the following initialization code :

Dim mLinks As Collection

Private Sub Form_Load()
   Dim objTemp As iCommand
   Set mLinks = New Collection
  
   Set objTemp = CreateSetCaptionCommand(Me)
   mLinks.Add CreateCommandToButtonLink(Command1, objTemp)
   mLinks.Add CreateCommandToMenuLink(Menu1, objTemp)
End Sub

Private
Sub Form_Unload(Cancel As Integer)
   Set mLinks = Nothing
End
Sub

As you can see, at form load time, I set up the links between the controls and the command. The only “surprise” is the links collection.

This collection has only one purpose : prevent that the link objects are released before the form closes. By adding them to a collection we increase their reference count, which keeps them alive. An even more crucial aspect is that we have to release the links collection when the form is unloading. If we would not do this, we would create circular references between the form and our commands (indirectly). Remember the link objects have a reference to the visual controls. If we would not release them we could end up with a circular reference.

Improvements and variations

It is obvious that my ICommand favors certain visual properties (Caption, tooltipText).  One could equally well include pictures in the properties.  The reason I did not do this is that pictures tend to be needed in different sizes (toolbars and command buttons might need a different size of picture). Therefore, it made more sense (for me) to leave the pictures in the visual widgets themselves.

It would be nice if each activeX control came with its own set of predefined commands.  That would make reuse very simple.

<more here>

Subitems :

 

 

Site updated : Monday, February 17, 2003