Click here to Skip to main content
15,918,967 members
Articles / Multimedia / GDI+

WinForms Custom Container Control

Rate me:
Please Sign up or sign in to vote.
4.63/5 (20 votes)
6 Jan 20052 min read 142.8K   2.9K   67   5
A UserControl for WinForms VB.NET that has child controls that it contains and is scrollable. This example shows how to draw your own controls and build your custom container to list them.

Sample Image - CustomContainerControl.jpg


Building a container control for WinForms was easier than I first imagined. Why did I build one? Because I needed a custom solution for viewing controls that were created, but could not be placed into any other third-party control.

It appears that Microsoft left out the Data Repeater that was in VB 6 for Windows, so now we have to make our own.

Making the Container

The container is just a UserControl. Some of the particulars are to set the AutoScroll=True and add some events for handling which control is selected as well as the count.

Public ReadOnly Property Count() As Integer
        Return Me.Controls.Count
    End Get
End Property

Public ReadOnly Property SelectedItem() As ChildItem
        Return Me._SelectedChild
    End Get
End Property

The Container control is loaded by passing it a strong-typed collection.

Public Property Items() As SimpleItemCollection
        Return _Items
    End Get
    Set(ByVal Value As SimpleItemCollection)
        _Items = Value
        If Value Is Nothing Then
        End If
    End Set
End Property

Once the collection is passed in, you can load the control using the LoadAll method in the custom control.

Private Sub LoadAll()
    Dim n As Integer
    Dim bSuspend As Boolean
    Dim flipper As Boolean
    Dim counter As Integer

    _Loading = True
    RaiseEvent SelectedItemChanged(Nothing)
    _SelectedChild = Nothing

    _Top = 0
    For Each item As SimpleItem In Me._Items
        Dim itm As New ChildItem(item)


        With itm
            .Left = 0
            .Width = Me.Width
            .Top = _Top
        End With

        _Top += itm.Height

        If Not bSuspend Then
            ' This loads just enough controls at first
            ' so it appears as if the control is doing something
            If _Top > Me.Height Then
                bSuspend = True
            End If
        End If

        AddHandler itm.ItemSelected, AddressOf ChildSelected
        AddHandler itm.ItemDoubleClicked, AddressOf _

        'Select the first item
        If Me.Count = 1 Then
        End If

    RaiseEvent CountChanged(Me.Count)

    _Loading = False

    ' Hide the Horizontal scrollbar that shows

    ' up sometimes when the child
    ' controls are not less than the size of the container.
    Me.AutoScrollHandler.VisibleAutoScrollHorizontal = False
End Sub

Making the Child Control

The child control inherits from Control. The reason for doing so is the reduced overhead of the ScrollableControl and the ContainerControl. There might be better ways to build this control, but I've found this to be sufficient and quick displaying. If you use a UserControl as the child and set the Dock to Top, it eases the loading code, etc., but it takes forever to resize the control and doesn't paint as fast.

The child control is simple and powerful once you get the hang of painting to the control. Get familiar with using the Graphic class. Methods such as Drawstring and FillRectangle.

Create a class that Inherits from Control.

Image 2

Add some Imports to the Control to manage the ComponentModel as well as the drawing.

Imports System.Drawing.Drawing2D
Imports System.ComponentModel

There are some key style settings you need to add that are documented very little on the web. These settings manage the drawing and scrolling for the controls.

Public Sub New()

    ' This call is required by the Component Designer.

    Me.BackColor = Color.White

    ' Set the value of the double-buffering style bits to true.
    ' This paints the control off screen so there's no flicker effect.
    Me.SetStyle(ControlStyles.DoubleBuffer _
      Or ControlStyles.UserPaint _
      Or ControlStyles.AllPaintingInWmPaint, _

    ' This enables mouse support such as the Mouse Wheel
    setstyle(ControlStyles.UserMouse, True)

    ' This will repaint the control whenever it is resized
    setstyle(ControlStyles.ResizeRedraw, True)

End Sub

When creating the control, which is handled by the Custom container, just pass in the Contact (which in this example is the class object that holds the data for the control).

Public Sub New(ByVal item As SimpleItem)
    _Contact = item
    Me.Height = 40
End Sub

Public ReadOnly Property Contact() As SimpleItem
        Return _Contact
    End Get
End Property

Painting the child control is going to have to be managed by the code. Unlike the UserControl class, this control does not paint itself. You lose the simplicity, but you gain speed when loading hundreds of controls. Plus, it's cool to impress friends and neighbors with your code! Yeah right.

Protected Overrides Sub OnPaint(ByVal pe As _

    Dim ft As Font
    ' Get the font of the Parent
    If Not Me.Parent Is Nothing Then
        ft = Me.Parent.Font
        ft = New Font("Tahoma", 8, GraphicsUnit.Point)
    End If

    ' Paint the First Name and Last Name
    pe.Graphics.DrawString(Contact.FirstName _
       & " " & Contact.LastName, ft, _
       New SolidBrush(Me.Parent.ForeColor), 28, 6)

End Sub

Now this is just a simple and rude example. You can get as elaborate as you want with painting the control. This is how the big-boys (third-party) do it.

Managing which item is selected is just a matter of trapping for MouseDown and raising an event that tells the parent container, which in this instance is the Custom Container, that this item is selected to unselect all others and move Me into view.

Private Sub pnlMain_MouseDown(ByVal sender As System.Object, _
     ByVal e As System.Windows.Forms.MouseEventArgs) _
           Handles MyBase.MouseDown
    If e.Button = MouseButtons.Left AndAlso AllowSelect Then

        ' Repaint the control
    End If

End Sub

Public Sub SetSelected()
    RaiseEvent ItemSelected(Me)
    Selected = True
End Sub

Public Property Selected() As Boolean
        Return _Selected
    End Get
    Set(ByVal Value As Boolean)
        _Selected = Value
        If Value Then
            BackColor = Color.Gainsboro
            BackColor = Color.White
        End If
    End Set
End Property

The example code has all you'll need to get started and expand upon.


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Written By
Software Developer (Senior) BOCA Software Technologies, Inc.
United States United States
.NET Developer in Garner, North Carolina. Specializing in WinForms development in C#, VB.Net.

CEO/Founder BOCA Software Technologies, Inc.

Comments and Discussions

GeneralJust a suggestion Pin
Diamonddrake24-Dec-10 6:46
Diamonddrake24-Dec-10 6:46 
QuestionPanel class? Pin
Uwe Keim6-Jan-05 18:18
sitebuilderUwe Keim6-Jan-05 18:18 
AnswerRe: Panel class? Pin
Victor Boba10-Jan-05 15:33
Victor Boba10-Jan-05 15:33 
GeneralRe: Panel class? Pin
wumpus19-Feb-05 14:16
wumpus19-Feb-05 14:16 
It's true, Victor is actually a blind developer. Take pity on him Smile | :)

Great article-- I gave it a 5!
QuestionRe: Panel class? Pin
MikeAngel13-Apr-12 7:46
MikeAngel13-Apr-12 7:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.