Como associo objetos de comando ao receptor certo?

9

Eu tentei usar o Command Pattern para implementar Desfazer e Refazer no meu projeto

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);
    }
}

Implementação de AddElementcomando em 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";
    }
 }
...

as pilhas Undoe Redosão armazenadas na FormMainclasse e passadas para o formulário do editor.

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;

Quando em um novo, FormEdito usuário clica no botão Refazer ou Desfazer, a função correspondente no FormEdité executada, mas, como verifiquei, este receptor do comando é a forma na qual o comando foi criado pela primeira vez e agora pode ter sido descartado. Espero que o programa gere um erro, mas parece que o Commandobjeto armazena uma referência à forma antiga e isso leva ao mau comportamento.

Portanto, acho que preciso encontrar um receptor consistente para os comandos, tanto o formulário principal quanto o controle webBrowser, que tem o mesmo tempo de vida útil dos comandos. Mas ainda assim eu deveria ter acesso a alguns controles relacionados aos comandos.

Onde é o melhor lugar para implementar as funções de comando como receptor de Commandobjetos? Ou qualquer outra maneira de associar o novo formulário a um comando retirado da pilha.

Ahmad
fonte
Eu acho que esta decisão é sua. Não podemos ajudá-lo porque não conhecemos as especificações ou os requisitos funcionais da sua aplicação.
Euphoric
8
Acredito que os objetos de comando devem conter apenas dados serializáveis ​​(ou seja, sem referências a outros objetos), pois os usos comuns para eles incluem o envio de formulários serializados pelas redes, salvando-os em um arquivo para mais tarde ou reproduzindo-os em um receptor diferente (se você quiser suas alterações para aparecer na minha tela em tempo real, por exemplo). Isso pode significar que você deseja passar o Receiver para cada método de comando, ou talvez fornecer aos métodos executeCommand () / undoCommand () do Receiver que permitem a passagem, ou usar objetos de comando que contenham apenas nomes / argumentos de métodos em vez de código .
Ixrec
@Ixrec Obrigado pelo seu conselho, então você quer dizer que eu deveria poder definir o Receiverobjeto de cada comando, eu vou fazer isso.
Ahmad
Considere usar o padrão de lembrança.
P. Roe

Respostas:

1

O padrão de comando deve se aplicar ao modelo , e não à interface do usuário. No seu caso, faça

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

Para atualizar a interface do usuário, use o padrão Observador , para que todos os formulários abertos e seus controles possam reagir a alterações no modelo subjacente.

Seu código se tornará mais claro e dissociado, pois o Command pode cuidar apenas de alterar o documento, e os observadores na interface do usuário precisam atualizar os controles apenas sem considerar exatamente o que mudou.

Quando um formulário é fechado, ele se registrará como observador e nenhuma referência a ele será mantida.

Se um novo formulário for aberto após uma alteração no documento, ele será notificado após um desfazer, mesmo que não estivesse presente quando a alteração original foi feita.

Apalala
fonte