Sometimes you need a control, not just any control but something that is a little of that and a little
of this, but you just can’t find what you need. So do you build your own control? You could but that
is only if you want something that is completely your’s built from the ground up how you want it.
But what if all you need is this control with something else added, then why not just
extend that control, override one maybe two functions and have all new functionality of that control?
Well that is what I’m going to be doing here today. I will be extending a ListBox control to create
a custom listbox that has your standard Text like a regular listbox but also has a Description text
that will site below your main text, we will also be adding an image to it that will show up on the left
side, so lets get started.
First before we start let me point out a couple of requirements for this task.
- You’ll need at least the .NET Framework 2.0+ or Mono 2.0+
- You’ll need a IDE like Visual Studio, SharpDevelop, or MonoDevelop, or an Editor and the C# compiler.
- You’ll need to have experience in C# and .NET. Although all the code is on this page it you want to
use this article to extend other controls you will need the know-how to do so.
So lets get our CustomListBox class all setup. Here is what it looks like.
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Window.Forms;
namespace Example.Controls {
public class CustomListBox : ListBox {
public CustomListBox() {
this.ItemHeight = 35;
this.DrawMode = DrawMode.OwnerDrawFixed;
}
}
}
Obviously you can set-up your namespace to whatever you want it to be.
So that is what we have so far. Notice that how declare are class CustomListBox
and then also extend the ListBox class. This is where all of our functionality will
be coming from so we don’t have to worry about coding in scrolling, and all those other little
things.
Now before be go any further on are ListBox class lets create a new class that will store
are information for each listbox item. I will call this class CustomListBoxItem
but you can choose whatever you want.
So here is what that class looks like.
public class CustomListBoxItem {
int imageIndex = -1;
string text;
string description;
public int ImageIndex {
get {
return imageIndex;
}
set {
imageIndex = value;
}
}
public string Text {
get {
return text;
}
set {
text = value;
}
}
public string Description {
get {
return description;
}
set {
description = value;
}
}
}
Now that we have information for our Items we can get to work on drawing
are new ListBox.
So one thing we will want to do is add a ImageList to are ListBox class like so.
ImageList imageList1;
public ImageList ImageList {
get {
return imageList1;
}
set {
imageList1 = value;
}
}
We will place that inside of our CustomListBox class just above the class Constructor.
Now the only thing left to do is override the OnDrawItem method and draw are items.
protected override void OnDrawItem(DrawItemEventArgs e) {
if(this.Items.Count > 0) {
CustomListBoxItem item = (CustomListBoxItem)this.Items[e.Index];
e.DrawBackground();
e.DrawFocusRectangle();
Color foreColor = this.ForeColor;
int xOffset = 2;
if((e.State & DrawItemState.Selected) == DrawItemState.Selected) {
foreColor = SystemColors.HighlightText;
}
if(item.ImageIndex >= 0 && imageList1.Images.Count > 0) {
e.Graphics.DrawImage(imageList1.Images[item.ImageIndex], 2, e.Bounds.Y + 2);
xOffset += imageList1.Images[item.ImageIndex].Width + 2;
}
e.Graphics.DrawString(item.Text, this.Font, new SolidBrush(foreColor), new Point(xOffset, e.Bounds.Y + 5));
e.Graphics.DrawString(item.Description, this.Font, new SolidBrush(Color.LightGray), new Point(xOffset, e.Bounds.Y + 20));
}
}
So as you can see we first check to see that the ListBox has items so we don’t get an
error while trying to call a item that does not exists.
After that we need to actually get are item out of the listbox items.
Which is where this comes into play.
CustomListBoxItem item = (CustomListBoxItem)this.Items[e.Index];
The ListBoxItemCollection is a collection of objects so you will need to cast the object
as your class, just make sure you only add your class to the items.
Next we call e.DrawBackground() and e.DrawFocusRectangle(),
these will take care of drawing are background when the item is selected and the focus
rectangle.
After this we check to see if the item is selected so we can use the correct font color.
Then we move on to checking if we have an image to use, and if we do we then draw the image
and change the offset for the text.
After that all we have to do is draw are text, note how we use e.Bounds.Y + 5 to draw the y position
of the text in each item. This is because when we draw the text it is relative to the control position
and not the item position so we add e.Bounds.Y to fix that.
Also you may want to use a different font for the description maybe one smaller or something like that.
And that’s all you can compile your control into a .DLL or include the source file in your application
to use your new control, Enjoy.
Here is a screenshot of what the ListBox looks like.

For you convience both classes.
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace Example {
public class CustomListBox : ListBox {
ImageList imageList1;
public ImageList ImageList {
get {
return imageList1;
}
set {
imageList1 = value;
}
}
public CustomListBox() {
this.ItemHeight = 35;
this.DrawMode = DrawMode.OwnerDrawFixed;
}
protected override void OnDrawItem(DrawItemEventArgs e) {
if(this.Items.Count > 0) {
CustomListBoxItem item = (CustomListBoxItem)this.Items[e.Index];
e.DrawBackground();
e.DrawFocusRectangle();
Color foreColor = this.ForeColor;
int xOffset = 2;
if((e.State & DrawItemState.Selected) == DrawItemState.Selected) {
foreColor = SystemColors.HighlightText;
}
if(item.ImageIndex >= 0 && imageList1.Images.Count > 0) {
e.Graphics.DrawImage(imageList1.Images[item.ImageIndex], 2, e.Bounds.Y + 2);
xOffset += imageList1.Images[item.ImageIndex].Width + 2;
}
e.Graphics.DrawString(item.Text, this.Font, new SolidBrush(foreColor), new Point(xOffset, e.Bounds.Y + 5));
e.Graphics.DrawString(item.Description, this.Font, new SolidBrush(Color.LightGray), new Point(xOffset, e.Bounds.Y + 20));
}
}
}
public class CustomListBoxItem {
int imageIndex = -1;
string text;
string description;
public int ImageIndex {
get {
return imageIndex;
}
set {
imageIndex = value;
}
}
public string Text {
get {
return text;
}
set {
text = value;
}
}
public string Description {
get {
return description;
}
set {
description = value;
}
}
}
}