views:

3343

answers:

3

Hi,

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 titlebar), especially when I move it very fastly, 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 everytime 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
+1  A: 

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.

Konrad Rudolph
calling Graphics.Clear still doesn't really do the trick...This is what happens: http://img300.imageshack.us/my.php?image=drawmessjp0.jpg
Ropstah
ropstah: in your picture the background isn't cleared. Have you *recompiled* the project after adding the clearing code? It should look different then. However, recompiling is necessary for the IDE to take notice of the changes!
Konrad Rudolph
Yes I have recompiled, the problem persists.....
Ropstah
+3  A: 

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;
    }
}
Bevan
+3  A: 

This is your problem:

Dim r As Rectangle = e.ClipRectangle

Change it to:

Dim r As Rectangle = Me.ClientRectangle
Hans Passant
Hey, I got an up-vote. Awesome.
Hans Passant