Wie ordne ich Befehlsobjekte dem richtigen Empfänger zu?

9

Ich habe versucht, Command Pattern zum Implementieren von Undo und Redo in meinem Projekt zu verwenden

public abstract class Command
{
    protected Form Receiver { set; get; }
    protected HtmlElement Element { set; get; }
    abstract public void ReDo();
    abstract public void UnDo();
    public Command(Form receiver)
    {
        this.Receiver = receiver;
    }
}
class AddElementCmd : Command
{        
    public AddElementCmd(HtmlElement elem, Form receiver)
        : base(receiver)
    {
        Element = elem;
    }
    public override void ReDo()
    {
        ((FormEdit)Receiver).AddElement(Element,false);
    }
    public override void UnDo()
    {
        ((FormEdit)Receiver).DelElement(Element, false);
    }
}
class DelElementCmd : Command
{
    public DelElementCmd(HtmlElement elem, Form receiver)
        : base(receiver)
    {
        Element = elem;
    }
    public override void ReDo()
    {
        ((FormEdit)Receiver).DelElement(Element, false);
    }
    public override void UnDo()
    {
        ((FormEdit)Receiver).AddElement(Element, false);
    }
}

Implementierung des AddElementBefehls in FormEdit.

public void AddElement(HtmlElement elem, bool isNew = true)
{
    IHTMLElement2 dom = elem.DomElement as IHTMLElement2;
    if (isNew)
    {
        Command cmd = new AddElementCmd(elem, this);
        Undo.Push(cmd);
        Redo.Clear();
    }    
    // some codes here....
    if (showAlltoolStripButton.Checked)
    {
        dom.runtimeStyle.visibility = "hidden";
    }
    else if (showSelectionToolStripButton.Checked)
    {
        dom.runtimeStyle.visibility = "visible";
    }
 }
...

Die Stapel Undound Redowerden in der FormMainKlasse gespeichert und an das Editorformular übergeben.

public Stack<Command> Undo = new Stack<Command>();
public Stack<Command> Redo = new Stack<Command>();

....
FormEdit editor = new FormEdit ();
editor.Browser = webBrowser1;
editor.addedElements = addedElements;
editor.restoreElements = restoreElements;
editor.Undo = Undo;
editor.Redo = Redo;

Wenn FormEditder Benutzer in einem neuen Modus auf die Schaltfläche Wiederherstellen oder Rückgängig klickt, wird die entsprechende Funktion in FormEditausgeführt. Wenn ich diesen Empfänger des Befehls überprüft habe, ist dies die Form, in der der Befehl zuerst erstellt wurde und jetzt möglicherweise entsorgt wurde. Ich erwarte, dass das Programm einen Fehler auslöst, aber es scheint, dass das CommandObjekt einen Verweis auf die alte Form speichert und dies zu Fehlverhalten führt.

Daher denke ich, dass ich einen konsistenten Empfänger für die Befehle finden muss, entweder das Hauptformular oder das webBrowser-Steuerelement, das die gleiche Lebensdauer wie die Befehle selbst hat. Trotzdem sollte ich Zugriff auf einige Steuerelemente haben, die sich auf die Befehle beziehen.

Wo kann man die Befehlsfunktionen als Empfänger von CommandObjekten am besten implementieren ? Oder eine andere Möglichkeit, das neue Formular einem vom Stapel abgelegten Befehl zuzuordnen.

Ahmad
quelle
Ich denke, diese Entscheidung liegt bei Ihnen. Wir können Ihnen nicht helfen, da wir die Spezifikationen oder funktionalen Anforderungen Ihrer Anwendung nicht kennen.
Euphoric
8
Ich glaube, Befehlsobjekte sollten nur serialisierbare Daten enthalten (dh keine Verweise auf andere Objekte), da sie häufig für das Senden ihrer serialisierten Formulare über Netzwerke, das Speichern in einer Datei für später oder das Wiedergeben auf einem anderen Empfänger verwendet werden (wenn Sie möchten) Ihre Änderungen werden beispielsweise in Echtzeit auf meinem Bildschirm angezeigt. Dies kann bedeuten, dass Sie den Receiver an jede Befehlsmethode übergeben möchten oder den Receiver die Methoden executeCommand () / undoCommand () angeben möchten, die ihn selbst übergeben, oder Befehlsobjekte verwenden möchten, die nur Methodennamen / Argumente anstelle von Code enthalten .
Ixrec
@Ixrec Vielen Dank für Ihren Rat, dann meinen Sie, ich sollte in der Lage sein, das Receivervon jedem Befehlsobjekt festzulegen , ich werde dies tun.
Ahmad
Verwenden Sie stattdessen das Erinnerungsmuster.
P. Roe

Antworten:

1

Das Befehlsmuster auf das gelten sollte Modell und nicht der UI. In deinem Fall mach es

protected HtmlDocument Receiver { set; get; }
protected HtmlElement Element { set; get; }

Verwenden Sie zum Aktualisieren der Benutzeroberfläche das Observer- Muster, damit alle geöffneten Formulare und ihre Steuerelemente auf Änderungen im zugrunde liegenden Modell reagieren können.

Ihr Code wird klarer und entkoppelter, da Command nur das Dokument ändern kann und die Beobachter in der Benutzeroberfläche nur die Steuerelemente aktualisieren müssen, ohne genau zu berücksichtigen , was geändert wurde.

Wenn ein Formular geschlossen wird, hebt es die Registrierung als Beobachter auf und es werden keine Verweise darauf aufbewahrt.

Wenn ein neues Formular nach einer Änderung des Dokuments geöffnet wird, wird es nach einem Rückgängigmachen benachrichtigt, auch wenn es zum Zeitpunkt der ursprünglichen Änderung nicht vorhanden war.

Apalala
quelle