-1

我有一个相对简单的 Windows 应用程序。它使用大约 15 个控件。

我的构造函数方法如下所示:

public Form1()
{
    InitializeComponent();
    this.Paint += this.Teken;
}

我的 Teken 方法(简化)如下所示:

private void Teken(object o, PaintEventArgs pea)
{
    if (start)
    {
        if (comboBox1.Text == "Basic")
            Bitmaps.DrawBitmap1(pea.Graphics, bitmapsize, max);
    }
}

我的 DrawBitmap1 方法将每个像素设置为某种颜色,然后使用 Graphics.DrawImage 方法绘制位图。

绘制位图后,控件变得非常滞后,在文本框中选择文本需要一秒钟多的时间。主要形式变得非常缓慢。

我可以理解要慢慢绘制的位图,但我无法理解这一点。

我的问题有简单的解决方案吗?

编辑:

这是 Drawbitmap1 代码:

public static void DrawBitmap1(Graphics gr, int bitmapsize, int max)
    {
        Bitmap bitmap1 = new Bitmap(bitmapsize, bitmapsize);

        for (int x = 1; x < bitmapsize; x++)
        {
            for (int y = 1; y < bitmapsize; y++)
            {
                int mandelnumber = CalculateMandel(x,y)

                if (Form1.mandelnumber == max) 
                    bitmap1.SetPixel(x, y, Color.Black);
                else if (Form1.mandelnumber %2 == 0) 
                    bitmap1.SetPixel(x, y, Color.White);
                else 
                    bitmap1.SetPixel(x, y, Color.Black);
            }
        }
        gr.DrawImage(bitmap1, 50, 100);
    }

mandelnumber 是在程序的另一部分计算的变量。

编辑2:

我运行了一个分析器,似乎CalculateMandel() 方法在通过位图的每个像素后并没有停止运行。这怎么可能?

编辑3:

结果当鼠标悬停在文本框或按钮上时,再次调用了 DrawBitmap1 函数。这怎么可能,我没有任何悬停事件,甚至没有 TextChanged 事件..

4

3 回答 3

1

您的DrawBitmap1方法可能是从源位图到绘图表面支持的位图的某种程度的转换。这可能是不同的颜色密度、不同的位密度等(可能是缩放?)。这通常需要一些额外的时间。

我建议改用PictureBox控件。

此外,如果您需要重新绘制要显示的图像,那么我建议只在图像发生变化时进行,而不是在绘制/绘制屏幕时进行 - 这会导致性能下降。例如

pictureBox.Image = DrawBitmap1(gr, bitmapSize, max);

如果您要在一秒钟内更改图像超过几次,我建议您不要使用 SetPixel,因为您将永远无法获得可以接受的刷新屏幕的性能。

上漆的问题在于,当疼痛发生时和位图需要改变的时间并不总是相同的。您可以在需要更改位图时刷新表单,但会降低表单所有其他绘制的性能。

于 2012-09-22T17:53:04.160 回答
1

每当您的表单重绘时,您都会运行一个非常昂贵的 O(n2) 算法,并且它没有得到很好的优化。

在我看来,您正在做很多只需要执行一次的冗余工作。您的DrawBitmap1函数生成三个图像之一(生成一个您甚至从未显示的位图,对吗?)。

您还可以将 if 语句从内部循环中剥离出来,然后创建三个版本。无需多次检查相同的条件,因为它在迭代之间没有变化。

您可以创建每个图像一次并将其缓存。如果再次需要该图像,只需直接从函数中返回它。

最重要的是,GDI+ 中的GetPixel()andSetPixel()函数非常慢。我的意思是,真的,真的很慢。.NET 中的任何真实图像处理都可能在不安全的上下文中使用直接指向内存的指针来完成。调用Bitmap.LockBits,它返回一个BitmapData对象,并使用Scan0属性来检索指向内存缓冲区的指针。

于 2012-09-22T18:11:27.810 回答
1
this.Paint += this.Teken;

Teken这是错误的,每次您的应用需要重新绘制时,它都会调用您的例程;当您打开窗口、调整窗口大小、更改焦点、移动另一个窗口、获取工具提示、更改分辨率或主题时,几乎无时无刻不在。实际上,唯一Teken不调用的时间是何时CalculateMandel完成并且您需要显示结果。您从不打电话Invalidate,所以当您需要绘制结果时,您永远不会收到警报。

...CalculateMandel()在通过位图的每个像素后不会停止运行。这怎么可能?

因为您明确告诉它每次 UI 发生任何变化时都重新计算位图中的所有内容;一而再,再而三。除非只有您的位图发生变化,否则您不会告诉它重新绘制。完全落后。这也将锁定您的 UI,因为即使不需要重绘,您也在无用地重新计算位图。

...DrawBitmap1将鼠标悬停在文本框或按钮上时再次调用函数。这怎么可能,我没有任何悬停事件

但还有别的东西;也许是一个工具提示。并且您已经告诉您的应用程序在CalculateMandel()每次应用程序 UI 中的任何内容(位图除外)发生更改时调用。

问题是你有两件完全不同的事情正在发生;当某些事情发生变化时,您的应用程序需要重新绘制 UI,并且您有一个计算 ( CalculateMandel()) 想要在完成后更新 UI。这些是单独的事件,您必须单独处理它们。

首先,摆脱this.Paint += this.Teken;; 这会导致您的问题,并且不会做您想做的事情。

其次,创建一个在其事件BackgroundWorker中调用的事件,并在您的事件中返回完成的位图,然后调用以重绘新数据。CalculateMandel()DoWorkProgressChangedyourWindowOrPanelOrWhatever.Invalidate()

于 2012-09-22T21:00:17.997 回答