Invalidate()会在屏幕上显示内容

时间:2015-10-12 11:04:59

标签: c# winforms invalidation

我对无效的理解是,它向区域添加了一个标记,表示该区域需要更新,然后当没有别的事情要做时,该区域会更新。 当发生这种情况时,区域中的所有内容都会被删除,然后由订阅者重新绘制到paint-event,如form-class的构造函数中所定义。

但是当我点击我的程序的逆转板(一个Windows窗体应用程序)时,逆转件被绘制在我点击的位置,但是什么是有效移动(由DrawEllipse绘制)的指示器保留在屏幕上,甚至尽管它们是以相同的方法绘制的,但是当我将窗口拖离屏幕并返回时,有效的移动指示符会消失。

我做错了什么?

为了完整性,我的代码在这里(最重要的评论是英文的,一些评论仍然是荷兰语):

 void tekenGrid(object obj, PaintEventArgs pea)
        {
        int vakjeBreedte = speelbordPanel.Width / Math.Max(r, k);
        int speelstukDiameter     = vakjeBreedte - 6;

        Pen mijnPen        = new Pen(Color.Black, 2);
        Graphics gr = speelbordGraphics; //we use the graphics object specifically made for the playing board
        gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;    

        for (int rij = 0; rij < r; rij++)             //lets make the grid, in this for-loop

        {
            for (int kolom = 0; kolom < k; kolom++)
            {
                {
                    int x = vakjeBreedte * kolom + 3;   //x,y coordinaat van de vierhoek waar de cirkel in zit
                    int y = vakjeBreedte * rij + 3;

                    gr.DrawRectangle(mijnPen,         vakjeBreedte * kolom, vakjeBreedte * rij, vakjeBreedte, vakjeBreedte);                  //gives the playing board black rectangles around every position

                    if (speelbord[kolom, rij] == speelstuk.Blauw)     //draw the blue pieces              
                    {
                        gr.FillEllipse(Brushes.Blue, x, y, speelstukDiameter, speelstukDiameter);     
                    }

                    else if (speelbord[kolom, rij] == speelstuk.Rood) //draw the red pieces
                    {
                        gr.FillEllipse(Brushes.Red,  x, y, speelstukDiameter, speelstukDiameter);
                    }

                    if (laatHintsZien == true) //the legal move function indicator is here
                    {
                        if (checkLegaalheid(kolom, rij))
                        {
                            gr.DrawEllipse(mijnPen, x, y, speelstukDiameter, speelstukDiameter);
                        }

                    }
                }
            }
        }
    }

1 个答案:

答案 0 :(得分:4)

  

该地区的所有内容都将被删除

这可能是产生这类问题的最大误解。您无法“删除”屏幕上的像素。它始终存在并具有特定的颜色。如果它有错误的颜色,那么你的工作是给它另一个。

Windows中的绘制周期如下:

  • 只要内容过期,您的窗口就需要进行绘制。这是通过调用Invalidate(),你的情况,或者当窗口管理器可以自己解决它来显式完成的,例如当你将窗口拖出屏幕后退回或当用户调整窗口大小时。在Aero之前的旧Windows版本上,当另一个窗口移动到您的Aero之外时。

  • 操作系统首先要为窗口创建一个“设备上下文”,它会将您绘制的任何内容指向窗口表面。 Graphics类是设备上下文的.NET包装器。此时你应该去“呃哦”,当然设备上下文与你之前创建并存储在speelbordGraphics变量中的设备上下文不匹配。特别是,它的ClipBounds属性可能是错误的,当窗口大小发生变化时会发生这种情况。

  • 接下来,它确定需要重新绘制窗口的哪个部分,由PaintEventArgs.ClipRectangle属性公开。你应该在这里再次“呃哦”。您在该剪辑矩形之外绘制的任何内容实际上都不会使其进入窗口表面。这是一种优化,无法在您的代码中使用。

  • 如果将DoubleBuffered属性设置为 true ,则Winforms会创建另一个设备上下文,它会将绘图指向内存中的位图,因此您绘制的所有内容都不会直接显示在屏幕上到位图。它有助于抑制可见的绘画瑕疵,通常称为“闪烁”。你真的会喜欢你的游戏板。你应该在这里再次惊呼“呃哦”,你通过speelbordGraphics绘制的内容是屏幕表面,而不是位图。

  • 接下来,运行表单的OnPaintBackground()方法。如果您没有覆盖它,则剪辑矩形中的像素将使用Graphics.FillRectangle()设置为BackColor颜色。这是与“该地区的一切都被删除”最接近的类比。 “呃哦”的另一个原因是,你的speelbordGraphics允许你直接将像素砸到屏幕上但没有绘制背景。这样做的副作用是您不会按预期看到像素被设置回BackColor。看起来他们没有被“抹去”。与您观察到的内容完全匹配。

  • 接下来,运行表单的OnPaint()方法。它会触发Paint事件,毫无疑问,tekenGrid方法会运行。

  • 如果DoubleBuffered属性设置为 true ,则Winforms现在使用快速bitblt将位图复制到屏幕表面。这里的另一个“呃哦”,它覆盖了你通过speelbordGraphics绘制的任何东西。这很容易注意到,你会看到你在几分之一秒内画出的东西,看起来像一个巨大的闪烁情况。与DoubleBuffered应该完成的完全相反:)

收集以前子弹中的所有“呃哦”,它们都有一个原因。它们都是使用speelbordGraphics引起的。你称之为'方便',但它根本不方便,它会导致错误。更糟糕的是,效率低下。设备上下文的创建非常便宜,远小于一微秒,但保持昂贵。无论如何,子弹中提到的那些设备上下文都会被创建,你的变量不会优化它们。并且它占用来自堆的内存,同一桌面上的所有进程都从中分配。堆大小有限,出于appcompat的原因,它只能存储65535个图形对象。

所以修复非常简单,只需删除变量即可。现在没有办法弄错。编译器会告诉你错误的位置,它会强制你使用你在Paint事件处理程序中从e.Graphics获得的Graphics对象。你将不可避免地发现并修复你的错误。