C# · 12月 25, 2021

c# – Bug或我做错了什么? Usercontrol绘画

private void UserControl1_Paint(object sender,PaintEventArgs e){ e.Graphics.DrawEllipse(Pens.Black,new Rectangle(-200,-500,this.Width + 400,this.Height + 420));}

将上面的代码粘贴到usercontrol中.将usercontrol放到表单上并将其锚定到所有4个点.

在设计器(在Visual Studio 2010下),它呈现完美(即使您调整大小).运行它并尝试调整窗体大小,椭圆变得倾斜.

以下是运行时第一个调整大小后的两个示例,设计器中的第二个调整大小.

显然,设计师的行为并不总是被认为是相同的(虽然它会很好)但我的理解是上面的代码是完全合法的.我错了吗?

解决方法 Stecya has posted“修复”,但省略了一个解释,详细说明了为什么有效,或者为什么你看到设计师和正在运行的应用程序中的行为之间存在差异.在我看来,这使答案只有最低限度的用处,所以我想我会试图解释一下.

您已经知道调用Invalidate method是解决方案.这样做是告诉Windows,下次窗口被绘制时需要重新绘制控件的整个表面区域.很简单,但你为什么不在设计师那里做到这一点?

答案在于您在启用Windows Aero主题的情况下运行程序. Aero使用一个全新的窗口管理器,基于组合称为Desktop Window Manager(或简称DWM).每个窗口都是双缓冲的,这意味着它的图形会在屏幕外呈现为临时位图,然后才会显示在屏幕上.这允许用户现在喜欢的各种酷炫效果和花哨的过渡.

但是,当然,这意味着已经绘制的部分不会被删除和重绘,除非您明确指示Windows需要这样做.这对于设计器内部的表单来说不是问题,因为在那里,Aero的DWM组合未启用.当窗口调整大小时,它会自动重绘,你的旋风看起来很平滑和正确.

在设计器之外,启用Aero合成后,只会重新绘制控件的新曝光部分(其余部分仍在缓冲区中),因此形状错误.旧形状的一部分仍然存在,并且刚刚绘制了新形状的一部分.调用Invalidate告诉Windows“此控件的图形表面已更改;忘记您认为已知的所有内容并在下次从头开始重绘” .因此,Windows尽职尽责地从其屏幕外缓冲区中丢弃该部分并从头开始重绘,从而生成正确渲染,平滑的路径.

您可以以另一种更优雅的方式实现相同的更改:告诉控件每次调整大小时都需要重绘自己.将以下代码插入控件的构造方法中:

public MyUserControl(){ // Force the control to redraw itself each time it is resized this.SetStyle(ControlStyles.ResizeRedraw);}