【问题标题】:Preserve painting after resize or refresh调整大小或刷新后保留绘画
【发布时间】:2015-03-03 11:36:00
【问题描述】:

如何保存我在图片框上绘制的画作?

我画了一个圆圈,并通过 ExtFloodFill API 填充它。 这很好用。

当我调整表单大小(或最小化它)并将其调整回原来的大小时,绘画的一部分消失了。

当我刷新图片框时,绘画将完全消失

我试图在 Paint 事件中重新绘制它,但这会导致它不断地重新绘制,因为绘画本身也触发了 Paint 事件。

请参阅下面的测试项目。

  • 当您点击图片框时,绘画将被绘制出来。
  • 双击图片框时会刷新。

[1 个表格和 1 个名为 pictureBox1 的图片框]

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;


namespace FloodFill
{
    public partial class Form1 : Form
    {
        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateSolidBrush(int crColor);
        [DllImport("gdi32.dll")]
        public static extern bool ExtFloodFill(IntPtr hdc, int nXStart, int nYStart, int crColor, uint fuFillType);
        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
        [DllImport("gdi32.dll")]
        public static extern int GetPixel(IntPtr hdc, int x, int y);
        public static uint FLOODFILLSURFACE = 1;

        public Form1()
        {
            InitializeComponent();
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            DrawCircle();
            FillGreen();
        }

        private void DrawCircle()
        {
            Graphics graBox = Graphics.FromHwnd(pictureBox1.Handle);
            graBox.DrawEllipse(Pens.Red, 10, 10, 100, 100);
        }

        private void FillGreen()
        {
            Graphics graBox = Graphics.FromHwnd(pictureBox1.Handle);
            IntPtr ptrHdc = graBox.GetHdc();
            IntPtr ptrBrush = CreateSolidBrush(ColorTranslator.ToWin32(Color.Green));
            SelectObject(ptrHdc, ptrBrush);
            ExtFloodFill(ptrHdc, 50, 50, GetPixel(ptrHdc, 50, 50), FLOODFILLSURFACE);
            DeleteObject(ptrBrush);
            graBox.ReleaseHdc(ptrHdc);
        }

        private void pictureBox1_DoubleClick(object sender, EventArgs e)
        {
            pictureBox1.Refresh();
        }

    }
}

当我的表单或图片框被调整大小或以任何其他方式刷新时,我如何保存我所做的绘画?

[编辑]

我将我的 Paint 事件更改为以下内容:

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        DrawCircle();
        FillGreen();
    }

现在圆圈在调整大小后被重新绘制,但 FloodFill 不是

(我还为图片框设置了浅蓝色背景以进行另一次测试)

[EDIT2]

我将 Paint 事件更改为使用 Graphics g,如下所示:

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        DrawCircle(g);
        FillGreen(g);
    }

    private void DrawCircle(Graphics g)
    {
        g.DrawEllipse(Pens.Red, 10, 10, 100, 100);
    }

    private void FillGreen(Graphics g)
    {
        IntPtr ptrHdc = g.GetHdc();
        IntPtr ptrBrush = CreateSolidBrush(ColorTranslator.ToWin32(Color.Green));
        SelectObject(ptrHdc, ptrBrush);
        ExtFloodFill(ptrHdc, 50, 50, GetPixel(ptrHdc, 50, 50), FLOODFILLSURFACE);
        DeleteObject(ptrBrush);
        g.ReleaseHdc(ptrHdc);
    }

但是当我调整回原始大小时,会跳过 FloodFill 的某些行,尤其是当我缓慢调整大小时

