【问题标题】:Paint / Shapes / Graphics issues (ZIP provided)油漆/形状/图形问题(提供 ZIP)
【发布时间】:2021-01-16 16:28:42
【问题描述】:

我的代码需要一些帮助,因为我做错了!我试图以正确的方式修复和做所有事情,但我遇到了更多问题......下面的代码是一个类似疼痛的程序的原型,在我知道我需要的一切并且原型完美运行之后,我想创建它。

我在这里分享了完整的代码,我还提供了一个链接来下载我的完整项目/解决方案,这样您就可以运行它来查看发生了什么或修改以进行测试.

下载: PainPrototype_BushWookie

主要问题:

  • 我想通过鼠标按钮创建矩形,就像此功能在绘图应用程序中的工作方式一样。这意味着当我的鼠标向下时我开始绘制矩形,当我在画布中移动鼠标时,矩形会动态改变其大小以适应。当我的鼠标向上时,矩形就完成了。我相信你知道我在说什么,因为这是几乎所有绘图软件绘制形状(包括矩形)的方式。我可以对此进行编码,但我不确定我是否以正确的方式进行了编码,而且我对此也有疑问!绘制一个矩形后,再次单击以绘制另一个矩形后,前一个矩形消失/删除。我需要在您的帮助下解决此问题!

当我使用我的“图形”对象(在代码中注释)和 不是“e.Graphics”来绘制矩形,这将绘制每个形状 厚/框架/更新..所以当我移动鼠标时,我会是数百 矩形作为这个连续绘制..

  • 我还有“画笔”工具,它可以像 Photoshop 中的画笔工具或基本 Windows Paint 中的钢笔工具一样在画布上绘图。这部分工作正常!但我是从“MouseMove”事件而不是从 Paint 中绘制的。我在教程视频中看到了这一点,但每个人都说“仅从疼痛事件中绘制”。当我尝试这样做时,画笔的功能已经毁了。这意味着,它绘制直线而不是“追逐”鼠标光标并在其路径之后绘制。你能帮我澄清一下吗,以及这应该如何在 Paint 事件中起作用?

  • 当我单击“清除”按钮时,我的最后一个问题出现了,它应该清除整个画布!这适用于我的画笔(仅当我从 MouseMove 绘制时)和像素工具(绘制单个像素)并正确清除画布。我从 Paint 事件中绘制的矩形没有被清除。在我按下“新建”按钮以获取新的空画布后,矩形也会留在那里。这是个大问题!如果我从 Paint 事件中清除它,它会这样做,但这会导致 Paint 事件在无限循环中调用..

这是我的主要问题,我需要帮助! 我还有一些关于绘图/图形的问题,我无法从 MSDN 中正确理解:

  • 为什么我应该只从疼痛事件中绘制?或者跳出这个问题,如果我从 MouseMove 绘图有什么问题?
  • 什么是 OnPaint(),为什么它与 Paint() 不同?
  • Invalidate().. 我知道这会调用 Paint 事件,但还有什么要知道的吗?为什么它与导致 Paint 事件重绘的任何其他方法不同?

我还想要求概述我的每一行代码,以帮助了解我可以改进的地方,因为我是 C# 的新手!例如,当我尝试从“新建”按钮中释放旧的图片框时..我所有的行都是必要的?还是我错过了什么?等等等等..或者我的代码逻辑很好,还是我应该以其他方式做?

提前感谢您的宝贵时间!如果您从上面的链接下载我的完整解决方案以便更轻松地查看并运行/测试它,那将是最好的!我只是想帮助解决我的问题并以正确的方式学习编码!再次感谢,感谢您的帮助!


这是我的代码,请注意pbx_canvasbmp_canvas的区别! (我想在位图上绘制,因为我想稍后保存创建的图像)。

namespace NewFile___PixelDraw___Pen___Shapes
{
    public partial class Form1 : Form
    {
        PictureBox pbx_usedColorBox;
        PictureBox pbx_canvas;
        Bitmap btm_canvas;
        Graphics graphics;
        Pen pen;

        Button btn_activeTool = new Button();
        Point initialMouse = new Point(-1, -1);
        Color chosenColor = Color.White;
        bool mouseDown;

        Rectangle rect;

        public Form1()
        {
            InitializeComponent();

            pen = new Pen(chosenColor, (float)num_drawWidth.Value)
            {
                StartCap = System.Drawing.Drawing2D.LineCap.Round,
                EndCap = System.Drawing.Drawing2D.LineCap.Round,
            };

            tbx_canvasX.MaxLength = 5;
            tbx_canvasY.MaxLength = 5;

            pbx_usedColorBox = pbx_cBlack;
            pnl_cPalette.BackColor = chosenColor;
            pbx_cWhite.BorderStyle = BorderStyle.None;
        }

