Undo

Undoバッファのインターフェイスなクラス。
UndoBufferTargetはUndoBufferがビューの状態を操作する際の仲介役。
/// <summary>
/// アンドゥバッファ基本クラス
/// </summary>
public abstract class UndoBuffer
{
    /// <summary>
    /// 子
    /// </summary>
    /// <remarks>
    /// 最後に入力した状態がリストの先頭になる。
    /// </remarks>
    protected List<UndoBuffer> list = new List<UndoBuffer>();

    /// <summary>
    /// アンドゥ
    /// </summary>
    public abstract void undo();

    /// <summary>
    /// リドゥ
    /// </summary>
    public abstract void redo();
	
    /// <summary>
    /// 子の追加
    /// </summary>
    /// <param name="buffer"></param>
    public virtual void add(UndoBuffer buffer){list.Insert(0,buffer);}
}

/// <summary>
/// アンドゥバッファ操作対象オブジェクト
/// </summary>
public abstract class UndoBufferTarget
{
    protected Object last_val = null;
    protected Object cur_val = null;

    public Object getLastValue() { return last_val; }
    public Object getCurrentValue() { return cur_val; }
    public virtual void setLastValue(Object value) { last_val = value; }
    public virtual void setCurrentValue(Object value) { cur_val = value; }
}
UndoBuffer実装クラス。
/// <summary>
/// アンドゥバッファコンテナ
/// </summary>
public class UndoBufferContainer : UndoBuffer
{
    /// <summary>
    /// 現在のバッファ位置
    /// </summary>
    /// <remarks>
    /// 0〜バッファ数+1
    /// </remarks>
    private int cur_idx = 0;

    /// <summary>
    /// アンドゥ
    /// </summary>
    public override void undo()
    {
        if(cur_idx != list.Count)
            list[cur_idx].undo();

        cur_idx = Math.Min(cur_idx + 1, list.Count);
    }

    /// <summary>
    /// リドゥ
    /// </summary>
    public override void redo()
    {
        cur_idx = Math.Max(cur_idx - 1, 0);
        list[cur_idx].redo();
    }
}

/// <summary>
/// アンドゥバッファアイテム
/// </summary>
public class UndoBufferItem : UndoBuffer
{
    private UndoBufferTarget target = null;
    private Object old_val = null;
    private Object new_val = null;


    /// <summary>
    /// コンストラクタ
    /// </summary>
    /// <param name="target">ターゲット</param>
    /// <param name="old_val">現在値</param>
    /// <param name="new_val">更新値</param>
    public UndoBufferItem(UndoBufferTarget target, Object old_val, Object new_val)
    {
        this.target = target;
        this.old_val = old_val;
        this.new_val = new_val;
    }

    /// <summary>
    /// アンドゥ
    /// </summary>
    public override void undo()
    {
        if (list.Count > 0)
        {
            for (int i = list.Count - 1; i >= 0; i--)
                list[i].undo();
        }
		
	    target.setCurrentValue(old_val);
    }
	
    /// <summary>
    /// リドゥ
    /// </summary>
    public override void redo()
    {
        if (list.Count > 0)
        {
            for (int i = list.Count - 1; i >= 0; i--)
                list[i].redo();
        }

        target.setCurrentValue(new_val);
    }
}
UndoBufferTargetの実装クラス。UndoBuffer<->コントロールを仲介する。
/// <summary>
/// UndoBufferTarget(コントロールをターゲット)
/// </summary>
public class UndoBufferTargetForControl : UndoBufferTarget
{
    private System.Windows.Forms.Control target = null;

    /// <summary>
    /// コンストラクタ
    /// </summary>
    /// <param name="target">ターゲット</param>
    public UndoBufferTargetForControl(System.Windows.Forms.Control target)
    {
        this.target = target;
        last_val = this.target.Text;
        cur_val = this.target.Text;
    }

    /// <summary>
    /// 値のセット
    /// </summary>
    /// <param name="value"></param>
    public override void setCurrentValue(Object value)
    {
        base.setCurrentValue(value);
        target.Text = value.ToString();
    }
}
動作確認用フォーム。
UndoBufferItemに子を追加することで一括操作が可能。
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private UndoBufferContainer undo_container = new UndoBufferContainer();
    private UndoBufferItem macro_item = null;
    private bool is_rec = false;  //マクロ記録中フラグ

    private void Form1_Load(object sender, EventArgs e)
    {
        textBox1.Tag = new UndoBufferTargetForControl(textBox1);
        textBox2.Tag = new UndoBufferTargetForControl(textBox2);
        textBox3.Tag = new UndoBufferTargetForControl(textBox3);
        textBox4.Tag = new UndoBufferTargetForControl(textBox4);
        textBox5.Tag = new UndoBufferTargetForControl(textBox5);
    }

    private void btnUndo_Click(object sender, EventArgs e)
    {
        undo_container.undo();
    }

    private void btnRedo_Click(object sender, EventArgs e)
    {
        undo_container.redo();
    }

    private void textBox_Leave(object sender, EventArgs e)
    {
        TextBox chg_obj = (TextBox)sender;
        UndoBufferTarget target = (UndoBufferTarget)chg_obj.Tag;

        if (is_rec)
        {
            if (macro_item == null)
                macro_item = new UndoBufferItem(target, target.getLastValue(), chg_obj.Text);
            else
                macro_item.add(new UndoBufferItem(target, target.getLastValue(), chg_obj.Text));
        }
        else
        {
            undo_container.add(new UndoBufferItem(target, target.getLastValue(), chg_obj.Text));
        }

        target.setLastValue(chg_obj.Text);
    }

    private void btnStMacro_Click(object sender, EventArgs e)
    {
        is_rec = true;
    }

    private void btnEdMacro_Click(object sender, EventArgs e)
    {
        if (macro_item != null)
            undo_container.add(macro_item);

        macro_item = null;
        is_rec = false;
    }
}