24

ウィンドウ アプリケーション (パワーポイントのようなエディター) に元に戻す/やり直しフレーム作業を実装する必要があります。従うべきベスト プラクティスは何か、オブジェクトのすべてのプロパティ変更と UI への反映をどのように処理するかです。

4

5 に答える 5

39

使用する 2 つの古典的なパターンがあります。1 つ目は、完全なオブジェクト状態のスナップショットを保存するために使用されるmemento パターンです。これはおそらくコマンド パターンよりもシステム集約的ですが、古いスナップショットへの非常に簡単なロールバックを可能にします。スナップショットを PaintShop/PhotoShop のようにディスクに保存するか、永続性を必要としない小さなオブジェクト用にメモリに保存することができます。あなたがやっていることは、まさにこのパターンが設計されたものであるため、他の人が提案したコマンド パターンよりもわずかにうまく適合するはずです。

また、追加の注意は、以前に行われたことを元に戻すための相互コマンドを必要としないため、潜在的に一方向の機能 [ハッシュや暗号化など] は、相互コマンドを使用して自明に元に戻すことができないことを意味します。コマンドは、古いスナップショットにロールバックするだけで非常に簡単に元に戻すことができます。

また、指摘されているように、コマンド パターンはリソース集約型ではない可能性があるため、次のような特定のケースではそれを認めます。

  • 永続化するラージ オブジェクトの状態がある、および/または
  • 破壊的な方法はなく、
  • 相互コマンドを非常に簡単に使用して、実行したアクションを元に戻すことができる場合

コマンドパターンの方が適しているかもしれません [しかし、必ずしもそうとは限りません。状況によって大きく異なります]。それ以外の場合は、memento パターンを使用します。

私はおそらく、この 2 つのマッシュアップを使用することを控えるでしょう。なぜなら、私の後ろに来て私のコードを維持する開発者のことを気にする傾向があるからです。また、そのプロセスをできるだけシンプルかつ安価にすることが雇用主に対する私の倫理的責任でもあるからです。可能。2 つのパターンのマッシュアップは、維持するのに費用がかかる、維持不可能な不快感のネズミ穴になりやすいと思います。

于 2009-12-16T16:40:16.253 に答える
6

古典的なプラクティスは、 Command Patternに従うことです。

コマンドでアクションを実行する任意のオブジェクトをカプセル化し、Undo() メソッドで逆のアクションを実行させることができます。すべてのアクションをスタックに保存して、簡単に巻き戻すことができます。

于 2009-12-16T16:38:07.973 に答える
2

Command Patternを見てください。モデルに対するすべての変更を個別のコマンド オブジェクトにカプセル化する必要があります。

于 2009-12-16T16:39:41.963 に答える
0

変更を追跡するための非常に柔軟なシステムを作成しました。2種類の変更を実装する描画プログラムがあります:

  • シェイプの追加/削除
  • シェイプのプロパティ変更

基本クラス:

public abstract class Actie
{
    public Actie(Vorm[] Vormen)
    {
        vormen = Vormen;
    }

    private Vorm[] vormen = new Vorm[] { };
    public Vorm[] Vormen
    {
        get { return vormen; }
    }

    public abstract void Undo();
    public abstract void Redo();
}

シェイプを追加するための派生クラス:

public class VormenToegevoegdActie : Actie
{
    public VormenToegevoegdActie(Vorm[] Vormen, Tekening tek)
        : base(Vormen)
    {
        this.tek = tek;
    }

    private Tekening tek;
    public override void Redo()
    {
        tek.Vormen.CanRaiseEvents = false;
        tek.Vormen.AddRange(Vormen);
        tek.Vormen.CanRaiseEvents = true;
    }

    public override void Undo()
    {
        tek.Vormen.CanRaiseEvents = false;
        foreach(Vorm v in Vormen)
            tek.Vormen.Remove(v);
        tek.Vormen.CanRaiseEvents = true;
    }
}

形状を削除するための派生クラス:

public class VormenVerwijderdActie : Actie
{
    public VormenVerwijderdActie(Vorm[] Vormen, Tekening tek)
        : base(Vormen)
    {
        this.tek = tek;
    }

    private Tekening tek;
    public override void Redo()
    {
        tek.Vormen.CanRaiseEvents = false;
        foreach(Vorm v in Vormen)
            tek.Vormen.Remove(v);
        tek.Vormen.CanRaiseEvents = true;
    }

    public override void Undo()
    {
        tek.Vormen.CanRaiseEvents = false;
        foreach(Vorm v in Vormen)
            tek.Vormen.Add(v);
        tek.Vormen.CanRaiseEvents = true;
    }
}

プロパティ変更の派生クラス:

public class PropertyChangedActie : Actie
{
    public PropertyChangedActie(Vorm[] Vormen, string PropertyName, object OldValue, object NewValue)
        : base(Vormen)
    {
        propertyName = PropertyName;
        oldValue = OldValue;
        newValue = NewValue;
    }

    private object oldValue;
    public object OldValue
    {
        get { return oldValue; }
    }

    private object newValue;
    public object NewValue
    {
        get { return newValue; }
    }

    private string propertyName;
    public string PropertyName
    {
        get { return propertyName; }
    }

    public override void Undo()
    {
        //Type t = base.Vorm.GetType();
        PropertyInfo info = Vormen.First().GetType().GetProperty(propertyName);
        foreach(Vorm v in Vormen)
        {
            v.CanRaiseVeranderdEvent = false;
            info.SetValue(v, oldValue, null);
            v.CanRaiseVeranderdEvent = true;
        }
    }
    public override void Redo()
    {
        //Type t = base.Vorm.GetType();
        PropertyInfo info = Vormen.First().GetType().GetProperty(propertyName);
        foreach(Vorm v in Vormen)
        {
            v.CanRaiseVeranderdEvent = false;
            info.SetValue(v, newValue, null);
            v.CanRaiseVeranderdEvent = true;
        }
    }
}

毎回Vormen = 変更に送信される項目の配列。そして、次のように使用する必要があります。

スタックの宣言:

Stack<Actie> UndoStack = new Stack<Actie>();
Stack<Actie> RedoStack = new Stack<Actie>();

新しい形状の追加 (例: ポイント)

VormenToegevoegdActie vta = new VormenToegevoegdActie(new Vorm[] { NieuweVorm }, this);
UndoStack.Push(vta);
RedoStack.Clear();

選択した図形を削除する

VormenVerwijderdActie vva = new VormenVerwijderdActie(to_remove, this);
UndoStack.Push(vva);
RedoStack.Clear();

プロパティ変更の登録

PropertyChangedActie ppa = new PropertyChangedActie(new Vorm[] { (Vorm)e.Object }, e.PropName, e.OldValue, e.NewValue);
UndoStack.Push(ppa);
RedoStack.Clear();

最後に元に戻す/やり直しアクション

public void Undo()
{
    Actie a = UndoStack.Pop();
    RedoStack.Push(a);
    a.Undo();
}

public void Redo()
{
    Actie a = RedoStack.Pop();
    UndoStack.Push(a);
    a.Redo();
}

これは、取り消しとやり直しのアルゴリズムを実装する最も効果的な方法だと思います。例として、私の Web サイトの次のページを見てください: DrawIt

ファイル Tekening.cs の 479 行目あたりに undo redo 機能を実装しました。ソースコードをダウンロードできます。あらゆる種類のアプリケーションで実装できます。

于 2016-01-12T22:40:07.037 に答える