        // NEW
        private void btn_new_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(tbx_canvasX.Text) && !string.IsNullOrEmpty(tbx_canvasY.Text))
            {
                Point offset = new Point(7, 19);
                Point canvasSize = new Point(int.Parse(tbx_canvasX.Text), int.Parse(tbx_canvasY.Text));
                Point canvasLocation = new Point((Size.Width / 2) - (canvasSize.X / 2) - offset.X, (Size.Height / 2) - (canvasSize.Y / 2) - offset.Y);

                if (canvasSize.X != 0 && canvasSize.Y != 0)
                {
                    if (pbx_canvas != null && btm_canvas != null)
                    {
                        pbx_canvas.MouseDown -= Pbx_canvas_MouseDown;
                        pbx_canvas.MouseMove -= Pbx_canvas_MouseMove;
                        pbx_canvas.MouseUp -= Pbx_canvas_MouseUp;
                        Controls.Remove(pbx_canvas);
                        pbx_canvas.Dispose();
                        btm_canvas.Dispose();
                        graphics.Dispose();
                    }

                    pbx_canvas = new PictureBox
                    {
                        Size = new Size(canvasSize.X, canvasSize.Y),
                        BorderStyle = BorderStyle.FixedSingle,
                        Anchor = AnchorStyles.None,
                        Location = canvasLocation,
                        BackColor = Color.White,
                        Cursor = Cursors.Cross,
                    };

                    Controls.Add(pbx_canvas);
                    pbx_canvas.MouseDown += Pbx_canvas_MouseDown;
                    pbx_canvas.MouseMove += Pbx_canvas_MouseMove;
                    pbx_canvas.MouseUp += Pbx_canvas_MouseUp;
                    pbx_canvas.Paint += Pbx_canvas_Paint;

                    btm_canvas = new Bitmap(canvasSize.X, canvasSize.Y);
                    graphics = Graphics.FromImage(btm_canvas);

                    if (cbx_smoothing.Checked)
                    {
                        graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                    }
                }
                else
                {
                    tbx_canvasX.Text = "";
                    tbx_canvasY.Text = "";
                }
            }
        }

        // PAINT
        private void Pbx_canvas_Paint(object sender, PaintEventArgs e)
        {
            Debug.WriteLine("IN PAINT EVENT!");

            e.Graphics.DrawRectangle(pen, rect);
            //graphics.DrawRectangle(pen, rect);
        }

        // M > DOWN
        private void Pbx_canvas_MouseDown(object sender, MouseEventArgs e)
        {
            switch (btn_activeTool.Name)
            {
                case "btn_toolBrush":
                    mouseDown = true;
                    initialMouse = e.Location;
                    break;
                case "btn_toolRect":
                    mouseDown = true;
                    initialMouse = e.Location;
                    rect = new Rectangle(initialMouse, new Size(0, 0));
                    break;
            }
        }

        // M > MOVE
        private void Pbx_canvas_MouseMove(object sender, MouseEventArgs e)
        {
            switch (btn_activeTool.Name)
            {
                case "btn_toolBrush":
                    if (mouseDown == true && initialMouse.X != -1 && initialMouse.Y != -1)
                    {
                        graphics.DrawLine(pen, initialMouse, e.Location);
                        pbx_canvas.Image = btm_canvas;
                        initialMouse = e.Location;
                    }
                    break;
                case "btn_toolRect":
                    if (mouseDown == true && initialMouse.X != -1 && initialMouse.Y != -1)
                    {
                        rect = new Rectangle(Math.Min(e.X, initialMouse.X),
                                             Math.Min(e.Y, initialMouse.Y),
                                             Math.Abs(e.X - initialMouse.X),
                                             Math.Abs(e.Y - initialMouse.Y));

                        pbx_canvas.Image = btm_canvas;
                        //Invalidate(rect);
                    }
                    break;
            }
        }

        // M > UP
        private void Pbx_canvas_MouseUp(object sender, MouseEventArgs e)
        {
            switch (btn_activeTool.Name)
            {
                case "btn_toolPixel":
                    btm_canvas.SetPixel(e.X, e.Y, chosenColor);
                    pbx_canvas.Image = btm_canvas; 
                    break;
                case "btn_toolBrush": 
                    mouseDown = false;
                    initialMouse.X = -1;
                    initialMouse.Y = -1;
                    break;
                case "btn_toolRect":
                    mouseDown = false;
                    initialMouse.X = -1;
                    initialMouse.Y = -1;
                    break;
            }
        }

        // PIXEL
        private void btn_toolPixel_Click(object sender, EventArgs e)
        {
            btn_activeTool.BackColor = Color.GhostWhite;
            btn_activeTool = (Button)sender;
            btn_activeTool.BackColor = Color.Beige;
        }

        // BRUSH
        private void btn_toolBrush_Click(object sender, EventArgs e)
        {
            btn_activeTool.BackColor = Color.GhostWhite;
            btn_activeTool = (Button)sender;
            btn_activeTool.BackColor = Color.Beige;
        }

        // RECT
        private void btn_toolRect_Click(object sender, EventArgs e)
        {
            btn_activeTool.BackColor = Color.GhostWhite;
            btn_activeTool = (Button)sender;
            btn_activeTool.BackColor = Color.Beige;
        }

        // CLEAR
        private void btn_clear_Click(object sender, EventArgs e)
        {
            graphics.Clear(pbx_canvas.BackColor);
            pbx_canvas.Image = null;
        }

        // Color change
        private void G_pbx_colors_Click(object sender, EventArgs e)
        {
            PictureBox pbx_sender = (PictureBox)sender;

            pbx_usedColorBox.BorderStyle = BorderStyle.FixedSingle;
            pbx_sender.BorderStyle = BorderStyle.None;
            pbx_usedColorBox = pbx_sender;

            chosenColor = pbx_sender.BackColor;
            pnl_cPalette.BackColor = chosenColor;
            pen.Color = chosenColor;
        }

        // CanvasSize
        private void G_tbx_canvasSize_Enter(object sender, EventArgs e)
        {
            BeginInvoke(new Action(() => (sender as TextBox).SelectAll()));
        }
        private void G_tbx_canvasSize_KeyPress(object sender, KeyPressEventArgs e)
        {
            e.Handled = !char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar);
        }

        // DrawWidth
        private void num_drawWidth_ValueChanged(object sender, EventArgs e)
        {
            pen.Width = (float)num_drawWidth.Value;
        }
    }
}

