Draw Transparent Rectangle WITHOUT OnPaintBackground

No it will not need the onPaintBackground as the control only draws what is within the region and yes the region is made up of the 4 lines and 4 corners that make the shape of your control.
 
Cags, I just got around to trying the Region thing and it is so very very close to what I need. If I werent in a place of business Id be giggling like a schoolgirl.
A few problems though:
1) My app just slowed waaaaaaaaaaaaaaaaay down after doing that.
For reference sake, here is my Paint code for the uctrlPanel control:
Code:
 Dim filler As System.Drawing.Drawing2D.LinearGradientBrush
            Dim rect As System.Drawing.Rectangle = Me.ClientRectangle
            Dim topCorn As Rectangle
            Dim graphPath As System.Drawing.Drawing2D.GraphicsPath

            If memGraphics.CanDoubleBuffer() Then
                If Not Me._HasDrawn Then
                    Me._HasDrawn = True

                     Draw the curved border
                    graphPath = Me.GetPath()
                    If Me.ClientRectangle.Width = 0 Then
                        rect.Width += 1
                    End If

                    If Me.ClientRectangle.Height = 0 Then
                        rect.Height += 1
                    End If

                    If Me._GradientMode = LinearGradientMode.None Then
                        filler = New System.Drawing.Drawing2D.LinearGradientBrush(rect, Me._BackColour1, Me._BackColour1, System.Drawing.Drawing2D.LinearGradientMode.Vertical)
                    Else
                        filler = New System.Drawing.Drawing2D.LinearGradientBrush(rect, Me._BackColour1, Me._BackColour1, CType(Me._GradientMode, System.Drawing.Drawing2D.LinearGradientMode))
                        filler = New System.Drawing.Drawing2D.LinearGradientBrush(rect, Me._BackColour1, Me._BackColour2, CType(Me._GradientMode, System.Drawing.Drawing2D.LinearGradientMode))
                    End If

                    memGraphics.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias

                    memGraphics.Graphics.FillPath(filler, graphPath)

                    Me.Region = New Region(graphPath)

                    Select Case Me._BorderStyle
                        Case System.Windows.Forms.BorderStyle.FixedSingle
                            Dim borderPen As New System.Drawing.Pen(Me._BorderColour, Me._BorderWidth)
                            memGraphics.Graphics.DrawPath(borderPen, graphPath)
                            borderPen.Dispose()

                        Case System.Windows.Forms.BorderStyle.Fixed3D
                            Me.DrawBorder3D(memGraphics.Graphics, Me.ClientRectangle)

                        Case System.Windows.Forms.BorderStyle.None

                    End Select

                     Render to the form
                    memGraphics.Render(e.Graphics)

                    filler.Dispose()
                    graphPath.Dispose()
                End If
            End If

Is there something in there I shouldnt be doing? Something I should do different? I am so new to this whole GDI+ thing that Im learning on the fly (though you have been very helpful so far).

There are a couple other problems right now (not drawing all of the border, not drawing all of the controls, etc.) but Ill go one at a time...for now the speed of the app is a big thing.
 
couple other relevant code pieces:
Code:
Protected Function GetPath() As System.Drawing.Drawing2D.GraphicsPath

            Dim graphPath As New System.Drawing.Drawing2D.GraphicsPath

            If Me._BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D Then
                graphPath.AddRectangle(Me.ClientRectangle)
            Else

                Try

                    Dim curve As Integer = 0

                    Dim rect As System.Drawing.Rectangle = Me.ClientRectangle
                    Dim offset As Integer = 0

                    Select Case Me._BorderStyle

                        Case System.Windows.Forms.BorderStyle.FixedSingle
                            If Me._BorderWidth > 1 Then
                                offset = DoubleToInt(Me.BorderWidth / 2)
                            End If
                            curve = Me.adjustedCurve

                        Case System.Windows.Forms.BorderStyle.Fixed3D

                        Case System.Windows.Forms.BorderStyle.None
                            curve = Me.adjustedCurve

                    End Select

                    If curve = 0 Then
                        graphPath.AddRectangle(rect.Inflate(rect, -offset, -offset))
                    Else

                        Dim rectWidth As Integer = rect.Width - 1 - offset
                        Dim rectHeight As Integer = rect.Height - 1 - offset
                        Dim curveWidth As Integer = 1

                        If (Me._CurveMode And CornerCurveMode.TopRight) <> 0 Then
                            curveWidth = (curve * 2)
                        Else
                            curveWidth = 1
                        End If
                        graphPath.AddArc(rectWidth - curveWidth, offset, curveWidth, curveWidth, 270, 90)

                        If (Me._CurveMode And CornerCurveMode.BottomRight) <> 0 Then
                            curveWidth = (curve * 2)
                        Else
                            curveWidth = 1
                        End If
                        graphPath.AddArc(rectWidth - curveWidth, rectHeight - curveWidth, curveWidth, curveWidth, 0, 90)

                        If (Me._CurveMode And CornerCurveMode.BottomLeft) <> 0 Then
                            curveWidth = (curve * 2)
                        Else
                            curveWidth = 1
                        End If
                        graphPath.AddArc(offset, rectHeight - curveWidth, curveWidth, curveWidth, 90, 90)

                        If (Me._CurveMode And CornerCurveMode.TopLeft) <> 0 Then
                            curveWidth = (curve * 2)
                        Else
                            curveWidth = 1
                        End If
                        graphPath.AddArc(offset, offset, curveWidth, curveWidth, 180, 90)

                        graphPath.CloseFigure()

                    End If

                Catch ex As System.Exception
                    graphPath.AddRectangle(Me.ClientRectangle)
                End Try

            End If

            Return graphPath

        End Function
