Introduction
In a normal listbox or listview control, you are bound with a lot of
restrictions. You cannot add images or icons properly, cannot provide your own
highlighting colors, or placement of text, and a most wanted feature is that you
cannot add multiline text to the default list controls.
Today I will show you how to build a list control that shows list items just
the way you want them to be. They can be multiline, or have images in them,
various sizes of texts or background colors, complex placements of items in it,
etc. Its all up to you how you design it. Complete flexibility!
Building The List Control
We're going to be using the default available controls to make this dream
come true. I will show you how to make a list control, what you do with that
list control is totally up to your imagination. So starting off we're going to
make a list control having a FlowLayoutPanel control in it only.
Create a new project in your Visual Studio. Add a Usercontrol to your project
and name it as ListControl.
On the ListControl place a FlowLayoutPanel and name it flpListBox.
Provide the following properties to the flpListBox:
- AutoScroll = True
- AutoSize = False
- Dock = Fill
- FlowDirection = TopDown
- Margin = 0, 0, 0, 0
- WrapContents = False
And provide following properties to the ListControl
usercontrol:
- BorderStyle = FixedSingle
- Padding = 0, 0, 0, 0
Now we need to add code to the ListControl to handle the
adding, removing, counting and clearing functions, just like any other list
control. Therefore we will add four functions Add, Remove, Clear, and
Count.
Public Sub Add(c As Control)
End Sub
Public Sub Remove(name As String)
End Sub
Public Sub Clear()
End Sub
Public ReadOnly Property Count() As Integer
Get
Return flpListBox.Controls.Count
End Get
End Property
Let's add the code for the Add sub. As you can see in the definition the Add
sub is accepting a control as a listitem, not text or any other kind of object.
This is because we're going to create a flexible list control where any type of
independent control can be added to it and the our ListControl
will move it around just like any other list control.
Public Sub Add(c As Control)
flpListBox.Controls.Add(c)
End Sub
That was a simple code. It only adds your control (i.e. list item) to the
flow layout panel. The flow layout panel has been told to add all the controls
inside it from Top to Down manner. So all controls will be added
vertically. You keep on adding controls and it will keep on appending it to the
list of controls added to the flow layout panel. Now lets code for Remove and
Clear subs.
Public Sub Remove(name As String)
Dim c As Control = flpListBox.Controls(Name)
flpListBox.Controls.Remove(c)
c.Dispose()
End Sub
Public Sub Clear()
Do
If flpListBox.Controls.Count = 0 Then Exit Do
Dim c As Control = flpListBox.Controls(0)
flpListBox.Controls.Remove(c)
c.Dispose()
Loop
End Sub
I want to discuss the Clear sub first here. In order to remove all the
controls from your list control, there is a very easy way of doing it. Simply
call the flpListBox.Controls.Clear and it will remove all the
controls. But these controls still remain in the memory. It is up to your coding
method or according to your programming needs, but I prefer my list control to
dispose off the control as soon as I remove them from the list control. In order
to do this first we need to grab the control from the flow layout panel, remove
it from its control list and then finally dispose it off.
In the Clear sub the above process of disposing is repeated until
all the controls from the list have been removed. But in Remove sub of
course we only need to do this process once just for the control user has asked
to remove.
Using The Control
Our ListControl is complete. You can trial run this control
at this stage. Place ListControl on a new Form and add the
following code to the Load event.
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
For i as Integer = 1 To 20
Dim btn As New Button
With btn
.Name = "b" & i
.Text = "Button " & i
.Height = 25
.Margin = New Padding(0)
End With
ListControl1.Add(btn)
Next
End Sub
The above code will add twenty buttons to the ListControl.
For each of those button you can hook a click event by AddHandler in
your form's code.
The reason why I have set the Margin property of the buttons to zero
is to reduce the line gap between each row in our list control.
Our ListControl is ready. It will automatically show the
scroll bar whenever the items exceed the height of the list control. You can
also scroll with mouse wheel. At the moment it does not handle resize events, in
case you may want to resize your control to fit the width of ListControl.
Managing Size and Resize Event
By here our list control is complete. You may want to add uneven sized
controls in it, its entirely up to you. You could even make something fancy like
this:
Because it is a flow layout panel control it allows you to add anything to it
and it will manage its placement by itself. But only placement, not the sizes.
Size is defined by you.
In order to build a proper looking list control we need to automate the sizes
as well here. For this the following code need to be added:
Private Sub SetupAnchors()
If flpListBox.Controls.Count > 0 Then
For i = 0 To flpListBox.Controls.Count - 1
Dim c As Control = flpListBox.Controls(i)
If i = 0 Then
c.Anchor = AnchorStyles.Left + AnchorStyles.Top
c.Width = flpListBox.Width - SystemInformation.VerticalScrollBarWidth
Else
c.Anchor = AnchorStyles.Left + AnchorStyles.Right
End If
Next
End If
End Sub
Private Sub flpListBox_Layout(sender As Object, e As System.Windows.Forms.LayoutEventArgs) Handles flpListBox.Layout
If flpListBox.Controls.Count Then
flpListBox.Controls(0).Width = flpListBox.Size.Width - SystemInformation.VerticalScrollBarWidth
End If
End Sub
We have added a subroutine called SetupAnchors. In a flow layout panel, if
you set the anchor of a control to Left + Right, all controls inside it can have
their own heights, but for widths they all adopt the width of the first control
in the list. Whatever the width is of the first control, other controls will
automatically follow. But the first control cannot have a Left + Right anchor.
You have to manually define the width of the first control. This is how flow
layout panel behaves by default.
So in above I have assigned a Top+Left anchor for the very first control in
the list and all the other controls have Left+Right anchor. As a result all
controls will follow the width of the first control. So whenever the
ListControl is resized, it will resize the first control only (see
flpListBox.Resize event) and other controls will
automatically copy its width.
Accordingly amend the Add and Remove subs to call the
SetupAnchors sub from them whenever a control is added or removed from the
list.
Public Sub Add(c As Control)
flpListBox.Controls.Add(c)
SetupAnchors()
End Sub
Public Sub Remove(name As String)
Dim c As Control = flpListBox.Controls(Name)
flpListBox.Controls.Remove(c)
c.Dispose()
SetupAnchors()
End Sub
Taking It To Next Level
Since ListControl allows any kind of control to be added to
it and it will handle it like any other list control item, you can even make
custom user controls and add it to ListControl. In the
first screenshot at the top of the page, I have created a custom usercontrol which manages its own
highlighting and colors, text placement, icon and stars, etc. All
ListControl does is add it to its list.
To achieve this lets make some improvements in the code that I've explained above. I've made some slight changes... now
you don't have to create a new control to add to the list, the ListControl will do that for you. Its all according to your needs, really! You can turn, twist,
twirl etc. with this control according to your application needs. You may embed
everything inside the ListControl or manually create a new control and attach
it.
I've built a list item control, called ListControlItem,
that has the following properties: song name,
artist name, album name, duration of song, image of the song or album, rating
for the song.
Note: Since this article is only to demonstrate the possibilities of
ListControl, I will not be explaining the code of ListControlItem. To see the code of ListControlItem download the sample project and have a look.
So I've changed the Add procedure like this:
Public Sub Add(Song As String, Artist As String, Album As String, Duration As String, SongImage As Image, Rating As Integer)
Dim c As New ListControlItem
With c
.Name = "item" & flpListBox.Controls.Count + 1
.Margin = New Padding(0)
.Song = Song
.Artist = Artist
.Album = Album
.Duration = Duration
.Image = SongImage
.Rating = Rating
End With
AddHandler c.SelectionChanged, AddressOf SelectionChanged
flpListBox.Controls.Add(c)
SetupAnchors()
End Sub
And the Remove procedure has been amended as:
Public Sub Remove(name As String)
Dim c As ListControlItem = flpListBox.Controls(name)
flpListBox.Controls.Remove(c)
RemoveHandler c.SelectionChanged, AddressOf SelectionChanged
c.Dispose()
SetupAnchors()
End Sub
As I have added a hook to an event from ListControlItem to monitor
when selection is changed, therefore when disposing the control it is an
efficient programming practice to remove the hook as well.
And I've added another subroutine to ensure that only one list item is
selected at a time:
Dim mLastSelected As ListControlItem = Nothing
Private Sub SelectionChanged(sender As Object)
If mLastSelected IsNot Nothing Then
mLastSelected.Selected = False
End If
mLastSelected = sender
End Sub
That's it!
Now when using the modified ListControl you don't need to create
or dispose controls. All will be handled by itself. You can add items to the
list this way from your Windows Form:
ListControl1.Add("Party Rock Anthem (feat. Lauren Bennett & GoonRock)", "LMFAO", "For DJs Only [2011]", "4:23", ImageList1.Images(7), 5)
See Example Code
Our ListControl is finally complete. It may not be as memory
efficient as the default listview or listbox control or any other third party
listview control, but it works absolutely well and provides the flexibility that
other list controls don't.
This little sample project is built to demonstrate the ListControl
being used for a media player type application. It depends what type of
application you're building. How the list item will look like is entirely up to
you as you will be designing the list item, ListControl will
only manage it in a list form.