调整大小或刷新后保留绘画
Preserve painting after resize or refresh
如何保存我在画框上画的画?
我画了一个圆,并通过 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 不是
(我也给picturebox设置了淡蓝色背景来测试一下)
[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 的一些行会被跳过,尤其是当我缓慢调整大小时
使用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);
}
}
None 的 DllImport
或常量是根本不需要的。
注意我选择使用宽度为2f的Pen来演示使用using
子句正确创建和处置GDI+
(非标准)对象Pen
这将持续存在,因为它会在需要时绘制。要最初绘制它,您必须调用 pictureBox1.Invalidate();
一次!
如果要更改坐标,应将变量 rect
移动到 class 级别,将其设置为新数据并在 PictureBox
上调用 Invalidate
! Colors
或 Pen.Width
也是如此:使用 class 级别变量,每次更改后通过调用 Invalidate()
..
触发 Paint 事件
一旦你理解了这一点,GDI+
中学习绘画最重要的部分就完成了..
要填写任何图纸,您有以下三种选择:
- 使用
DrawXXX
方法绘制的简单形状都有 FillXXX
方法。
- 可以使用
GraphicsPath
创建复杂的形状,它也有 DrawXXX
方法和 FillXXX
方法。
- 不是由形状创建而是由绘制像素创建的区域必须使用 floodfill 方法进行填充。
GDI+
中没有内置,但可以简单地编写自己的。也许像 the Fill4
in this answer..
更新:如果您觉得使用 gdi32.dll
中的 FloodFill
感觉更好,您可以这样做,但请更改代码以使用 Graphics
来自 Paint
事件的对象:
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);
}
为了更好的灵活性,您可以将参数全部动态化,但我不习惯那个旧库..
请注意,顺序调用很重要:FillXXX
将覆盖[=24=绘制的一些像素],所以它必须排在第一位。 FloodFill
然而取决于边界线,在你的情况下是圆圈,先绘制,所以它必须在之后..
如何保存我在画框上画的画?
我画了一个圆,并通过 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 不是
(我也给picturebox设置了淡蓝色背景来测试一下)
[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 的一些行会被跳过,尤其是当我缓慢调整大小时
使用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);
}
}
None 的 DllImport
或常量是根本不需要的。
注意我选择使用宽度为2f的Pen来演示使用using
子句正确创建和处置GDI+
(非标准)对象Pen
这将持续存在,因为它会在需要时绘制。要最初绘制它,您必须调用 pictureBox1.Invalidate();
一次!
如果要更改坐标,应将变量 rect
移动到 class 级别,将其设置为新数据并在 PictureBox
上调用 Invalidate
! Colors
或 Pen.Width
也是如此:使用 class 级别变量,每次更改后通过调用 Invalidate()
..
一旦你理解了这一点,GDI+
中学习绘画最重要的部分就完成了..
要填写任何图纸,您有以下三种选择:
- 使用
DrawXXX
方法绘制的简单形状都有FillXXX
方法。 - 可以使用
GraphicsPath
创建复杂的形状,它也有DrawXXX
方法和FillXXX
方法。 - 不是由形状创建而是由绘制像素创建的区域必须使用 floodfill 方法进行填充。
GDI+
中没有内置,但可以简单地编写自己的。也许像 theFill4
in this answer..
更新:如果您觉得使用 gdi32.dll
中的 FloodFill
感觉更好,您可以这样做,但请更改代码以使用 Graphics
来自 Paint
事件的对象:
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);
}
为了更好的灵活性,您可以将参数全部动态化,但我不习惯那个旧库..
请注意,顺序调用很重要:FillXXX
将覆盖[=24=绘制的一些像素],所以它必须排在第一位。 FloodFill
然而取决于边界线,在你的情况下是圆圈,先绘制,所以它必须在之后..