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 |