VB.NET Custom Control (custom drawing) Refresh issue

11,338

Solution 1

This is your problem:

Dim r As Rectangle = e.ClipRectangle

Change it to:

Dim r As Rectangle = Me.ClientRectangle

Solution 2

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;
    }
}

Solution 3

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.

Share:
11,338
Ropstah
Author by

Ropstah

Updated on June 14, 2022

Comments

  • Ropstah
    Ropstah almost 2 years

    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