【问题讨论】:

  • 您需要使用来自 Paint 参数的 e.Graphics 或图片框的图像位图来绘制或从绘制事件中绘制。使用从位图创建的图形!如您所见,其他任何事情都不会持续存在。我也看不出你为什么不使用 gdi+ 绘图,假设你使用的是 winforms..
  • 我来自 VB6,正在尝试将我的应用程序转换为 C# ...我编辑了我的问题以添加我尝试过的新 Paint 事件,其结果:现在正在重绘圆圈,但填充不是
  • 您能解释一下如何使用 Paint 参数中的 e.Graphics 吗?与我所做的相比,gdi+ 绘图又如何呢?
  • 在表单的Resize 事件中调用pictureBox1.Invalidate(); 可以轻松避免调整大小Paint 错误!
  • 谢谢!这成功了:)

标签: c# refresh repaint flood-fill


【解决方案1】:

使用GDI+方法绘制代码很简单:

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    Rectangle rect = new Rectangle(10, 10, 100, 100);
    e.Graphics.FillEllipse(Brushes.Green, rect);
    using (Pen pen = new Pen(Color.Red, 2f))
    {
      e.Graphics.DrawEllipse(pen , rect);
    }
}

根本不需要您的DllImport 或常量。

请注意,我选择使用宽度为 2f 的 Pen 来演示使用 using 子句正确创建和处置 GDI+(非标准)对象 Pen

这将持续存在,因为它会在需要时绘制。要开始绘制它,您必须调用一次pictureBox1.Invalidate();

如果要更改坐标,应将变量 rect 移动到类级别,将其设置为新数据并在 PictureBox 上调用 InvalidateColorsPen.Width 也是如此:使用类级别变量并在每次更改后通过调用 Invalidate() 触发 Paint 事件。

一旦你明白这一点,在GDI+学习绘画最重要的部分就完成了..

要填充任何图纸,您有三个选项:

  • 使用DrawXXX 方法绘制的简单形状都有FillXXX 方法。
  • 可以使用GraphicsPath 创建复杂的形状,它还具有DrawXXX 方法和FillXXX 方法。
  • 不是由形状而是由绘制的像素创建的区域必须使用填充方法填充。 GDI+ 里面没有内置,但是可以自己写。也许像the Fill4 in this answer..

更新:如果您觉得使用gdi32.dll 中的FloodFill 感觉更好,您可以这样做,但请更改代码以使用Paint 事件中的Graphics 对象:

   FillGreen(e.Graphics);

private void FillGreen(Graphics graBox)
{
    IntPtr ptrHdc = graBox.GetHdc();
    IntPtr ptrBrush =  CreateSolidBrush(ColorTranslator.ToWin32(Color.Green));
    SelectObject(ptrHdc, ptrBrush);
    ExtFloodFill(ptrHdc, 50, 50, GetPixel(ptrHdc, 50, 50), FLOODFILLSURFACE);
    DeleteObject(ptrBrush);
    graBox.ReleaseHdc(ptrHdc);
}

为了获得更好的灵活性,您可以将参数全部设为动态,但我不习惯那个旧库..

请注意,调用ma​​tters顺序FillXXX 将覆盖DrawXXX 绘制的一些像素,因此它必须先出现。 FloodFill 然而取决于边界线,在你的情况下,圆圈首先被绘制,所以它必须在之后..

【讨论】:

  • 感谢您的示例! ...最终我想在图片框(或其图形)上绘制位图(png文件),并动态填充该位图的某些奇形区域...填充颜色将取决于我通过tcp从a接收的数据连接
  • 对于我的 java 小程序,我确实创建了自己的 Floodfill 方法,效果很好,但是在我的 VB6 程序中,我使用了 ExtFloodFill API ...VB6 程序比 java 小程序发展得更远,所以我考虑转换那个...并且在 VB6 中使用 ExtFloodFill API 非常容易:-)
  • DrawImage 方法用于在位图上绘图。对于填充,您可能确实需要一种 Floodfill 方法,例如链接中的方法..
  • 顺便说一句:您还应该决定一个基本问题:您可以在 PictureBox 的表面上绘制 into 位图图像。 See here!here
  • 查看我正确使用 gdi32.dll 调用的更新答案!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-28
  • 2014-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-23
相关资源
最近更新 更多