Undo 기능에 쓰일 스택과 Redo 기능에 쓰일 스택을 이용해서 Undo, Redo 기능을 구현해보았다.

 

UndoRedoHistory 클래스에서는 특정 타입의 상태를 저장할 수 있고, Undo, Redo를 통해 상태 값을 가져온다.

 

 

/// <summary>

    /// Undo Redo 내역을 기록하는 클래스

    /// </summary>

    /// <typeparam name="T">기록할 타입</typeparam>

    public class UndoRedoHistory<T>

    {

        const int DefaultUndoCount = 10;

        RimitedStack<T> undoStack;

        RimitedStack<T> redoStack;

 

        /// <summary>

        /// Undo 기능을 사용할 수 있는지 여부를 가져온다.

        /// </summary>

        public bool IsCanUndo

        {

            get

            {

                //맨 초기 상태때문에 1보다 커야한다.

                return this.undoStack.Count > 1;

            }

        }

 

        /// <summary>

        /// Redo 기능을 사용할 수 있는지 여부를 가져온다.

        /// </summary>

        public bool IsCanRedo

        {

            get { return this.redoStack.Count > 0; }

        }

 

        public UndoRedoHistory()

            :this(DefaultUndoCount)

        {

 

        }

 

        public UndoRedoHistory(int defaultUndoCount)

        {

            undoStack = new RimitedStack<T>(defaultUndoCount);

            redoStack = new RimitedStack<T>(defaultUndoCount);

        }

 

        /// <summary>

        /// 이전 상태를 가져온다.

        /// </summary>

        /// <returns></returns>

        public T Undo()

        {

            T state = this.undoStack.Pop();

            this.redoStack.Push(state);

            return this.undoStack.Peek();

        }

 

        /// <summary>

        /// 이후 상태를 가져온다.

        /// </summary>

        /// <returns></returns>

        public T Redo()

        {

            T state = this.redoStack.Pop();

            this.undoStack.Push(state);

            return state;

        }

 

        /// <summary>

        /// 상태를 추가한다.

        /// </summary>

        /// <param name="state"></param>

        public void AddState(T state)

        {

            this.undoStack.Push(state);

            this.redoStack.Clear();

        }

 

        /// <summary>

        /// 상태를 모두 제거한다.

        /// </summary>

        internal void Clear()

        {

            this.undoStack.Clear();

            this.redoStack.Clear();

        }

    }

 

 

 

 

ReimetedStack은 개수 제한이 있는 스택이다.

 

개수 제한이 걸렸을 때, 맨 처음 삽입한 상태를 제거해야하므로, 내부 자료구조는 사실 Stack이 아닌 List로 구현해놓았다.

 

/// <summary>

/// 개수 제한이 있는 스택 클래스

/// </summary>

internal class LimitedStack<T>

{

    List<T> list = new List<T>();

    readonly int capacity;

    /// <summary>

    /// 개수를 가져온다.

    /// </summary>

    public int Count

    {

        get { return this.list.Count; }

    }

 

    /// <summary>

    /// 생성자

    /// </summary>

    /// <param name="capacity"></param>

    public LimitedStack(int capacity)

    {

        this.capacity = capacity;

    }

 

    /// <summary>

    /// 맨위의 개체를 반환하고 제거한다.

    /// </summary>

    /// <returns></returns>

    internal T Pop()

    {

        T t = this.list[0];

        this.list.RemoveAt(0);

        return t;

    }

 

    /// <summary>

    /// 개체를 맨위에 삽입한다.

    /// </summary>

    /// <param name="state"></param>

    internal void Push(T state)

    {

        this.list.Insert(0, state);

        if (this.list.Count > capacity)

        {

            this.list.RemoveAt(this.list.Count - 1);

        }

    }

 

    /// <summary>

    /// 맨위의 개체를 제거하지 않고 반환한다.

    /// </summary>

    /// <returns></returns>

    internal T Peek()

    {

        return this.list[0];

    }

 

    /// <summary>

    /// 개체를 모두 제거한다.

    /// </summary>

    internal void Clear()

    {

        this.list.Clear();

    }

}

 

 

 

 

아래는 텍스트 박스의 내용을 저장하는 기능을 구현한 데모 코드이다.

 

public partial class Form1 : Form

{

UndoRedoHistory<string> undoRedoHistory = new UndoRedoHistory<string>(10);

 

public Form1()

{

    InitializeComponent();

    SaveCurrentState();

    this.textBox1.KeyDown += TextBox1_KeyDown;

}

 

private void TextBox1_KeyDown(object sender, KeyEventArgs e)

{

    Keys keyData = e.KeyData;

    if ((keyData & Keys.Control) == Keys.Control)

    {

        if ((keyData & Keys.Z) == Keys.Z)

        {

            this.Undo();

        }

        else if ((keyData & Keys.Y) == Keys.Y)

        {

            this.Redo();

        }

    }

}

 

private void Redo()

{

    if (this.undoRedoHistory.IsCanRedo)

    {

        this.textBox1.Text = this.undoRedoHistory.Redo();

    }

}

 

private void Undo()

{

    if (this.undoRedoHistory.IsCanUndo)

    {

        this.textBox1.Text = this.undoRedoHistory.Undo();

    }

}

 

private void btnSave_Click(object sender, EventArgs e)

{

    this.SaveCurrentState();

}

 

/// <summary>

/// 현재 상태를 저장한다.

/// </summary>

private void SaveCurrentState()

{

    undoRedoHistory.AddState(this.textBox1.Text);

}

 

 

 

샘플 프로젝트 :KDYFramework.UndoRedo.zip

 

 

'.Net > C#' 카테고리의 다른 글

C# 실행파일 안에 DLL 넣기  (0) 2016.05.16
C# 리플렉션을 이용한 Delegate 넘기기  (0) 2016.05.11
C# 원문자 구하기  (0) 2015.10.05
SerialPort Read Blocking 시키기  (0) 2015.08.05
문자열 비교 테스트 (대소문자 무시)  (0) 2015.07.02

+ Recent posts