In this small article series, I will explain how to write a customized listbox control with some extended features that I believe most developers will find useful. Apart from the specific features being explained, these articles will also present an idea of how you can go about making your own custom components, providing you with your very own additions to the WinForms control toolbox. In this first article, I will address the collection events issue, and more specifically, how to add event notification when adding or removing items.
Many of the .NET WinForms controls that keep a collection of items are missing the vital events for when the collection changes. Normally, you would have to work this out using a data binding or a custom data source, but sometimes, it would just be nice to have the events wired to the control itself. So, we will be looking at this along with the basics for deriving your custom component. A note on the code; I decided to use VB.NET (even though I am more of a C# guy) because I've seen more questions concerning this subject on VB.NET forums.
Creating a derived component
ListBox control has its implementation, and we are not really able to change it much. However, we can create our own control and inherit all the functionality from the classic
ListBox control. We could take the long way by creating a User Control and implementing all the features from scratch, but that would take countless hours of developing, and that's not really the point here. What we want is to keep the basic functionality and only change the behavior for some selected features. If you are familiar with the concepts of inheritance, you will have no trouble understanding this. What you may not have thought about, however, is that class inheritance works the same way with controls. After all, a control such as the
Label, is a class like any other class in which you would write your code.
Let's try this out with a little example:
- Create a new Windows application.
- Add a new class to your project and name it
- Open the code for the class and insert the sentence "
Inherits Label" as shown in the code snippet below.
- Compile the solution.
Public Class MyLabel
If you open up the Form1 designer and check out the toolbox, you will find a new tool at the top called
MyLabel. Now, this control is just like the classic
Label control. You can put it on the
Form1 surface and set the properties for it. It's really no different from the
Label control. Why is that? It's because we're inheriting the
Label control and all of its functionality, or put another way, we are deriving from
So, now that you are familiar with the concepts of making your own control, let's get started with our
New items property interface
Our first goal with implementing the
CustomListBox control is to be able to generate events for when items are added or removed. Looking at our base class, the
ListBox control, we can see that all items are stored in a
ListBox.ObjectCollection class that is accessed through the
Items property. What we are going to do in our
CustomListBox is to create a new class that will serve as the new
Items interface. This class will be what the users of our
CustomListBox control will be accessing when they work on the collection. Let's have a look at the code for this class.
Public Class CollectionObjectInterface
Private owner As CustomListBox
Friend Sub New(ByVal owner As CustomListBox)
Me.owner = owner
Public Sub AddRange(ByVal items() As Object)
For Each item As Object In items
Public Sub AddRange(ByVal value As ListBox.ObjectCollection)
For Each item As Object In value
Public Sub Add(ByVal item As Object)
Public Sub Insert(ByVal index As Integer, ByVal item As Object)
Public Sub Remove(ByVal item As Object)
Dim index As Integer = Me.IndexOf(item)
If (index > -1) Then
Public Sub Clear()
For i As Integer = (Me.Count - 1) To 0 Step -1
Public Sub RemoveAt(ByVal index As Integer)
Public Function Contains(ByVal item As Object) As Boolean
Public Sub CopyTo(ByVal destination() As Object, ByVal arrayIndex As Integer)
Public Function GetEnumerator() As System.Collections.IEnumerator _
Public Function IndexOf(ByVal value As Object) As Integer
Public ReadOnly Property Count() As Integer
Public Property Item(ByVal index As Integer) As Object
Set(ByVal value As Object)
Me.owner.InnerItems(index) = value
ObjectCollectionInterface has all the public members of the
ListBox.ObjectCollection class. You could say it is a copy of the
ListBox.ObjectCollection class, although the implementation is not the same. This is because we want the user to be able to use the
Items property in the same way as with the
ListBox control. For certain reasons, which are beyond the scope of this article to address, we do not inherit the
ListBox.ObjectCollection. Therefore, we have to fully implement all the members.
The idea of this class is to provide a layer between the user and the base class collection of items, so that we can raise the
ItemRemoved events. It doesn't store any items in itself, but simply calls the methods of the original collection. The
CustomListBox will own this class, so in the constructor, we pass a reference to the parent
CustomListBox will expose the base class (
Items collection via a property called
ObjectCollectionInterface will use this property to delegate all work being done on the
Items collection. As you can see, all the method calls that have to do with adding or removing items are routed to the
RemoveAt methods in which we will perform the appropriate action on the original collection, and raise the events.
The CustomListBox control
CustomListBox control should be responsible for providing helper methods to raise the
ItemRemoved events, implement a private property to expose the base class items collection to the
ObjectCollectionInterface class, and provide a new implementation of the
Items property that uses the
ObjectCollectionInterface. Let's have a look at these three objectives in code:
Public Class CustomListBox
Public Event ItemAdded(ByVal sender As Object, ByVal e As ListBoxItemEventArgs)
Public Event ItemRemoved(ByVal sender As Object, ByVal e As ListBoxItemEventArgs)
Private itemsInterface As CollectionObjectInterface
Public Sub New()
Me.itemsInterface = New CollectionObjectInterface(Me)
Public Shadows ReadOnly Property Items() As CollectionObjectInterface
Private ReadOnly Property InnerItems() As ListBox.ObjectCollection
Protected Overridable Sub OnItemAdded(ByVal index As Integer)
RaiseEvent ItemAdded(Me, New ListBoxItemEventArgs(index))
Protected Overridable Sub OnItemRemoved(ByVal index As Integer)
RaiseEvent ItemRemoved(Me, New ListBoxItemEventArgs(index))
Apart from the fact that we are inheriting the
ListBox control, the most interesting thing about the
CustomListBox class is the declaration of the
Items property. Remember that we are inheriting an
Items property from our base class,
ListBox, and we would have no use for our
ObjectCollectionInterface class if the user would access that property. So, in order to take advantage of our own interface, we must hide the base class property. To hide a base class member, we have to use the
Shadows modifier. The new
Items property returns an instance of the
ObjectCollectionInterface. The instance is held by a private variable that is assigned in the constructor.
You may have noticed that in the event declaration, there is a parameter
e that is declared as
ListBoxItemEventArgs. This class derives from the
EventArgs class, and stores an integer that represents the zero-based index for the item that was added or removed. The code for the
ListBoxItemEventArgs class looks like this:
Public Class ListBoxItemEventArgs
Private _index As Integer
Public Sub New(ByVal index As Integer)
Me._index = index
Public ReadOnly Property Index() As Integer
There are, of course, ways in which you could extend the functionality of the events. For instance, you could add the possibility for event subscribers to prevent items from being removed, by adding a cancel flag to the event data. Or, you could add a reference to the removed object in the event arguments for the
A note on overridable members
When doing some background work for this article, I found an interview with Microsoft C# lead architect Anders Hejlsberg, discussing the pros and cons of declaring base class members as overridable. According to Anders Hejlsberg, there are two main reasons why the majority of class members in the .NET framework are not overridable. First, it puts a lot of responsibility on the developer that overrides a base member. He or she has to know what other methods can be called from within the override member, and in what order they should be called not to cause an invalid state. Secondly, it introduces an overhead to call a class member that has been overridden. Personally, I find it limiting not to be able to override members at certain times. Depending on context complexity, I believe a lot more base class members of the .NET framework could be marked as overridable without introducing an overall decreased performance or program stability. In the
CustomListBox case, things could have been made a lot nicer if all the members of the
ListBox.ObjectCollection were marked as overridable.
You can find the whole interview with Anders Hejlsberg here.
In this first article, we have looked at how we can extend or change the functionality of an existing UI control by deriving a new control from it. We implemented a
CustomListBox control that can generate events for when items stored in the base class collection are either added or removed. In the next article, we will be looking at how to implement methods for moving selected items up or down the list.