【问题标题】:save bitmap in list for undo c# fails at second time将位图保存在列表中以进行撤消 c# 第二次失败
【发布时间】:2014-04-09 02:30:53
【问题描述】:

我有一些代码允许我(Windows 窗体)加载图像,在其上绘制一个矩形并保存它。但是我想实现“撤消”功能。如果我写一个矩形,我会在位图中绘制并保存修改后的位图。绘制另一个矩形后,我也保存位图(在列表中)。然而,我创建了一个按钮,删除我保存的最后一个位图,并将位图(新的最后一个)设置为图片框中的图像。那行得通,但是如果我做另一个矩形并单击撤消,则没有任何反应。我很困惑,不知道问题出在哪里。这是我的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace RecAngle
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
         this.DoubleBuffered = true;
    }
    Rectangle mRect;
    Bitmap bm;
    Image file;
    Boolean opened = false;
    SaveFileDialog sfd = new SaveFileDialog();
    OpenFileDialog ofd = new OpenFileDialog();
    Boolean draw = false;
    List<Bitmap> bitMapList = new List<Bitmap>();
    Boolean undo = false;

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
        mRect = new Rectangle(e.X, e.Y, 0, 0);
        pictureBox1.Invalidate();

    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            mRect = new Rectangle(mRect.Left, mRect.Top, e.X - mRect.Left, e.Y - mRect.Top);
            pictureBox1.Invalidate(); 
            draw = true;
        }
    }
    private void button1_Click(object sender, EventArgs e)
    {
        DialogResult dr = ofd.ShowDialog();
        if (dr == DialogResult.OK)
        {
            file = Image.FromFile(ofd.FileName);
            bm = new Bitmap(ofd.FileName);
            pictureBox1.Image = bm;
            bitMapList.Add(bm);
            opened = true;
        }
    }
    private void button2_Click(object sender, EventArgs e)
    {
        DialogResult dr = sfd.ShowDialog();
        if (opened)
        {
                try
                {
                    bm.Save(sfd.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
                }
                catch (Exception x)
                {
                    Console.WriteLine(x);
                }
        }
    }

    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
        try
        {
            if (draw)
            {
                if (undo)
                {
                    bm = bitMapList[bitMapList.Count - 1];

                }

                    using (Graphics a = Graphics.FromImage(bm))
                    {
                        Pen pen = new Pen(Color.Red, 2);
                        a.DrawRectangle(pen, mRect);
                        pictureBox1.Invalidate();
                        bitMapList.Add( new Bitmap(bm));
                        pictureBox1.Image = bitMapList[bitMapList.Count - 1];

                    }
            }
        }
        catch (Exception x)
        {
            Console.WriteLine(x);
        }
    }

    private void button3_Click(object sender, EventArgs e)
    {

        if (bitMapList.Count != 0)
        {
            bitMapList.RemoveAt(bitMapList.Count - 1);
            pictureBox1.Image = bitMapList[bitMapList.Count - 1];
            undo = true;

        }
    }

}
}

我保存的东西有错吗?我认为它与“删除”有关,但我真的没有看到错误。 谢谢你的帮助

【问题讨论】:

    标签: c# bitmap


    【解决方案1】:

    您的代码非常混乱。你为什么一直打电话给Invalidate?你为什么使用ListStack 更合适?为什么在MouseUp 方法中进行撤消时分配bm,而不是直接在撤消按钮中单击?你为什么要让Graphics 实例存活这么久,而你只需要它来绘制一个矩形?

    最后,为什么不将undo 设置回 false?

    此外,您对位图一次在哪里感到非常困惑。在 Undo 方法中,您检查bitMapList.Count != 0,然后删除最后一项。但是,如果bitMapList.Count1,则下一行将导致IndexOutOfRangeException

    你必须打破这种困惑。想想你想做什么,想想如何干净利落地做。这是一团糟。您想要历史记录中的哪些位图?您是否真的需要为“实际”位图创建一个单独的位图实例,即使您刚刚将相同的位图放入历史记录中?为什么不简单地将历史上最新的位图视为“当前”位图?然后,您只需 Pop 堆栈中的最后一项,一切都会毫无问题,而且干净利落。当然,您必须在位图的新实例上进行绘制,但这并没有开销,因为new Bitmap(bm); 无论如何都会做同样的事情。唯一会改变的是顺序 - 您将创建新的位图,绘制矩形并将 Push 它放入堆栈。就这么简单。

    代码应该易于理解。这是代码维护的主要目标之一。这就是为什么你想将你的逻辑分成不同的方法等,它必须易于阅读和理解,并且在一些负责的小范围内将所有内容都包含在一些方法中会有所帮助。如果您已经对这么短的代码感到困惑,想象一下维护一个比这更复杂的应用程序会有多困难。

    话虽如此,解决您当前问题的快速方法是更改​​ pictureBox1_MouseUp 中的撤消代码,如下所示:

    if (undo)
    {
      bm = new Bitmap(bitMapList[bitMapList.Count - 1]);
      undo = false;
    }
    

    但是,我强烈建议您改写代码,使其更清晰。你甚至没有命名你的控件,这根本不利于可读性。为什么不用btnUndo 而不是button3

    【讨论】:

    • 感谢您的详细帮助!我来自 webdynpros ABAP 的开发,并且以一种特殊的方式。然而,我必须做这个“绘画”来编辑票务系统的屏幕截图,并且必须快速开发解决方案,但我对 C# 完全陌生,并且必须同时制作其他项目,所以这个简短的代码让我很困惑,因为每个小时我必须从 Webdynpro 更改为 Javascript,然后更改为 C# 等等……这个问题已经解决了,但我重组了代码并使用堆栈来完成。真的,真的很感谢你的回答,太好了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-01
    • 1970-01-01
    • 2011-09-02
    • 1970-01-01
    • 2013-12-17
    • 2020-10-02
    • 2011-03-22
    相关资源
    最近更新 更多