views:

190

answers:

2

I have a window which works like the Visual Studio designer. Each document has two views:

  • Source View,
  • Designer View.

I have a toolbar that can issue different commands. The toolbar button has a CommandId string property that store the Id of the command, such as:

  • Cut, Copy, Paste;
  • Insert Grid,
  • Auto Format
  • ...

I am having trouble designing the command pattern where the execution of the command is different depending on the view.

For an obvious example, the Copy Command will copy the selected text when in the Source View, but will copy the selected control when in the Designer View.

I am currently mapping the commandId string to a CopyCommand object, but since the execution of the command is different depending on the view, I am not sure how this should be implemented.

  • Should each view supplies a list of concrete command that it understand (and thus having two CopyCommand like SourceCopyCommand and DesignCopyCommand that share the same id)?

  • Or should each command be unique, but the view has a big mapping function that change the behavior depending on the command id?

A: 

What if passed in a ICommandContext (would be like a Receiver see "Design Patterns" pg.234). Each of your views would implement this interface and then have the knowledge as to how to carryout the command. The Command objects only responsibility at that point is to encapsulate the action so you can call CopyCommand from the menu, from the keyboard "Ctrl + C", with a right click copy, and maybe some other way...

public interface ICommandContext 
{
void Cut();
void Copy();
void Past();
void AutoFormat(); 
}

public SourceView : ICommandContext
{
    public void Cut()
    {
        // Do stuff here...
    }
}

public class CopyCommand : Command
{
    public override Execute(ICommandContext context)
    {
        context.Copy();
    }
}

definition (from dofactory):

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

J.13.L
Meh, I guess I wasn't doing it right then. I blame it on the restricted internet access at work. (hopes bosses see that comment)
Pierre-Alain Vigeant
+2  A: 

Use a combination of the Strategy Pattern and the State Pattern. Make each window implement an interface that defines common commands that can be sent to it - ex. IWindowEditCommands. For multi-pannel windows, use internal strategy objects to encapsulate the different implementation of common commands for each possible window state. The State pattern comes in when you switch between window states - ex. design view and source view. When the window changes state, it changes the type of the object stored in commandState appropriately, ensuring that the correct concrete Strategy is used for implementing cut, copy, and paste.

To wire this up to your menu Command objects, just have the menu Commands find the currently selected window and dispatch the appropriate message to it.

public interface IWindowEditCommands
{
    string Copy();
    string Cut();
    void   Paste(string buffer);
}

public class Editor implements IWindowEditCommands
{
    private IWindowEditCommands commandState;

    //constructor and other methods

    public void SwitchToSourceView()
    {
        //do stuff
        commandState = new EditorSourceViewStrategy(this);
    }

    public void SwitchToDesignView()
    {
        //do stuff
        commandState = new EditorDesignViewStrategy(this);
    }

    //IWindowEditCommands methods
    public string Copy() { return commandState.Copy(); }
    public string Cut()  { return commandState.Cut(); }

    public void Paste(string buffer) { commandState.paste(buffer); }

}

public class EditorSourceViewStrategy implements IWindowEditCommands
{
    private Editor editor;

    public EditorSourceViewEditCommands(Editor editor)
    {
        this.editor = editor;
    }

    public string Copy() {return...} //return the selected source from the source view
    public string Cut()  {return...} //return the and delete the selected source from the source view
    public void Paste(String buffer) {} //insert the buffer text at the insertion point
}

public class EditorDesignViewStrategy implements IWindowEditCommands
{
    private Editor editor;

    public EditorDesignViewEditCommands(Editor editor)
    {
        this.editor = editor;
    }

    public string Copy() {return...} //return the selected TAGS from the source view
    public string Cut()  {return...} //return the and delete the selected TAGS from the source view
    public void Paste(String buffer) {} //insert the buffer text at the insertion point
}
Ryan Michela