繁体   English   中英

Invalidate()将东西留在屏幕上

[英]Invalidate() leaves things on the screen

我对invalidate的理解是,它向区域添加了一个标志,表明该区域需要更新,然后当无其他事情可做时,该区域将被更新。 发生这种情况时,该区域中的所有内容都将被删除,然后由订户重新提交到paint-event,这是在forms-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);
                        }

                    }
                }
            }
        }
    }

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

这可能是产生此类问题的最大误解。 您无法“删除”屏幕上的像素。 它总是在那里并且具有特定的颜色。 如果颜色错误,则您的工作就是再给它一个。

Windows中的绘制周期如下:

  • 只要窗口的内容过期,就需要对其进行绘制。 这是通过调用Invalidate(),您的情况或窗口管理器可以自行解决的,例如将窗口拖出屏幕并返回或用户调整窗口大小来明确完成的。 在Aero之前的旧Windows版本上,当另一个窗口在您的整个窗口上移动时也是如此。

  • 操作系统要做的第一件事是为窗口创建“设备上下文”,它将您绘制的所有内容定向到窗口表面。 Graphics类是设备上下文的.NET包装器。 此时,您应该“ u​​h-oh”,该设备上下文当然与您之前创建并存储在speelbordGraphics变量中的设备上下文不匹配。 特别是,其ClipBounds属性可能是错误的,这种错误会在窗口大小更改时发生。

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

  • 如果将DoubleBuffered属性设置为true,则Winforms将创建另一个设备上下文,它将油漆引导到内存中的位图,因此绘制的所有内容都不会直接进入屏幕,而是直接进入位图。 它有助于抑制可见的绘画伪影,通常称为“闪烁”。 您会为游戏板真正喜欢的东西。 您应该再次在这里惊叹“哦,哦”,通过speelbordGraphics绘制的speelbordGraphics会进入屏幕表面,而不是位图。

  • 接下来,运行表单的OnPaintBackground()方法。 如果未覆盖它,则使用Graphics.FillRectangle()将剪辑矩形中的像素设置为BackColor颜色。 这与“该区域的所有内容均被删除”最接近。 继续使用“ uh-oh”的另一个原因是,您的speelbordGraphics允许您直接将像素猛击到屏幕上,而无需绘制背景。 这样做的副作用是您不会看到像素被设置回预期的BackColor。 看起来他们没有被“擦除”。 与您观察到的结果非常匹配。

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

  • 如果DoubleBuffered属性设置为true,则Winforms现在将使用快速的bitblt将位图复制到屏幕表面。 这里的另一个“呃-哦”​​,覆盖了您通过speelbordGraphics绘制的speelbordGraphics 这很容易注意到,您会在短短的一秒钟内看到绘制的内容,看起来就像是大量闪烁的情况。 与DoubleBuffered应该完成的事情完全相反:)

收集前面的项目符号中的所有“呃”,它们都有一个单独的原因。 它们都是由使用speelbordGraphics引起的。 您称它为“方便”,但一点也不方便,它会导致错误。 更糟糕的是,它效率低下 设备上下文的创建非常便宜,不到一微秒,但保持环境昂贵。 无论如何,创建在项目符号中提到的那些设备上下文,您的变量不会对其进行优化。 而且它会占用同一台式机上所有进程从中分配的堆中的内存。 堆大小是有限的,出于应用程序兼容性原因,它只能存储65535个图形对象。

因此,修复非常简单,只需删除变量即可。 现在没有办法弄错了。 编译器会告诉您您在哪里做错了,它将迫使您在Paint事件处理程序中使用从e.Graphics获取的Graphics对象。 您将不可避免地发现并修复您的错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM