↩️
タスク削除して戻せない地獄 → Undo機能を後付けした話
はじめに
ある日こうなった。
タスク削除 → 間違えた → 戻せない
Ctrl+Zで戻したい。
当たり前の機能なのに、後付けするとそこそこ重い。
やりたいこと
- Ctrl+Zで直前の操作を取り消す
- 最大20回
- 全操作対応
👉 「全部にUndoが効く」が前提
設計:UndoAction
何を記録するか。
public class UndoAction
{
public string Type { get; set; }
public string Description { get; set; }
public TaskItem Before { get; set; }
public long? TaskId { get; set; }
public List<TaskItem> ClearedTasks { get; set; }
}
👉 本質はこれ
操作前の状態を持つ
Afterはいらない。DBにある。
設計:UndoService
public class UndoService
{
private readonly Stack<UndoAction> _stack = new();
private const int MaxUndo = 20;
public void Push(UndoAction action)
{
_stack.Push(action);
if (_stack.Count > MaxUndo)
{
var temp = _stack.ToArray();
_stack.Clear();
for (int i = 0; i < MaxUndo; i++)
{
_stack.Push(temp[MaxUndo - 1 - i]);
}
}
}
public UndoAction Pop()
{
if (_stack.Count == 0) return null;
return _stack.Pop();
}
}
👉 Stack一択
Cloneが命
public TaskItem Clone()
{
return new TaskItem
{
Id = this.Id,
Title = this.Title,
Category = this.Category,
DueDate = this.DueDate,
IsCompleted = this.IsCompleted,
SortOrder = this.SortOrder,
RepeatRule = this.RepeatRule,
Note = this.Note,
Tag = this.Tag,
ViewId = this.ViewId,
};
}
👉 これがすべて
忘れると壊れる
記録(Push)
_undo.Push(new UndoAction
{
Type = "Delete",
Before = task.Clone(),
TaskId = task.Id
});
👉 操作前に必ずPush
復元(Undo)
switch (action.Type)
{
case "Add":
_db.Delete(action.TaskId.Value);
break;
case "Delete":
_db.ReInsert(action.Before);
break;
case "Edit":
_db.Update(action.Before);
break;
case "Clear":
foreach (var t in action.ClearedTasks)
_db.ReInsert(t);
break;
}
👉 逆操作をするだけ
ReInsertの注意
INSERT INTO Tasks (Id, Title, ...)
VALUES ($id, $title, ...);
👉 ID指定必須
しないと別タスクになる
Ctrl+Z
if (e.Key == Key.Z && Keyboard.Modifiers == ModifierKeys.Control)
{
var msg = _vm.Undo();
RenderDisplay();
}
なぜ難しいか
理由はこれ。
全操作を逆再現する必要がある
単機能じゃない。
設計の重さ
Add / Delete / Edit / Sort / Clear
全部に
事前保存 + 逆処理
が必要。
👉 後付けだと漏れる
Redoを入れなかった理由
- コストが倍
- 利用頻度低
👉 後から足せる
教訓
- Cloneを忘れるな
- Beforeだけでいい
- Stackで管理
- 最初から仕込め
まとめ
Undoはシンプル。
操作前を保存 → 戻す
でもカバー範囲が広い。
👉 設計の問題
ダウンロード
作者:mitsukida
お問い合わせ:mmitsuki0806@gmail.com
🔗 次の記事
Discussion