Commands

  • CommandBase,
  • StackedCommandBase, IStackedCommandBase
  • UndoStack, RedoStack, UndoCommand, RedoCommand
  • Model and diagram commands
  • Command inheritance diagram

XCase’s commands are small objects that are typically created in response to some user action (typically toolbar click or menu selection) and somehow alter the UML model and diagrams built upon the model.

Commands overview

The key methods of each command are Execute and UnExecute. Execute performs some operations, UnExecute reverts the changes done by Execute.

When creating new command, one usually does not have to override Execute or UnExecute, because they are implemented as a Template methods (design pattern) that rely on CommandOperation and UndoOperation.

CommandOperation should perform the task itself, Execute serves as a kind of wrapper of CommandOperation that integrates the task into the MVC infrastructure. Relation between UnExecute and UndoOperation is analogical. Choose proper base class for the new command (typically it would be DiagramCommandBase, ModelCommandBase or MacroCommand) to achive desired integration into the program. Turn to overriding of Execute and UnExecute only when solving some uncommon scenario._

Base class of all commands is abstract CommandBase that contains some basic fields and abstract methods CommandOperation, UndoOperation, RedoOperation and CanExecute and also a default implementation of Execute method (that calls CommandOperation) and UnExecute (that calls UndoOperation). Inherting classes have to implement these methods:
  • CommandOperation – this method should perform the actual effective function of the command (e.g. add an element to a diagram, add attribute to a class)
  • UndoOperation – should revert all changes done by CommandOperation
  • CanExecute – should return true if command can be executed – when it is properly initialized and all relevant objects are in a state that permits execution of the command
Beside CanExecute there is another method that can be used to validate commands – marking properties of the command with MandatoryArgument and CommandResult attributes.

Command stacks

To support undo and redo operations, XCase works with stacks of commands. When a command is executed, it is pushed to the undo stack. When user wants to undo the last command, the command is popped from the the undo stack, its operation is reverted and the command is pushed to the redo stack. When user wants to redo last undone command, the command is popped from the redo stack and executed and pushed again to the undo stack. Thanks to command stacks, XCase support undo/redo with unlimited depth.

Pair of stacks (undo and redo stack) exists only in one instance. All commands are pushed to these stacks. Both DiagramController's and ModelController's getUndoStack and getRedoStack methods return these stacks.

There are two actual commands that follow the two previous scenarious – UndoCommand and RedoCommand. They are executed when user wants to undo or redo his action.

More complex commands

StackedCommandBase is the next class in commands hierarchy. Again, it is an abstract class, but its Execute and UnExecute method work with command stacks. When created, reference to stacks is passed in the constructor (undo/redo stacks are part of CommandControllerBase object which is the constructor’s parameter). When executed, command is pushed to command stack, when unexecuted, the command is pushed to redo stack. Here the template methods Execute and UnExecute work with command stacks. StackedCommandBase implements IStackedCommand interface that can also be used to work with stacked commands.

DiagramCommandBase is again an abstract class – subclass of StackedCommandBase and parent of all commands that alter diagrams.
*ModelCommandBase is another abstract subclass of StackedCommandBase and parent of all commands that alter the model.

When deciding whether to implement the new command as a diagram command or model command, look at the action the command does from the user point of view. Does the action alter only one diagram or the model under all the diagrams?

Diagram commands typically work with Diagram object and its DiagramElements collection (when adding an element to the diagram or removing an element from diagram). Another type of diagram command is ViewCommand. ViewCommands alter visualization of an element on the particular diagram (its position, size etc.) – this is done by changing a certain ViewHelper (another section of this help is devoted to ViewHelpers - see ViewHelpers).
Model commands typically alter properties of some model element (Class, Association etc.).

MacroCommand is a special kind of command that is composed of another commands. Using MacroCommand, more complex action can be executed as a single command.

When deciding whether to join commands into one MacroCommand, look again from the user point of view. When user selects “undo”, should be the whole action reverted or should it be taken back step by step? The first scenario speaks for MacroCommand, the second for separate commands.

Command factories

Each command class usually has its corresponding factory that creates instances of the command. Another section of this help is devoted to command factories (see Command factories).

Commands inheritance diagram

CommandsInheritanceDiagram.png

HOW-TO create a command

  1. Create a new class in the Controller.Commands folder or one of its subfolders (depends on what the command does)
  2. You may want to change the namespace to XCase.Controller.Commands if you do not want to include another namespace into the place where you use your new command
  3. Decide whether the command you are creating, involves only one diagram or the whole model and choose DiagramCommandBase or ModelCommandBase as the ancestor of your new command accordingly
  4. Decide the complexity of your command. If it is a command, which could utilize another commands, create new basic operations as separate commands and then group them into one MacroCommand<ModelController> or MacroCommand<DiagramController> along with the existing commands you would like to use using MacroCommand.Commands collection in a command preparation method of the newly created MacroCommand, like Set() or InitializeCommand() (it is up to you how you name this method, it is not standardized), which will be called by the user of your command (ElementController, PropertiesWindows, MainMenuCommand etc.) after CommandCreation and before command execution.
  5. In the constructor of your command, set the Description property to a text description of what your command does. Store the description in the CommandDescription.resx file in the Commands folder.
  6. Create a Factory for your command. Look at another command of the same type (DiagramCommandBase or ModelCommandBase). The Factories are all the same except names.
  7. If your command does not support Undo, you can set the undoable property to false in the constructor. This will cause that this command will not be placed on the UndoStack after execution.
  8. Override the CanExecute, CommandOperation and UndoOperation methods of the CommandBase.
  9. If you are returning false in CanExecute or OperationResult.Failed in UndoOperation, do not forget to fill the ErrorDescription property with an item from the CommandError.resx file.
  10. You may need to override RedoOperation also, if your command cannot use CommandOperation as RedoOperation. (When you are creating a new object, you don't want to create another one when Redoing, you want to return the already created one from CommandOperation)

Last edited Dec 10, 2008 at 7:01 AM by Kuba, version 9

Comments

No comments yet.