使用位图作为缓冲区在面板上绘制

3

我一直在学习如何使用位图绘制面板。我想运行一个试验程序,只是将白色面板变成黑色。(可能看起来很复杂,但这只是为了测试基础知识)我的程序如下:

public partial class Form1 : Form
{
    private Bitmap buffer = new Bitmap(100,100);

    public Form1()
    {
        InitializeComponent();
    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
       e.Graphics.DrawImageUnscaled(buffer, Point.Empty);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        for (int i = 0; i < 100; i++)
        {
            for (int j = 0; j < 100; j++)
            {
                buffer.SetPixel(i, j, Color.Black);
            }
        }
    }
}

当我运行它并按下按钮时,面板似乎没有改变。我到底做错了什么?提前感谢您的帮助。

除了其他问题之外,在位图上绘制矩形并填充它将更有效率,大约高达10,000倍。 - Tony Hopkinson
我可以使用什么方法来做到这一点?是位图方法还是其他方法? - Jack Wetherell
1
在绘图事件处理程序中,您可以使用e.Graphics来绘制简单的形状,例如使用FillRectangle()创建黑色矩形。但是,@TonyHopkinson,OP明确指出Bitmap不是最简单或最有效的方法;然而,如果Bitmap来自内存(假设由非托管图形库绘制)或者OP对其进行的更复杂的更改不仅仅是将每个像素设置为黑色,则这绝对是完成此操作的方法。 - KeithS
@KeithS。我完全不同意,setpixel是最后的手段。 - Tony Hopkinson
3个回答

3
您需要使面板客户区失效,这样Windows才会强制重新绘制。但还有其他问题:
  1. FillRectangle比在循环中每个像素点进行绘制更高效,正如@Tony所建议的。
  2. 如果在准备显示buffer之前面板被无效化,可能会遇到并发问题。请确保位图生成与其展示相隔离。
这些建议总结如下(但尚未测试):
private void button1_Click(object sender, EventArgs e)
{
    Bitmap tempBuffer = new Bitmap(100, 100);

    using (Graphics g = Graphics.FromImage(tempBuffer))
    using (SolidBrush blackBrush = new SolidBrush(Color.Black))
    {
        g.FillRectangle(blackBrush, new Rectangle(0, 0, tempBuffer.Width-1, tempBuffer.Height-1);
    }

    buffer = tempBuffer;
    panel1.Invalidate();
}

3

除了使面板的客户区失效外,如果您希望在单击按钮时进行绘制,您需要在按钮的单击事件中连接绘制事件。尝试以下内容:

public partial class Form1 : Form
{
    private bool _paintWired;

    public Form1()
    {
        InitializeComponent();
    }

    private void PanelPaint(object sender, PaintEventArgs e)
    {
        using (Graphics g = this.panel1.CreateGraphics())
        {
            g.FillRectangle(Brushes.Black, this.panel1.Bounds);
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if(!_paintWired)
        {
            this.panel1.Paint += new PaintEventHandler(PanelPaint);
            _paintWired = true;
        }

        this.panel1.Invalidate();
    }
}

更新:抱歉,我忽略了使用位图的重点。


1
不行。在构造函数或InitializeComponent()中附加处理程序。在您所做的位置附加它将导致每次单击按钮时再次附加,从而导致处理程序被多次调用(因此矩形被多次绘制)。在更复杂的示例中,您可能会期望这样做,或者处理程序是自动发生的某些事情,例如定时事件,否则它很快就会变得不可行。 - KeithS
非常正确,我匆忙之中忘记了那一部分。更新了代码以反映处理该问题的一种方式。 - Jeff
这样就好了?为什么你不把它放在我说的两个地方之一呢? - KeithS
因为原问题陈述了“当我运行它并按下按钮时,面板似乎没有改变。”他试图在按钮点击的响应中进行绘画。实际上,在响应中将面板涂成黑色并没有太多意义,但这就是问题所述和我尝试回答的内容。我一直将其连接到你提到的位置。 - Jeff
它能够工作,所以我会取消踩下的投票,但是为了记录,我不同意“懒惰”地附加事件处理程序。如果行为取决于某些用户事件,我更喜欢跟踪该事件已发生并在固定处理程序中使行为有条件。通过简单地将布尔值设置为true,然后在if语句中使用该布尔值来控制绘制矩形,您的代码将大大简化。 - KeithS

0

试试这个例子,我用它来做类似你想要做的事情,它很有效。希望能帮到你。

example_1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接