PictureBox.Invalidate没有正确重新渲染

时间:2014-09-09 14:30:21

标签: c# winforms

我正在尝试构建一个小测试应用程序(并且我的WinForm技能已经有点生锈),并在其上面添加了一个Image和一些叠加层。

Example

我的图像设置为在PictureBox中拉伸,但我右侧的字段我想要来自图像的原点。因此,我决定直接渲染PictureBox正在使用的图像,以确保坐标始终正确。这是白框渲染:

  private void pbImage_Paint(object sender, PaintEventArgs e)
    {
        try
        {
            if (this.rdFront.Checked)
                RenderFront(pbImage.Image, true);
            else
                RenderBack(pbImage.Image, true);

        }
        catch (ArgumentNullException ex)
        { }
    }

public void RenderFront(Image image, bool includeBoxes)
    {
        // If we have no image then we can't render
        if (image == null)
            throw new ArgumentNullException("image");

        Graphics gfx = Graphics.FromImage(image);

        // Get the top label
        foreach (MessageConfiguration config in this.config.Values.Where(c => c.Front))
        {
            if (includeBoxes)
            {
                // Fill a White rectangle and then surround with a black border
                gfx.FillRectangle(Brushes.White, config.X, config.Y, config.Width, config.Height);
                gfx.DrawRectangle(Pens.Black, config.X - 1, config.Y - 1, config.Width + 2, config.Height + 2);
            }

            gfx.DrawString(config.Text, new Font(FontFamily.GenericMonospace, config.FontSize), Brushes.Black, new PointF(config.X, config.Y));
        }
    }

我遇到的问题是如果我这样做并且总是在底层图像上绘制,那么当我移动白色叠加层时,我最终会得到未绘制的图像部分。所以我决定在每次重新渲染之前克隆图像(基于我不关心性能)。

因此,我决定在需要手动使其无效时克隆图像,并在设置更改时调用此图像:

    public void Refresh()
    {
        if (this.rdFront.Checked)
            pbImage.Image = new Bitmap(front);
        else
            pbImage.Image = new Bitmap(back);

        this.pbImage.Invalidate();
    }

现在我确定我必须遗漏一些明显的东西 - 如果我修改了我的企鹅渲染的其中一个没有叠加的值。但是,如果我强制调整应用程序的大小,那么企鹅和叠加层都会突然出现。

任何人都可以提出我可能做错的建议吗?

修改

这是项目的下载链接,因为它非常小。将图像路径粘贴到“正面图像”框中,然后尝试使用右侧的控件(设置100x100高度和宽度)。尝试重新调整大小以查看所需的效果。 https://dl.dropboxusercontent.com/u/41796243/TemplateTester.zip

1 个答案:

答案 0 :(得分:2)

控件和表单已经有Refresh方法。你真的打电话给你的 Refresh方法吗?您是否收到警告,表示您应该使用new关键字?最好为您的Refresh方法添加其他名称(例如RefreshImage)!


我真的不确定你为什么要使用一个图片框,但后来决定做你的绘画。我建议在屏幕外绘制一个图像,然后将其分配到图片框中:

public void RefreshImage()
{
    Bitmap bmp;
    if (this.rdFront.Checked)
        bmp = new Bitmap(front);
    else
        bmp = new Bitmap(back);

    using (Graphics gfx = Graphics.FromImage(bmp)) {
        foreach (MessageConfiguration config in this.config.Values.Where(c => c.Front))
        {
            if (includeBoxes) {
                // Fill a White rectangle and then surround with a black border
                gfx.FillRectangle(Brushes.White, config.X, config.Y, config.Width, config.Height);
                gfx.DrawRectangle(Pens.Black, config.X - 1, config.Y - 1, config.Width + 2, config.Height + 2);
            }
            gfx.DrawString(config.Text, new Font(FontFamily.GenericMonospace, config.FontSize), Brushes.Black, new PointF(config.X, config.Y));
        }
    }

    pbImage.Image = bmp;
}

并删除pbImage_Paint方法。


另一种可能性是以另一种方式使用pbImage_Paint事件处理程序。调用绘制图像的图片框的base.Paint()处理程序,但保持图像本身不变。而是使用Graphics参数给出的PaintEventArgs e对象在其上绘制。此Graphics对象表示图片框的客户区。这不会改变分配给图片框的位图,而只会在屏幕上绘制。

private void pbImage_Paint(object sender, PaintEventArgs e)
{
    base.Paint(); // Paints the image
    if (this.rdFront.Checked)
        RenderFront(e.Graphics, true);
    else
        RenderBack(e.Graphics, true);
}

public void RenderFront(Graphics g, bool includeBoxes)
{
    foreach (MessageConfiguration config in this.config.Values.Where(c => c.Front)) {
        if (includeBoxes) {
            g.FillRectangle(Brushes.White, config.X, config.Y, config.Width, config.Height);
            g.DrawRectangle(Pens.Black, config.X - 1, config.Y - 1, config.Width + 2, config.Height + 2);
        }
        g.DrawString(config.Text, new Font(FontFamily.GenericMonospace, config.FontSize), Brushes.Black, new PointF(config.X, config.Y));
    }
}