And
Code:
        Public Sub Render(ByVal g As Graphics)
            If Not Me._memoryBitmap Is Nothing Then
                g.DrawImage(Me._memoryBitmap, New Rectangle(0, 0, Me._width, Me._height), 0, 0, Me._width, Me._height, GraphicsUnit.Pixel)
            End If
        End Sub
 
Well I thought I might reply now, just incase you have chance to look at this before Im available to check more closely tomorrow. If my post doesnt make sense I appologise, I have been drinking.

As Im only looking at sections of your code its hard to tell, but I notice that in your Render method you are attempting to draw a local bitmap to your screen. Does this mean that all drawing is done to a local bitmap and then the paint method simply draws the local bitmap? If this is the case then an application witch uses alot of instances of this control could use a lot of memory. I suppose this might cause some slowdown. I can see no particular reason why the basic code behind drawing a curved rectangle should cause a slowdown in your application. I will look at your code further tomorrow, but if you could give me an approximation of how many of this control may be on screen at once, then that would be very helpfull.

EDIT: I nearly forgot to mention, you say your entire controls doesnt draw correctly, I could be wrong but im guessing by this you mean part of the border is missing. If this is the case it is perhaps because you are setting the region of the control to the size of the border, wheras infact this region should be large enough to entirely encase the border (i.e at least 2 pixel larger in either direction).
 
Last edited by a moderator:
After another look at your code I would also suggest trying to eliminate the Try Statements as they shouldnt be neccessary in this case. If you store the path of the border locally it will prevent you having to call GetPath everytime you paint, instead calling it only when the control is resized.
 
Ill try out the things you mentioned today. Sorry about not replying yesterday...I was out sick.
The reasson Im using that bitmap to draw was because I was attempting to double buffer the control. The normal windows double buffering wasnt working well enough so I had to do it on my own.
As for the amount of this control in my program...well if you look at the images I put above, every single time you see a rounded edge...its this control.
 
also, what I meant by "not drawing all of the controls" is that some of my controls dont draw INSIDE the curves now. Specifically my tabcontrol which uses this panel. It now draws outside the curves just fine (which fixes the origional problem), but doesnt draw inside anymore.
I also have a label control that is just this panel with a label inside of it (so I can have curved labels) and it does the same thing. Draws outside the curves now, but whats inside doesnt draw right. Ill try to get a screenshot or 2.
 
I apologize now if these images are too huge...dont have any graphics programs at work except for MS Paint.

Pay attention to whats inside the main white panel. Using the Region stuff you talked about (I took out the double buffering and it is much faster) it now paints outside the curves...but doesnt paint INSIDE.
[Broken External Image]:http://www.livethislife.net/images/ForWork/nodrawinside.png


Now the odd thing is that as soon as I click the search link, which raises an event...it paints whats inside like this (remember, the ONLY thing Ive done at this point is type "smith" and clicked "Search"...this screenshot was taken while processing for Search was going on):
[Broken External Image]:http://www.livethislife.net/images/ForWork/aftersearch.png

and here is one more example of things not painting. The red borders were added in Paint. If you look at the big one you are seeing a windows panel inside of my custom panel. Inside the windows panel are 2 windows listboxes (along with some labels and things). Those listboxes arent fully painting even though I have no code to override their paint events. If you look at the smaller red borders you will see another issue. Those are my custom panel with a windows label and a windows checkbox inside. The labels seem to be painting just fine, but the checkboxes arent painting their background to match the parents background...which is why you see the white square outside the checkbox.

Cags I cant thank you enough for the help youve given me already...this Region thing is DEFINATLY heading in the right direction...Ive got faster loading and no flicker...now I just need to figure out these painting issues.
[Broken External Image]:http://www.livethislife.net/images/ForWork/inworkflow.png
 
In your OnPaint method how are you drawing the Background of the control are you calling e.Graphics.Clear(this.BackColor); or are you drawing a region/ rectangle?
 
