これまで作成してきた簡易画像編集ソフトにはアンドゥとリドゥの機能がありませんでした。そこでアンドゥとリドゥの機能を追加します。
まず、以下のようなクラスを作成します。アンドゥまたはリドゥをするときはビットマップ情報とどの部分が範囲選択されているかを復元するため、この情報をUndoBufクラスに保存します。あとはこれをリストに格納して必要なときに取り出せばアンドゥとリドゥを実装することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class UndoBuf { public UndoBuf(Bitmap bitmap, Rectangle rect) { Bitmap = bitmap; Rect = rect; } public Bitmap Bitmap { get; private set; } public Rectangle Rect { get; private set; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
public class UndoBufs { List<UndoBuf> undoBufs = new List<UndoBuf>(); List<UndoBuf> redoBufs = new List<UndoBuf>(); public void InsertUndoBuf(UndoBuf undoBuf) { undoBufs.Insert(0, undoBuf); } public void InsertRedoBuf(UndoBuf redoBuf) { redoBufs.Insert(0, redoBuf); } public void ClearRedoBufs() { redoBufs.Clear(); } public UndoBuf RemoveUndoBuf() { if(undoBufs.Count > 0) { UndoBuf undoBuf = undoBufs[0]; undoBufs.RemoveAt(0); return undoBuf; } else return null; } public UndoBuf RemoveRedoBuf() { if(redoBufs.Count > 0) { UndoBuf redoBuf = redoBufs[0]; redoBufs.RemoveAt(0); return redoBuf; } else return null; } public bool CanUndo() { return undoBufs.Count > 0; } public bool CanRedo() { return redoBufs.Count > 0; } public void ClearUndoBufs() { undoBufs.Clear(); redoBufs.Clear(); } } |
次にUserControlImageクラスに以下を追加します。
UndoするときはRedoできるように現在のデータをRedo用のリストに保存します。Redoするときはその逆です。また範囲選択されている状態も復元したいのでそのために必要なデータ(startPoint、endPoint)もセットします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
public partial class UserControlImage : UserControl { UndoBufs UndoBufs = new UndoBufs(); public bool CanUndo() { return UndoBufs.CanUndo(); } public bool CanRedo() { return UndoBufs.CanRedo(); } public void Undo() { if(BitmapRectangle != null) { ClearBitmapRectangle(); return; } if(UndoBufs.CanUndo()) { if(_bitmap != null) UndoBufs.InsertRedoBuf(new UndoBuf(_bitmap, DragDropRectangle)); UndoBuf undoBuf = UndoBufs.RemoveUndoBuf(); // プロパティBitmapを使わないようにする _bitmap = undoBuf.Bitmap; startPoint = new Point(undoBuf.Rect.Left, undoBuf.Rect.Top); endPoint = new Point(undoBuf.Rect.Right, undoBuf.Rect.Bottom); if(_bitmap != null) { OptimizationScrloolBar(); ShowBitmap(Bitmap); } BitmapChanged?.Invoke(this, new EventArgs()); } } public void Redo() { if(UndoBufs.CanRedo()) { ClearBitmapRectangle(); UndoBufs.InsertUndoBuf(new UndoBuf(_bitmap, DragDropRectangle)); UndoBuf redoBuf = UndoBufs.RemoveRedoBuf(); // プロパティBitmapを使わないようにする _bitmap = redoBuf.Bitmap; startPoint = new Point(redoBuf.Rect.Left, redoBuf.Rect.Top); endPoint = new Point(redoBuf.Rect.Right, redoBuf.Rect.Bottom); if(_bitmap != null) { OptimizationScrloolBar(); ShowBitmap(Bitmap); } BitmapChanged?.Invoke(this, new EventArgs()); } } public void ClearUndoBufs() { UndoBufs.ClearUndoBufs(); } } |
アンドゥバッファを追加するのはUserControlImageクラスのBitmapプロパティが変更されるときです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public partial class UserControlImage : UserControl { Bitmap bitmap = null; public Bitmap Bitmap { get { return bitmap; } set { if(bitmap != value && bitmap != null) { if(IsNewBitmap(_bitmap, value)) { UndoBufs.InsertUndoBuf(new UndoBuf(_bitmap, DragDropRectangle)); UndoBufs.ClearRedoBufs(); } } _bitmap = value; } } } |
それから新しいファイルが読み込まれたときはアンドゥバッファをクリアする必要があります。
1 2 3 4 5 6 7 8 9 10 11 |
public partial class UserControlImage : UserControl { public void Init() { // ここでスクロールに関する値をリセットする ClearScrollInfo(); ClearBitmapRectangle(); ClearUndoBufs(); } } |
あとはメニューにUndo、Redoの項目を追加して実行できるようにするだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public partial class Form1 : Form { private void UndoMenuItem_Click(object sender, EventArgs e) { if(userControlImage1.CanUndo()) userControlImage1.Undo(); else MessageBox.Show("Undoできません", "", MessageBoxButtons.OK, MessageBoxIcon.Error); } private void RedoMenuItem_Click(object sender, EventArgs e) { if(userControlImage1.CanRedo()) userControlImage1.Redo(); else MessageBox.Show("Redoできません", "", MessageBoxButtons.OK, MessageBoxIcon.Error); } // Undo、Redoできないときはメニューを選択不可にする private void UndoRedoMenuItem_DropDownOpening(object sender, EventArgs e) { UndoMenuItem.Enabled = true; RedoMenuItem.Enabled = true; if(!userControlImage1.CanUndo()) UndoMenuItem.Enabled = false; if(!userControlImage1.CanRedo()) RedoMenuItem.Enabled = false; } } |