C#:Windows窗体:是什么导致Invalidate()无法重绘? [英] C#: Windows Forms: What could cause Invalidate() to not redraw?

查看:68
本文介绍了C#:Windows窗体:是什么导致Invalidate()无法重绘?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用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 =新的System.Drawing.Point(170,82);this.worldBox.Name ="worldBox";this.worldBox.Size =新的System.Drawing.Size(261,250);this.worldBox.TabIndex = 0;this.worldBox.TabStop = false;this.worldBox.MouseMove + =新System.Windows.Forms.MouseEventHandler(this.worldBox_MouseMove);this.worldBox.MouseDown + =新System.Windows.Forms.MouseEventHandler(this.worldBox_MouseDown);this.worldBox.MouseUp + =新System.Windows.Forms.MouseEventHandler(this.worldBox_MouseUp); 

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

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

View.DrawWorldBox:

  public void DrawWorldBox(PictureBox worldBox,面板帆布ICollection< IGameObjectController>控制器,ICollection< IGameObjectController>selectedGameObjects,IDictionary< string,Image>预览){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>>层数=LevelEditorUtils.LayersOfControllers(controllers);IOrderedEnumerable< KeyValuePair< float,ICollection< IGameObjectController>>>分类层=来自层中的项目orderby项.键降序选择物品;使用(Graphics g = Graphics.FromImage(worldBox.Image)){foreach(KeyValuePair< float,ICollection< IGameObjectController>>kv in sortedLayers){foreach(kv.Value中的IGameObjectController控制器){//...浮动比例= controller.View.Scale;浮动宽度= controller.View.Width;浮点高度= controller.View.Height;矩形controllerRect =新矩形((int)controller.Model.Position.X,(int)controller.Model.Position.Y,(int)(宽度*比例),(int)(高度*比例));//剔除与画布不相交的对象如果(controllerRect.IntersectsWith(screenRect)){图片img =预览[controller.Model.HumanReadableName];g.DrawImage(img,controllerRect);}如果(selectedGameObjects.Contains(controller)){selectionRectangles.Add(controllerRect);}}}foreach(selectionRectangles中的rect矩形){g.DrawRectangle(drawingPen,rect);}selectionRectangles.Clear();}worldBox.Invalidate();} 

我在这里怎么可能做错了?

解决方案

要了解这一点,您必须对它在操作系统级别上的工作方式有所了解.

绘制

Windows控件是为了响应 WM_PAINT 消息.当他们收到此消息时,他们会画出自己无效的任何部分.可以使特定的控件无效,并且可以使特定的控件区域无效,这都是为了最大程度地减少已完成的重新绘制次数.

最终,Windows将看到某些控件需要重新粉刷并向其发出 WM_PAINT 消息.但这仅在处理完所有其他消息之后才执行此操作,这意味着 Invalidate 不会强制立即重绘.刷新从技术上来说应该是 ,但并不总是可靠的.(更新:这是因为 Refresh virtual ,并且某些野外控件以错误的实现覆盖了此方法.)

有一种方法,它通过发出 WM_PAINT 消息来强制即时绘画,即

Called in my code to draw the world appropriately:

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();
}

What could I be doing wrong here?

To understand this you have to have some understanding of the way this works at the OS level.

Windows controls are drawn in response to a WM_PAINT message. When they receive this message, they draw whichever part of themselves has been invalidated. Specific controls can be invalidated, and specific regions of controls can be invalidated, this is all done to minimize the amount of repainting that's done.

Eventually, Windows will see that some controls need repainting and issue WM_PAINT messages to them. But it only does this after all other messages have been processed, which means that Invalidate does not force an immediate redraw. Refresh technically should, but isn't always reliable. (UPDATE: This is because Refresh is virtual and there are certain controls in the wild that override this method with an incorrect implementation.)

There is one method that does force an immediate paint by issuing a WM_PAINT message, and that is Control.Update. So if you want to force an immediate redraw, you use:

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

This will always redraw the control, no matter what else is happening, even if the UI is still processing messages. Literally, I believe it uses the SendMessage API instead of PostMessage which forces painting to be done synchronously instead of tossing it at the end of a long message queue.

这篇关于C#:Windows窗体:是什么导致Invalidate()无法重绘?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