Introduction
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
Get
Return Me.Controls.Count
End Get
End Property
Public ReadOnly Property SelectedItem() As ChildItem
Get
Return Me._SelectedChild
End Get
End Property
The Container control is loaded by passing it a strong-typed collection.
Public Property Items() As SimpleItemCollection
Get
Return _Items
End Get
Set(ByVal Value As SimpleItemCollection)
_Items = Value
If Value Is Nothing Then
Me.ClearAll()
Else
Me.LoadAll()
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
Me.Controls.Clear()
Me.SuspendLayout()
RaiseEvent SelectedItemChanged(Nothing)
_SelectedChild = Nothing
_Top = 0
For Each item As SimpleItem In Me._Items
Dim itm As New ChildItem(item)
itm.SuspendLayout()
Me.Controls.Add(itm)
With itm
.Left = 0
.Width = Me.Width
.Top = _Top
End With
itm.ResumeLayout()
_Top += itm.Height
If Not bSuspend Then
If _Top > Me.Height Then
Application.DoEvents()
Me.SuspendLayout()
bSuspend = True
End If
End If
AddHandler itm.ItemSelected, AddressOf ChildSelected
AddHandler itm.ItemDoubleClicked, AddressOf _
ItemDoubleClicked_Handler
If Me.Count = 1 Then
itm.SetSelected()
End If
Next
Me.ResumeLayout()
RaiseEvent CountChanged(Me.Count)
_Loading = False
Me.ResumeLayout()
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
.
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()
MyBase.New()
InitializeComponent()
Me.BackColor = Color.White
Me.SetStyle(ControlStyles.DoubleBuffer _
Or ControlStyles.UserPaint _
Or ControlStyles.AllPaintingInWmPaint, _
True)
setstyle(ControlStyles.UserMouse, True)
setstyle(ControlStyles.ResizeRedraw, True)
Me.UpdateStyles()
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)
Me.New()
_Contact = item
Me.Height = 40
End Sub
Public ReadOnly Property Contact() As SimpleItem
Get
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 _
System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(pe)
Dim ft As Font
If Not Me.Parent Is Nothing Then
ft = Me.Parent.Font
Else
ft = New Font("Tahoma", 8, GraphicsUnit.Point)
End If
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
Me.SetSelected()
Invalidate()
End If
End Sub
Public Sub SetSelected()
RaiseEvent ItemSelected(Me)
Selected = True
End Sub
Public Property Selected() As Boolean
Get
Return _Selected
End Get
Set(ByVal Value As Boolean)
_Selected = Value
If Value Then
BackColor = Color.Gainsboro
Else
BackColor = Color.White
End If
Invalidate()
End Set
End Property
The example code has all you'll need to get started and expand upon.