VB.NET Custom Control(自定义绘图)刷新问题

发布于 2024-07-13 07:36:05 字数 1766 浏览 8 评论 0原文

我用两个项目创建了一个简单的解决方案。 第一个项目(类库)包含一个名为 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

冷心人i 2024-07-20 07:36:05

这是你的问题:

Dim r As Rectangle = e.ClipRectangle

将其更改为:

Dim r As Rectangle = Me.ClientRectangle

This is your problem:

Dim r As Rectangle = e.ClipRectangle

Change it to:

Dim r As Rectangle = Me.ClientRectangle
桃酥萝莉 2024-07-20 07:36:05

在我看来,您的 Container 类并未绘制其整个区域 - 通常,控件负责绘制其整个矩形。

为了拥有不执行此操作的控件(具有透明区域(如圆角)),您需要为控件提供 WS_EX_TRANSPARENT 属性。 请注意,这是一个 Windows API 主题,而不是 .NET 主题,因此您正在走向一些小巫术的方向。

虽然 CodeProject 文章是用 C# 编写的,但它使用 C# 和 .NET 3.5 创建透明控件 似乎确实与您想要实现的目标直接相关。

引用那篇文章,首先需要重写 UserControl 的构造函数并配置背景:

public TranspControl()
{
     SetStyle(ControlStyles.SupportsTransparentBackColor, true);
     SetStyle(ControlStyles.Opaque, true);
     this.BackColor = Color.Transparent;
}

然后,需要重写 CreateParams() 方法来设置控件样式 WS_EX_TRANSPARENT

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x20;
        return cp;
    }
}

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:

public TranspControl()
{
     SetStyle(ControlStyles.SupportsTransparentBackColor, true);
     SetStyle(ControlStyles.Opaque, true);
     this.BackColor = Color.Transparent;
}

Then, you need to override the CreateParams() method to set the control style WS_EX_TRANSPARENT:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x20;
        return cp;
    }
}
半透明的墙 2024-07-20 07:36:05

没有必要在此处强制重绘(在正常情况下),因为一旦您的控件被轻推,就会自动强制重绘。

但是,您需要做的是在绘制其他任何内容之前清除控件的背景:否则,您的绘制操作将与以前的绘制过程混合在一起。 添加一个即可

e.Graphics.Clear(BackColor)

只需在 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

e.Graphics.Clear(BackColor)

before your other drawing operations in the Paint event handler. Also, consider using the OnPaint method rather than the Paint event since you subclass the control and don't need to resort to the Paint event handler.

For the record, Refresh forces a synchronous redraw which is usually not desired. Rather, use Invalidate which enqueues the redraw request into the default window message queue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文