【问题讨论】:

  • 与其发布完整的 coee 并编写如此多的应用程序,您应该首先了解基础知识。您必须遵守规则,并且可能会更改大部分代码。 - 第一步了解持久绘图和非持久绘图之间的区别。如果您的矩形应该是橡皮筋矩形,请使用 this 之类的代码 - 对于“真实”的东西,您需要确定它们是否是动态的并将它们与其他所有内容一起绘制......
  • ..每次发生任何变化或系统需要解除。 或者,如果你想绘制成位图。对于差异see here
  • 简单示例:How to use the Paint event to draw shapes at mouse coordinates。仅使用 Paint 事件,或在自定义控件/用户控件/表单中覆盖 OnPaint()OnPaint() 是引发 Paint 事件的方法。当您需要重新绘制画布时,请致电 Invalidate()。 + @TaW 没有发布,所以我会这样做:Creating different brush patterns。这些应该可以帮助您入门。
  • #TaW - 你可以告诉 90% 的在这里提问的人...这对你来说很简单,初学者很容易混淆,有时也很难为初学者在网上妥善照顾。这就是为什么我认为最好分享所有内容以检查它......无论如何,感谢你们两个非常有用的链接!仍然困惑我应该在哪里为动态形状写我的绘图材料,因为一个人说的和其他人不同。我尝试修复我的代码!谢谢!

标签: c# winforms graphics draw paint


【解决方案1】:

为什么我应该只从 Paint 事件中绘制?或者跳出这个问题,如果我从 MouseMove 中绘图有什么问题?

你打算画什么?您无权访问控件的图形对象。如果需要,您可以绘制位图或其他缓冲区。但是绘制事件处理程序并不是一个好主意。您可能有多个输入事件一个接一个,如果每个事件都导致代价高昂的绘制,而不是简单的无效,应用程序将无法正常工作。

什么是 OnPaint(),为什么它与 Paint() 不同?

覆盖 OnPaint 或使用 Paint-event 通常并不重要。

Invalidate().. 我知道这会调用 Paint 事件,但还有什么要知道的吗?为什么它与任何其他导致 Paint 事件重绘的方法不同?

这不会调用绘制事件。它会将控件标记为稍后需要重绘。

提供的代码有很多问题,比如

  • 在 switch 语句中使用字符串
  • 所有逻辑似乎都在一个类中
  • 您在按钮单击处理程序中附加事件
  • 据我所知,你没有处理任何东西

这是关于我将如何解决问题的高级概述。

激活工具时,应该有一个状态机来跟踪使用的工具,以及如何为工具绘制形状。您似乎有一个原始状态机,但我建议使用类对状态进行建模,以便每个工具彼此独立。

使用工具完成后,您应该将完成的形状绘制到表示完成图形的位图上。此位图应在活动工具的图形之前绘制到屏幕上。这样,当您绘制下一个矩形时,前一个矩形将存储在位图中。

另一种方法是保留使用工具时附加的形状缓冲区。这样您就可以依次绘制每个形状,并且可以在创建后删除或更改它。

【讨论】:

  • 感谢您的回答,我学到了很多东西,但对我来说,“使用类的状态机”之类的东西很难弄清楚在实践中应该如何看待。我需要询问有关“一旦您使用工具完成...”部分的更多详细信息。我试图把我的 'pbx_canvas.Image = btm_canvas; ' 'graphics.DrawRectangle' 行之前的部分,但效果不好。我还得到了一个结果,在我移动鼠标时矩形连续绘制...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-04
  • 1970-01-01
相关资源
最近更新 更多