我在picturebox中加载了一个图像。我通过鼠标点击事件对图像执行绘制操作。当点击鼠标时,它会绘制一个黑色的小矩形区域。现在我想实现撤消对此的操作。当我单击一个按钮时,最后的绘制操作应该撤消。这是我的绘画操作代码..
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
rect.Width = 0;
rect.Height = 0;
pictureBox1.Invalidate();
int radius = 10; //Set the number of pixel you want to use here
//Calculate the numbers based on radius
int x0 = Math.Max(e.X - (radius / 2), 0),
y0 = Math.Max(e.Y - (radius / 2), 0),
x1 = Math.Min(e.X + (radius / 2), pictureBox1.Width),
y1 = Math.Min(e.Y + (radius / 2), pictureBox1.Height);
Bitmap bm = pictureBox1.Image as Bitmap; //Get the bitmap (assuming it is stored that way)
for (int ix = x0; ix < x1; ix++)
{
for (int iy = y0; iy < y1; iy++)
{
bm.SetPixel(ix, iy, Color.Black); //Change the pixel color, maybe should be relative to bitmap
}
}
pictureBox1.Refresh(); //Force refresh
}
任何人请帮助我如何撤消上次执行的操作。
答案 0 :(得分:5)
因为您在内存中使用光栅图像,所以不能只撤消操作。可以有多种解决方案:
答案 1 :(得分:1)
执行此操作的一种方法是将bitmap
存储为上次操作之前的状态,然后将bitmap
重新绘制到picturebox
上。这可能不是最有效的,因为位图可能会变得非常大,这取决于它们的大小,但却是最简单和最快捷的方式之一。
另一种更有效的方法是以某种方式记录前后位图之间的差异,例如已更改的像素及其之前的颜色,并将这些像素回滚到其原始颜色。然后,这只保存已更改的像素,但编码更复杂。
答案 2 :(得分:1)
您可以声明一个私有的Image字段并将图像状态保存到它以供撤消使用,使用Memento Design Pattern来保存和加载对象“image”状态是最好的做法,抓住它就可以了。
然而,不是一次撤消/重做操作,更好的解决方案是实施多撤消/重做策略,如下所示:
示例:
private Stack<Image> _undoStack = new Stack<Image>();
private Stack<Image> _redoStack = new Stack<Image>();
private readonly object _undoRedoLocker = new object();
private void Undo()
{
lock (_undoRedoLocker)
{
if (_undoStack.Count > 0)
{
_redoStack.Push(_undoStack.Pop());
//OnUndo();
pictureBox1.Image = _redoStack.Peek();
pictureBox1.Refresh();
}
}
}
private void Redo()
{
lock (_undoRedoLocker)
{
if (_redoStack.Count > 0)
{
_undoStack.Push(_redoStack.Pop());
//OnRedo();
pictureBox1.Image = _undoStack.Peek();
pictureBox1.Refresh();
}
}
}
//And whenever image need to be modified, add it to the undo stack first and then modify it
private void UpdateImageData(Action updateImage)
{
lock (_undoRedoLocker)
{
_undoStack.Push(pictureBox1.Image);//image);
try
{
//manipulate the image here.
updateImage();
}
catch
{
_undoStack.Pop();//because of exception remove the last added frame from stack
throw;
}
}
}
private void pictureBox1_MouseClick(object sender, EventArgs e)
{
UpdateImageData(delegate()
{
rect.Width = 0;
rect.Height = 0;
pictureBox1.Invalidate();
int radius = 10; //Set the number of pixel you want to use here
//Calculate the numbers based on radius
int x0 = Math.Max(e.X - (radius / 2), 0),
y0 = Math.Max(e.Y - (radius / 2), 0),
x1 = Math.Min(e.X + (radius / 2), pictureBox1.Width),
y1 = Math.Min(e.Y + (radius / 2), pictureBox1.Height);
Bitmap bm = pictureBox1.Image as Bitmap; //Get the bitmap (assuming it is stored that way)
for (int ix = x0; ix < x1; ix++)
{
for (int iy = y0; iy < y1; iy++)
{
bm.SetPixel(ix, iy, Color.Black); //Change the pixel color, maybe should be relative to bitmap
}
}
pictureBox1.Refresh(); //Force refresh
}
}
注意:我没有测试上面的代码,但它可能正常工作,如果你发现任何问题留下评论