C# · 12月 31, 2021

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

我正在使用 Windows窗体.很长一段时间,picture@R_21_2419@.Invalidate();努力使屏幕重绘.但是,现在不行,我不知道为什么. this.world@R_21_2419@ = new System.Windows.Forms.Picture@R_21_2419@();this.world@R_21_2419@.BackColor = System.Drawing.SystemColors.Control;this.world@R_21_2419@.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;this.world@R_21_2419@.Location = new System.Drawing.Point(170,82);this.world@R_21_2419@.Name = “world@R_21_2419@”;this.world@R_21_2419@.Size = new System.Drawing.Size(261,250);this.world@R_21_2419@.TabIndex = 0;this.world@R_21_2419@.TabStop = false;this.world@R_21_2419@.MouseMove += new System.Windows.Forms.MouseEventHandler(this.world@R_21_2419@_MouseMove);this.world@R_21_2419@.MouseDown += new System.Windows.Forms.MouseEventHandler(this.world@R_21_2419@_MouseDown);this.world@R_21_2419@.MouseUp += new System.Windows.Forms.MouseEventHandler(this.world@R_21_2419@_MouseUp);

在我的代码中被称为适当地画出世界:

view.DrawWorld@R_21_2419@(world@R_21_2419@,canvas,gameEngine.GameObjectManager.Controllers,selectedGameObjects,LevelEditorUtils.PREVIEWS);

View.DrawWorld@R_21_2419@:

public void DrawWorld@R_21_2419@(Picture@R_21_2419@ world@R_21_2419@,Panel canvas,ICollection<IGameObjectController> controllers,ICollection<IGameObjectController> selectedGameObjects,IDictionary<string,Image> previews){ int left = Math.Abs(world@R_21_2419@.Location.X); int top = Math.Abs(world@R_21_2419@.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(world@R_21_2419@.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(); } world@R_21_2419@.Invalidate();}

在这里我可以做错什么

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

Windows控件是为响应WM_PAINT消息而绘制的.当他们收到这个消息时,他们绘制自己的哪一部分已经被无效.特定控制可以被无效,并且控制的特定区域可以被无效,这一切都是为了尽量减少重新绘制的量.

最终,Windows会看到一些控件需要重画,并向他们发出WM_PAINT消息.但是,只有在处理完所有其他消息后才能执行此操作,这意味着Invalidate不会强制立即重绘.在技​​术上刷新应该,但并不总是可靠的. (更新:这是因为刷新是虚拟的,并且有一些控件在野外覆盖此方法与不正确的实现.)

有一种方法通过发出WM_PAINT消息强制立即绘制,即Control.Update.所以如果要强制立即重画,您可以使用:

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

即使UI仍在处理消息,这将始终重绘控件,无论发生了什么.从字面上来说,我相信它使用SendMessage API而不是PostMessage,它强制绘画同步完成,而不是在长消息队列的末尾将它们折腾.