如何有效地为 WinForms Image/Drawing 应用程序实现撤消、重做
How to implement undo, redo for a WinForms Image/Drawing application efficiently
我有一个 WinForms image/paint 应用程序。
该应用程序允许用户执行以下操作:
- load/import 图片
- 保存图片
- 旋转、裁剪图像
- 更改像素或区域的颜色
如何以高效的方法为多个操作实现 undo/redo 功能?
我应该处理哪个鼠标事件?
我想避免将整个内容保存在图片框(加载图像的地方)中,因为它可能会占用大量内存。
谢谢。
我见过的实现撤消/重做的最佳方法是使用 "Command" 模式。
每个可以执行的操作都应该作为一个 class 实现,它要么继承自基础 Command
class,要么实现一个 ICommand
接口。 class 应该有一个 Execute
和一个 Undo
方法,其中 Execute
对数据执行操作,并存储撤消操作所需的任何信息。 Undo
使用存储的有关撤消更改的信息。
如果你只是想要一个简单的系统,在那里你只能撤消/重做最后的操作,只需保留一个 lastCommand
字段来存储最后一个 运行 命令,然后调用 Undo
需要的时候就可以了。
如果您想要更复杂的东西,具有多级撤消,请保留一个有序的命令列表,您可以将其视为 Stack
。每执行一次 Command
就执行一次,并放在栈顶,一些变量指向最后一条命令。在撤消操作时,在 lastCommand
上调用 Undo
,然后将其设置为它之前的 Command
。要重做操作,只需在下一个命令中再次调用 Execute
,并将指针向上移动。您必须确保当您不在堆栈的 "top" 时,任何 Command
您 运行 都会使前面的操作失效并删除。
如果你愿意,你可以得到更高级的东西,比如命令合并在一起。
举个例子,你会对 Command
做的事情可以改变一个像素(全部来自内存,无法访问 Visual Studio,所以请原谅任何语法错误) .
由于您没有指定语言,我将使用 C#,但这也可以在 VB.NET 中轻松完成。
public class ChangePixelCommand : ICommand
{
private Color _previousColor;
private int x;
private int y;
public void Execute(Bitmap image, int x, int y, Color color)
{
// record the data needed to undo this operation.
_previousColor = image.GetPixel(x, y);
_x = x;
_y = y;
// update the image.
image.SetPixel(x, y, color);
}
public void Undo(Bitmap image)
{
// Use the data we saved earlier to put the pixel back the way it was.
image.SetPixel(_x, _y, _previousColor);
}
}
要在上面的 "simple" 方法中使用它,您只需执行如下操作:
// Change a pixel.
lastCommand = new ChangePixelCommand(myImage,
Control.MousePosition.X,
Control.MousePosition.Y,
Colors.Red);
lastCommand.Execute();
// Undo the change
lastCommand.Undo();
要更改区域,您可以只存储发生更改的矩形的边界,以及该区域之前的样子的小快照。
对于Rotation,就是旋转的角度。
我不确定为什么你们会想要 "undo" 保存,但是如果你 真的 需要,你可以存储文件名并删除文件。不过恢复文件以前的内容...这可能会很棘手。
我有一个 WinForms image/paint 应用程序。
该应用程序允许用户执行以下操作:
- load/import 图片
- 保存图片
- 旋转、裁剪图像
- 更改像素或区域的颜色
如何以高效的方法为多个操作实现 undo/redo 功能? 我应该处理哪个鼠标事件?
我想避免将整个内容保存在图片框(加载图像的地方)中,因为它可能会占用大量内存。
谢谢。
我见过的实现撤消/重做的最佳方法是使用 "Command" 模式。
每个可以执行的操作都应该作为一个 class 实现,它要么继承自基础 Command
class,要么实现一个 ICommand
接口。 class 应该有一个 Execute
和一个 Undo
方法,其中 Execute
对数据执行操作,并存储撤消操作所需的任何信息。 Undo
使用存储的有关撤消更改的信息。
如果你只是想要一个简单的系统,在那里你只能撤消/重做最后的操作,只需保留一个 lastCommand
字段来存储最后一个 运行 命令,然后调用 Undo
需要的时候就可以了。
如果您想要更复杂的东西,具有多级撤消,请保留一个有序的命令列表,您可以将其视为 Stack
。每执行一次 Command
就执行一次,并放在栈顶,一些变量指向最后一条命令。在撤消操作时,在 lastCommand
上调用 Undo
,然后将其设置为它之前的 Command
。要重做操作,只需在下一个命令中再次调用 Execute
,并将指针向上移动。您必须确保当您不在堆栈的 "top" 时,任何 Command
您 运行 都会使前面的操作失效并删除。
如果你愿意,你可以得到更高级的东西,比如命令合并在一起。
举个例子,你会对 Command
做的事情可以改变一个像素(全部来自内存,无法访问 Visual Studio,所以请原谅任何语法错误) .
由于您没有指定语言,我将使用 C#,但这也可以在 VB.NET 中轻松完成。
public class ChangePixelCommand : ICommand
{
private Color _previousColor;
private int x;
private int y;
public void Execute(Bitmap image, int x, int y, Color color)
{
// record the data needed to undo this operation.
_previousColor = image.GetPixel(x, y);
_x = x;
_y = y;
// update the image.
image.SetPixel(x, y, color);
}
public void Undo(Bitmap image)
{
// Use the data we saved earlier to put the pixel back the way it was.
image.SetPixel(_x, _y, _previousColor);
}
}
要在上面的 "simple" 方法中使用它,您只需执行如下操作:
// Change a pixel.
lastCommand = new ChangePixelCommand(myImage,
Control.MousePosition.X,
Control.MousePosition.Y,
Colors.Red);
lastCommand.Execute();
// Undo the change
lastCommand.Undo();
要更改区域,您可以只存储发生更改的矩形的边界,以及该区域之前的样子的小快照。
对于Rotation,就是旋转的角度。
我不确定为什么你们会想要 "undo" 保存,但是如果你 真的 需要,你可以存储文件名并删除文件。不过恢复文件以前的内容...这可能会很棘手。