C#:Windows窗体:是什么导致Invalidate()无法重绘? [英] C#: Windows Forms: What could cause Invalidate() to not redraw?
问题描述
我正在使用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
,并且某些野外控件以错误的实现覆盖了此方法.)>
有一种方法,它通过发出 Called in my code to draw the world appropriately: View.DrawWorldBox: 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 Eventually, Windows will see that some controls need repainting and issue There is one method that does force an immediate paint by issuing a 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 这篇关于C#:Windows窗体:是什么导致Invalidate()无法重绘?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! WM_PAINT
消息来强制即时绘画,即view.DrawWorldBox(worldBox, canvas, gameEngine.GameObjectManager.Controllers,
selectedGameObjects, LevelEditorUtils.PREVIEWS);
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();
}
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.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.)WM_PAINT
message, and that is Control.Update
. So if you want to force an immediate redraw, you use:control.Invalidate();
control.Update();
SendMessage
API instead of PostMessage
which forces painting to be done synchronously instead of tossing it at the end of a long message queue.