Results 1 to 3 of 3

Thread: [RESOLVED] GDI and Paint Event Handling Issues.

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Jun 2008
    Location
    Portland, OR, USA
    Posts
    659

    Resolved [RESOLVED] GDI and Paint Event Handling Issues.

    From jmchillihiney's most excellent code bank sumission on Drawing:


    The way GDI+ works, controls get repainted on screen over and over again, sometimes many times in quick succession. On each of these occasions the control's Paint event is raised. You need to use the Paint event of the control to perform your own drawing or else, the next time the control is repainted, your drawing will just disappear as the control paints over it.

    When you're creating a custom control you should override the OnPaint method and perform your drawing there. When you're designing a form or user control and you want to add custom drawing to a child control you handle that control's Paint event, then perform your drawing in the event handler.

    You may have seen, or heard people talk about, controls flickering on screen. That's caused by repainting large areas of the control repeatedly and happens because painting on screen is actually quite a slow process. As such, you should try to do as little of it as possible.
    So, I am attempting to draw part of a custom control. I have placed a SplitContainer on a UserControl, and oriented it vertically. I then shrunk to top panel to 15 pixels in height.

    I have two Methods which each draw a different type of Path + fill into the panel, and then draw a border around it (One is a gradient fill, and the other is just a plain color).

    The idea is that when the user clicks on this panel, it switches from one to the other (a "Selected State" and "Not-Selected" state).

    I have also created an icon which looks like the +/- widget on a treeview (or more specifically, on the vs2008 Toolbox, which is essentially what I am trying to emulate here . . .) which toggles as the control is selected/unselected. Not that ultimately, the selected state will change when the user selects OTHER controls, but the Expand/Collapse gizmo will toggle when the user clicks on the panel in question.

    Again, check out the vs2008 toolbox. The Group "headers" are what I am after here.

    Believe it or not, all of this now WORKS (gee me, look what I did!) EXCEPT the initialization. If I "Handle" the paint event of the SplitCOntainer.Panel1 object in any way which prevents the flickering jmc alludes to above, any redrawing required by either initialization OR hiding/unhiding of the host form is prevented. And it seems like the SplitContainer panel is constantly redraing (probably checking to see if the splitter has been moved).

    I can post some code, but it is a little confusing, since I am dealing with some semi-complex interaction here. Also, It is not necssecarily structured well ()yet). AFTER I figured out the drawing bits, THEN I was going to try to do some refactoring and restructuring. Code Follows in next post.

    If anyone has any thoughts or experience with this, I woul ddefinitely appreciate it!

  2. #2

    Thread Starter
    Fanatic Member
    Join Date
    Jun 2008
    Location
    Portland, OR, USA
    Posts
    659

    Re: GDI and Paint Event Handling Issues.

    SO, here is where I am at code-wise. I apologize for the length- I wasn't sure if I should leave anything out. It's still in a rather brutish stage, so plase bear with me on that . . .

    So, how can I cut out the flicker, and still handle the necessary redrawing?
    Oh, yeah, there doesn't seem to be any way to acheive this by using "REfresh/Invalidate Etc that I can find so far. But I may just not be doing it right.

    Code:
    Public Class ToolBoxGroup
    
        Private mbooToolGroupChanged As Boolean = False
        Private mbooSelected As Boolean = False
        Private mBooExpanded As Boolean = False
    
    
        Public Sub New()
    
            ' This call is required by the Windows Form Designer.
            InitializeComponent()
    
            ' Add any initialization after the InitializeComponent() call.
    
            mbooToolGroupChanged = True
    
            AddHandler pbExpand.Click, AddressOf Me.OnToolBarClick
            AddHandler SplitContainer1.Panel1.Click, AddressOf Me.OnToolBarClick
            AddHandler SplitContainer1.Panel1.Paint, AddressOf Me.DrawToolBar
            'If I handle the Panel1Paint Event HERE, and check the boolean value
            'mbooToolGroupChanged in the call to DrawToolBar, it works GREAT-but only
            'AFTER initialization. PRIOR to the first user click on the panel1 control, 
            'the panel lacks any of the stylish drawing.  If I DON'T handle the Paint event, 
            'Everything works great, except that the Panel1 Control, in all it's painted glory
            'is in a CONSTANT state of Flicker. 
    
            SetPictureBox()
    
            'Tried calling DrawToolBar HERE to no avail. 
        End Sub
    
    
        Public Sub SetPictureBox()
            Dim pb As PictureBox = pbExpand
    
            'Just some housekeeping to make sure the PictureBox
            'is properly sized . . .
            pb.BackColor = Color.Transparent
            pb.Top = 3
            pb.Left = 3
            pb.Height = 9
            pb.Width = 9
    
        End Sub
    
    
        Public Sub OnToolBarClick(ByVal Sender As Object, ByVal e As System.EventArgs)
    
            'The user has clicked the Panel1 control, so it needs to be
            'Drawn in order to toggle States:
            mbooToolGroupChanged = True
    
            'It has ALSO been Selected:
            mbooSelected = True
    
            'Whichever Expansion State it WAS in, reverse it:
            mBooExpanded = Not mBooExpanded
    
            'Now Draw it:        DrawToolBar()
        End Sub
    
    
        Public Sub DrawToolBar()
    
            'Trying to limit the situations in which the 
            'redrawing happens:
            If mbooToolGroupChanged Then
    
                Dim Pb As PictureBox = pbExpand
                Dim pnl As Panel = SplitContainer1.Panel1
    
                'DO the Graphics thing:
                Dim gfx As Graphics = pnl.CreateGraphics
                Dim Rect As New Rectangle(pnl.Location, pnl.Size)
                Dim args As New System.Windows.Forms.PaintEventArgs(gfx, Rect)
    
                'Which version do I Draw?
                If Not mbooSelected Then
    
                    'A method which draws the contents of SplitContainer.Panel1
                    'with a nice 3D-looking gradient:
                    DrawStandardToolBar(pnl, args)
                Else
    
                    'A method which fills it with a flat highlight color:
                    DrawSelectedToolBar(pnl, args)
                End If
    
                'Use the member function to see which version of the 
                'Selector Icon to use:
                Pb.Image = SelectorImage()
    
                'DO THIS:
                gfx.Dispose()
    
                'Until the user instigates another change, stop the continual re-drawing:
                mbooToolGroupChanged = False
                'Dammit, even when I WANT it to, it won't now (the Hide/Unhide problem)
            End If
    
    
        End Sub
    
        'Test for which image to load:
        Public Function SelectorImage() As Image
            Dim img As Image
    
            If Not mBooExpanded Then
                img = My.Resources.ListExpandPNG9x9
            Else
                img = My.Resources.ListCollapsePNG9x9
            End If
    
            Return img
    
        End Function
    
    
        Private Sub DrawStandardToolBar(ByVal Sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)
    
            'Set a rectangle to the dimensions of the Graphics Clip passed in:
            Dim rect As Drawing.RectangleF = e.ClipRectangle
    
            'Define a path that is the borders of the rectangle:
            Dim gPath As New Drawing2D.GraphicsPath
            gPath.AddRectangle(rect)
    
            'FIll with a vertical gradient:
            Dim br As New Drawing2D.LinearGradientBrush(rect, Color.FromKnownColor(KnownColor.ButtonHighlight), Color.FromKnownColor(KnownColor.ButtonShadow), Drawing2D.LinearGradientMode.Vertical)
            e.Graphics.FillPath(br, gPath)
    
            'Now for some funky offsetting of the Rightmost edge
            'of the rectangle, so that the border I am about to 
            'draw remains within the visible bounds of the Panel:
            Dim Loc2 As Drawing.PointF
            Loc2.X = rect.X
            Loc2.Y = rect.Y
    
            Dim Size2 As Drawing.SizeF
            Size2.Width = rect.Width - 1 'Not sure why this was necessary . . .
            Size2.Height = rect.Height - 1
    
            Dim newRect As New Drawing.RectangleF(Loc2, Size2)
    
            'Draw a border around the gradient region:
            Dim pn As New Drawing.Pen(Color.SlateGray, 1)
            pn.Alignment = Drawing2D.PenAlignment.Inset
            gPath.AddRectangle(newRect)
            e.Graphics.DrawPath(pn, gPath)
    
        End Sub
    
    
    
        Private Sub DrawSelectedToolBar(ByVal Sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)
    
            'Set a rectangle to the dimensions of the Graphics Clip passed in:
            Dim rect As Drawing.RectangleF = e.ClipRectangle
    
            'Define a path that is the borders of the rectangle:
            Dim gPath As New Drawing2D.GraphicsPath
            gPath.AddRectangle(rect)
    
            'FIll with a vertical gradient:
            Dim br As New Drawing2D.LinearGradientBrush(rect, Color.FromKnownColor(KnownColor.InactiveCaptionText), Color.FromKnownColor(KnownColor.InactiveCaptionText), Drawing2D.LinearGradientMode.Vertical)
            e.Graphics.FillPath(br, gPath)
    
    
            'Now for some funky offsetting of the Rightmost edge
            'of the rectangle, so that the border I am about to 
            'draw remains within the visible bounds of the Panel:
            Dim Loc2 As Drawing.PointF
            Loc2.X = rect.X
            Loc2.Y = rect.Y
    
            Dim Size2 As Drawing.SizeF
            Size2.Width = rect.Width - 1 'Not sure why this was necessary . . .
            Size2.Height = rect.Height - 1
    
            Dim newRect As New Drawing.RectangleF(Loc2, Size2)
    
            'Draw a border around the gradient region:
            Dim pn As New Drawing.Pen(Color.FromKnownColor(KnownColor.Highlight), 1)
            pn.Alignment = Drawing2D.PenAlignment.Inset
            gPath.AddRectangle(newRect)
            e.Graphics.DrawPath(pn, gPath)
    
        End Sub
    
        'Code for a test button to see how the control toggles Selected/Unselected state
        'until I can implement the next stage of this thing:
        Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
    
            'Yup. It needs to change:
            mbooToolGroupChanged = True
    
            'Nope. Not selected anymore:
            mbooSelected = False
    
            'Yup. ReDraw:
            DrawToolBar()
        End Sub
    
    End Class

  3. #3
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: GDI and Paint Event Handling Issues.

    Not sure what you are after, but perhaps you can try to use the SetStyles method to set a few styles you often want in a custom drawn control. They will prevent flickering as much as possible. Just set them in the constructor:
    Code:
    Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
    Me.SetStyle(ControlStyles.DoubleBuffer, True)
    Me.SetStyle(ControlStyles.ResizeRedraw, True)
    Me.SetStyle(ControlStyles.UserPaint, True)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width