VB.NET Custom Control(自定义绘图)刷新问题
我用两个项目创建了一个简单的解决方案。 第一个项目(类库)包含一个名为 Container 的自定义控件,它用圆角绘制自身。 第二个项目(Windows 窗体)是一个测试应用程序。
如果我在第二个项目的主窗体中添加一个容器实例,它会很好地显示圆角。 另外,当我运行第二个项目时,我可以看到容器。
然而,当我开始移动表单(单击并按住标题栏)时,尤其是当我移动得非常快时,所有绘图都变得混乱,一遍又一遍地绘制,但没有先清除它的表面...
我可以调用 Container1. Form1.Move 事件中的 Refresh(),但我不想每次都设置它,因为这也意味着我必须在 Form1.Resize
事件中调用 Container1.Refresh(),谁知道呢还有哪个事件...
Container(控件)类本身是否有一个事件,我应该在其中调用 Me.Refresh() 或 Me.Update() 或 Me.Invalidate() ?
供参考(Form1.vb)
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub Form1_Move(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Move
Me.Container1.Refresh()
End Sub
End Class
供参考(Container.vb):
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Public Class Container : Inherits Control
Private _Gp As GraphicsPath
Private Sub Container_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim r As Rectangle = e.ClipRectangle
Dim gp As New GraphicsPath
Dim cs As Integer = 25 'CornerSize'
r.Inflate(-5, -5)
gp.AddArc(r.X, r.Y, cs, cs, 180, 90)
gp.AddArc(r.X + r.Width - cs, r.Y, cs, cs, 270, 90)
gp.AddArc(r.X + r.Width - cs, r.Y + r.Height - cs, cs, cs, 0, 90)
gp.AddArc(r.X, r.Y + r.Height - cs, cs, cs, 90, 90)
Dim t As Single = cs / 2 + r.Y
gp.AddLine(r.X, r.Y + r.Height - cs, r.X, t)
e.Graphics.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias
e.Graphics.DrawPath(Pens.Black, gp)
End Sub
End Class
I've created a simple solution with 2 projects. The 1st project (class library) contains a custom control called Container which draws itself with rounded corners. The 2nd project (windows forms) is a test application.
If I add a Container instance to main Form in the 2nd project it shows the rounded corners nicely. Also when I run the 2nd project I can see the Container.
However when I start moving the form (click and hold the title bar), especially when I move it very fast, all the drawing is messed up, drawn over and over again but not clearing it's surface first...
I can call Container1.Refresh() in the Form1.Move event, but I don't want to set this every time because this also means I have to call Container1.Refresh() in the Form1.Resize
event and who knows which other event...
Is there an event in the Container (control) class itself where I should call Me.Refresh() or Me.Update() or Me.Invalidate() ?
For reference (Form1.vb)
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub Form1_Move(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Move
Me.Container1.Refresh()
End Sub
End Class
for reference (Container.vb):
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Public Class Container : Inherits Control
Private _Gp As GraphicsPath
Private Sub Container_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim r As Rectangle = e.ClipRectangle
Dim gp As New GraphicsPath
Dim cs As Integer = 25 'CornerSize'
r.Inflate(-5, -5)
gp.AddArc(r.X, r.Y, cs, cs, 180, 90)
gp.AddArc(r.X + r.Width - cs, r.Y, cs, cs, 270, 90)
gp.AddArc(r.X + r.Width - cs, r.Y + r.Height - cs, cs, cs, 0, 90)
gp.AddArc(r.X, r.Y + r.Height - cs, cs, cs, 90, 90)
Dim t As Single = cs / 2 + r.Y
gp.AddLine(r.X, r.Y + r.Height - cs, r.X, t)
e.Graphics.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias
e.Graphics.DrawPath(Pens.Black, gp)
End Sub
End Class
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是你的问题:
将其更改为:
This is your problem:
Change it to:
在我看来,您的 Container 类并未绘制其整个区域 - 通常,控件负责绘制其整个矩形。
为了拥有不执行此操作的控件(具有透明区域(如圆角)),您需要为控件提供
WS_EX_TRANSPARENT
属性。 请注意,这是一个 Windows API 主题,而不是 .NET 主题,因此您正在走向一些小巫术的方向。虽然 CodeProject 文章是用 C# 编写的,但它使用 C# 和 .NET 3.5 创建透明控件 似乎确实与您想要实现的目标直接相关。
引用那篇文章,首先需要重写 UserControl 的构造函数并配置背景:
然后,需要重写
CreateParams()
方法来设置控件样式WS_EX_TRANSPARENT
:It looks to me as though your
Container
class isn't painting its entire area - normally a control is responsible for painting its entire rectangle.In order to have a control that doesn't do this - that has transparent areas (like your rounded corners) - you need to give your control the
WS_EX_TRANSPARENT
property. Note that this is a Windows API subject, not a .NET one, so you're heading in the direction of some minor voodoo.While it's written in C#, the CodeProject article Making Transparent Controls with C# and .NET 3.5 does seem directly relevant to what you're trying to achieve.
To quote that article, you first need to override the constructor of your UserControl and configure the background:
Then, you need to override the
CreateParams()
method to set the control styleWS_EX_TRANSPARENT
:没有必要在此处强制重绘(在正常情况下),因为一旦您的控件被轻推,就会自动强制重绘。
但是,您需要做的是在绘制其他任何内容之前清除控件的背景:否则,您的绘制操作将与以前的绘制过程混合在一起。 添加一个即可
只需在
Paint
事件处理程序中的其他绘图操作之前 。 另外,请考虑使用OnPaint
方法而不是Paint
事件,因为您是控件的子类,并且不需要诉诸Paint
事件处理程序。根据记录,
Refresh
强制同步重绘,这通常是不需要的。 相反,使用Invalidate
将重绘请求排入默认窗口消息队列。It shouldn't be necessary to force a redraw here (under normal circumstances) since that redraw is automatically forced as soon as your control gets nudged.
However, what you need to do is clearing the background of your control before painting anything else: otherwise, your painting operation will mingle with previous painting processes. Just add an
before your other drawing operations in the
Paint
event handler. Also, consider using theOnPaint
method rather than thePaint
event since you subclass the control and don't need to resort to thePaint
event handler.For the record,
Refresh
forces a synchronous redraw which is usually not desired. Rather, useInvalidate
which enqueues the redraw request into the default window message queue.