Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Flexible List Control

4.90/5 (60 votes)
7 Feb 2013CPOL7 min read 147.5K   8.4K  
Building a list control that allows anything to be added to it.
333864/screenshot_new.png

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.

VB.NET
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.

VB.NET
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.

VB.NET
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.

VB.NET
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.

333864/scr1.png

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:

333864/scr2.png

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:

VB.NET
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
                ' Its the first control, all subsequent controls follow
                ' the anchor behavior of this control.
                c.Anchor = AnchorStyles.Left + AnchorStyles.Top
                c.Width = flpListBox.Width - SystemInformation.VerticalScrollBarWidth
            Else
                ' It is not the first control. Set its anchor to
                ' copy the width of the first control in the list.
                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.

VB.NET
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:

VB.NET
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
        ' Assign an auto generated name
        .Name = "item" & flpListBox.Controls.Count + 1
        .Margin = New Padding(0)
        ' set properties
        .Song = Song
        .Artist = Artist
        .Album = Album
        .Duration = Duration
        .Image = SongImage
        .Rating = Rating
    End With
    ' To check when the selection is changed
    AddHandler c.SelectionChanged, AddressOf SelectionChanged
    '
    flpListBox.Controls.Add(c)
    SetupAnchors()
End Sub

And the Remove procedure has been amended as:

VB.NET
Public Sub Remove(name As String)
    ' grab which control is being removed
    Dim c As ListControlItem = flpListBox.Controls(name)
    flpListBox.Controls.Remove(c)
    ' remove the event hook
    RemoveHandler c.SelectionChanged, AddressOf SelectionChanged
    ' now dispose off properly
    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:

VB.NET
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:

VB.NET
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)