Cags said:
In your OnPaint method how are you drawing the Background of the control are you calling e.Graphics.Clear(this.BackColor); or are you drawing a region/ rectangle?

apparently neither. But the problem with using e.Graphics.Clear(this.Backcolor) is that the backcolor is Color.Transparent and windows transparency doesnt work right unless you dont override OnPaintBackground.
 
I was using Fillpath because the background is a gradient and Fillpath allows the use of a gradient brush whereas e.Graphics.Clear(Me.BackColor) just uses 1 color.

*edit*
right now this is my paint method:
Code:
       graphPath = Me.GetPath()
            If Me.ClientRectangle.Width = 0 Then
                rect.Width += 1
            End If

            If Me.ClientRectangle.Height = 0 Then
                rect.Height += 1
            End If

            If Me._GradientMode = LinearGradientMode.None Then
                filler = New System.Drawing.Drawing2D.LinearGradientBrush(rect, Me._BackColour1, Me._BackColour1, System.Drawing.Drawing2D.LinearGradientMode.Vertical)
            Else
                filler = New System.Drawing.Drawing2D.LinearGradientBrush(rect, Me._BackColour1, Me._BackColour2, CType(Me._GradientMode, System.Drawing.Drawing2D.LinearGradientMode))
            End If

            e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias

            e.Graphics.FillPath(filler, graphPath)

            Me.Region = New Region(graphPath)

OnPaintBackground is overridden and is blank.
 
It will help your program run fast if you call get Path only when you resize / create the control rather than when every time you paint. Todo this create a method called CalcLayout, and add the path calculation to it something like this... (my vb isnt that good)
Code:
    Public graphPath As New System.Drawing.Drawing2D.GraphicsPath

    Private Sub CalcLayout()
        graphPath = Me.GetPath()

        If Me.ClientRectangle.Width = 0 Then
            rect.Width += 1
        End If

        If Me.ClientRectangle.Height = 0 Then
            rect.Height += 1
        End If

        Me.Region = graphPath

        Invalidate()

    End Sub

    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
        MyBase.OnPaint(e)

        If Me._GradientMode = LinearGradientMode.None Then
            filler = New System.Drawing.Drawing2D.LinearGradientBrush(rect, Me._BackColour1, Me._BackColour1, System.Drawing.Drawing2D.LinearGradientMode.Vertical)
        Else
            filler = New System.Drawing.Drawing2D.LinearGradientBrush(rect, Me._BackColour1, Me._BackColour2, CType(Me._GradientMode, System.Drawing.Drawing2D.LinearGradientMode))
        End If

        e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias

        e.Graphics.FillPath(filler, graphPath)
    End Sub

    Protected Overrides Sub OnResize(ByVal e As EventArgs)
        MyBase.OnResize(e)
        CalcLayout()
    End Sub
 
you sir, are very nearly my hero.
:)
what you just posted works 99% perfectly...the only problem is that the gradient background doesnt draw all the way across now.
example:
[Broken External Image]:http://www.livethislife.net/images/ForWork/wackyfun.png

Im going to look into it more, but was hoping you knew why that was happening.

I did however navigate through the app and apart from the gradient and a few other issues Im sure I can solve...this worked perfect.
You english folks always seem to help me out so much. If Im ever in england, youll get a round a drinks on me.
:)
 
nope. Still shows the gradient wrong.
Im betting that somewhere the "rect" object isnt being treated the same. I basically took what you posted and commented out everything I had. So Ill go back and look at whats done to rect in my code.
 
ahh i think its because the rectangle you pass in to the linear brush should be relative to the control not the form so it should be
Code:
new Rectangle(0, 0, this.Width, this.Height)
 
got it. Origionally rect was declared = Me.ClientRectangle. Forgot that part this time around.
so...possibly 1 last question. Earlier you mentioned getting the border into the Region by adding 1 or 2 pixels all around. Should I do that in the graphPath or somewhere else?
 
The way ive been getting a border on my test control is like this, note you will have to modify the GetPath method slightly to accept a Rectangle. With the method Im using Im not entirely sure the border is the right thickness but you can tweak the size of the rectangle you pass in untill this is correct.
Code:
private sub CalcLayout()

	_BorderPath = GetPath(new Rectangle(1, 1, this.Bounds.Width, this.Bounds.Height))
	Me.Region = new Region(GetPath(this.Bounds))
	Invalidate();
end sub

then in the OnPaint method you call DrawPath(myPen, _BorderPath);

EDIT: You also nead to remember that you will have to call CalcLayout whenever the border thickness is changed, and Invalidate whenever its colour is changed etc.
 
Im not exactly following what you do with the rectangle that you pass into your GetPath method.
are you just using it in place of the Method level "rect" that is declared in my GetPath method (do you still have that code? I thought I posted it a couple pages back).
 
Back
Top