C#:Windows窗体:什么可能导致Invalidate()不重绘?

时间:2010-04-23 01:22:34

标签: c# winforms

我正在使用Windows窗体。很长一段时间,pictureBox.Invalidate();努力重新绘制屏幕。但是,它现在不起作用,我不知道为什么。

this.worldBox = new System.Windows.Forms.PictureBox();
this.worldBox.BackColor = System.Drawing.SystemColors.Control;
this.worldBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.worldBox.Location = new System.Drawing.Point(170, 82);
this.worldBox.Name = "worldBox";
this.worldBox.Size = new System.Drawing.Size(261, 250);
this.worldBox.TabIndex = 0;
this.worldBox.TabStop = false;
this.worldBox.MouseMove += new 
    System.Windows.Forms.MouseEventHandler(this.worldBox_MouseMove);
this.worldBox.MouseDown += new 
    System.Windows.Forms.MouseEventHandler(this.worldBox_MouseDown);
this.worldBox.MouseUp += new 
    System.Windows.Forms.MouseEventHandler(this.worldBox_MouseUp);

在我的代码中调用以适当地绘制世界:

view.DrawWorldBox(worldBox, canvas, gameEngine.GameObjectManager.Controllers, 
    selectedGameObjects, LevelEditorUtils.PREVIEWS);

View.DrawWorldBox:

public void DrawWorldBox(PictureBox worldBox,
    Panel canvas,
    ICollection<IGameObjectController> controllers,
    ICollection<IGameObjectController> selectedGameObjects,
    IDictionary<string, Image> previews)
{
    int left = Math.Abs(worldBox.Location.X);
    int top = Math.Abs(worldBox.Location.Y);
    Rectangle screenRect = new Rectangle(left, top, canvas.Width, 
        canvas.Height);

    IDictionary<float, ICollection<IGameObjectController>> layers = 
        LevelEditorUtils.LayersOfControllers(controllers);
    IOrderedEnumerable<KeyValuePair<float, 
        ICollection<IGameObjectController>>> sortedLayers 
            = from item in layers
              orderby item.Key descending
              select item;

    using (Graphics g = Graphics.FromImage(worldBox.Image))
    {
        foreach (KeyValuePair<float, ICollection<IGameObjectController>> 
        kv in sortedLayers)
        {
            foreach (IGameObjectController controller in kv.Value)
            {
                // ...

                float scale = controller.View.Scale;
                float width = controller.View.Width;
                float height = controller.View.Height;
                Rectangle controllerRect = new 
                    Rectangle((int)controller.Model.Position.X,
                    (int)controller.Model.Position.Y,
                    (int)(width * scale),
                    (int)(height * scale));

                // cull objects that aren't intersecting with the canvas
                if (controllerRect.IntersectsWith(screenRect))
                {
                    Image img = previews[controller.Model.HumanReadableName];
                    g.DrawImage(img, controllerRect);
                }

                if (selectedGameObjects.Contains(controller))
                {
                    selectionRectangles.Add(controllerRect);
                }
            }
        }
        foreach (Rectangle rect in selectionRectangles)
        {
            g.DrawRectangle(drawingPen, rect);
        }
        selectionRectangles.Clear();
    }
    worldBox.Invalidate();
}

我在这里做错了什么?

3 个答案:

答案 0 :(得分:19)

要理解这一点,你必须要了解它在操作系统级别的工作方式。

绘制Windows控件以响应WM_PAINT消息。当他们收到此消息时,他们会绘制自己无效的部分。特定控件可能无效,控件的特定区域可能无效,这样做是为了最大限度地减少重新绘制的数量。

最终,Windows会看到某些控件需要重新绘制并向其发出WM_PAINT条消息。但它只在处理完所有其他消息后执行此操作,这意味着Invalidate不会强制立即重绘。 Refresh技术上应该,但并不总是可靠的。 (更新:这是因为Refreshvirtual并且在野外存在某些控件,这些控件使用不正确的实现覆盖此方法。)

有一种方法 通过发出WM_PAINT消息强制立即绘制,即Control.Update。因此,如果您想强制立即重绘,请使用:

control.Invalidate();
control.Update();

即使用户界面仍处理消息,无论发生什么事情,都会重新绘制控件。从字面上看,我认为它使用SendMessage API代替PostMessage,这迫使绘画同步完成,而不是在长消息队列的末尾抛出它。

答案 1 :(得分:1)

Invalidate()仅“使控件或表单无效”(将其标记为重新绘制),但不强制重绘。当应用程序在消息队列中没有要处理的消息时再次重新绘制时,它将重新绘制。如果要强制重新绘制,可以使用Refresh()

答案 2 :(得分:0)

在这种情况下,

InvalidateRefresh将执行相同的操作,并强制重绘(最终)。如果您没有看到任何重绘(永远),那么这意味着在DrawWorldBox中根本没有绘制任何内容,或者从PictureBox图像的可见部分绘制的任何内容都被绘制出来。

确保(正在使用断点或记录或单步执行代码)某些正被添加到selectionRectangles,并且至少有一个矩形覆盖PictureBox的可见部分。还要确保您正在绘制的笔与背景颜色不一样。