Table of Contents
In this article I want to demonstrate the technique of creation of a list box
control that draws a small image to the left of each item's text (see Fig.1).
This control exposes the
property containing custom items which may be edited by means of
Visual Studio's collection editor.
Fig.1 Control appearance at run time
There are several articles devoted to the same problem, as for
example ListBox with Icons by nhgiang.
But none of these implement a control having the
that may be edited by means of the Visual Studio's Windows Forms Designer.
They use some manually created code fragments like:
Programmatically adding items - as in C++, no Visual C# advantage.
imageListBox.Items.Add(new ImageListBoxItem("Text 1", 0));
imageListBox.Items.Add(new ImageListBoxItem("Text 2", 1));
imageListBox.Items.Add(new ImageListBoxItem("Text 3", 2));
The control which implementation is described in present article
provides visual editing opportunity that speeds up the development process.
But, of course, it still supports the programmatic way of managing items.
Visually added items - you do not have to keep in mind what index
is associated with an image in the image list. All the editing is done in the
collection editor and you don't need to touch the code (see Fig.2).
Fig.2 Collection editing at design time
In the following paragraphs I will shortly describe the problems I had
encountered. For implementation you better look at the code - it speaks for
Using the control in your project
If you want to try first the demo project, please do not
forget to compile it first (at least the ImageListBox control
library), or otherwise the control won't appear in the test form at design
Fig.3 Principal properties of the control
If you want to use the
ImageListBox on your form,
please also add the
ImageList control to your form, fill it with
images, then set the
and then start adding the items to
Items property (see Fig.3).
There's not much to say about the control class. The
is inherited from standard
ListBox control. We should implement
ImageList property that will allow to provide the source for
images to be drawn in the list box. Another step will be overriding the
Items property. The only important thing is to set the
attribute to inform the designer that we want to serialize the contents of the
collection, not the collection itself during design time. The
property is set to
OwnerDrawFixed value and then is hidden from
property browser by the
[Browsable(false)] attribute. The drawing
is made in the
OnDrawItem protected method and there's nothing
complex to be described.
There are several ways of implementing the collection,
as internal or nested class, or somehow else. In this
demonstration we will use the base class
Items collection to store
the object of our collection, so it will be better to implement the collection
as a nested class to allow access to control's private members. The collection
class should implement the
interfaces. We can't use in this case
since there's more complex behavior than just simple item storage. I recommend
you to look the code for the details of implementation. Another important issue
is to implement
this[int index] property for the collection class
that will allow to access the collection members.
Item class implementation. Method I
Implementing item class from scratch (no base class)
First we need to implement at least two constructors for the item class. The
first is parameterless that allows the Collection editor to create an "empty"
item with all properties set to default. Another constructor is the one with
the largest number of parameters needed to fully reconstruct all editable
properties of the item object. In our case this is
Text, int ImageIndex).
The item class implements the
interfaces (I am not sure whether the
ICloneable is needed, but
this simply copies the set of interfaces implemented by
ISerializable interface is employed by the Collection editor to
allow item properties to be set in the editor. Each item serializes two
Text (string) and
These properties appear in the property grid on the right-hand part of
Collection editor (see Fig.4)
ImageListBoxItem properties. Method I.
Another noticeable problem is to get the Collection editor to display correct values
(as well as thumbnail images) for the
property. We'll do this in three steps:
ImageList read-only property to the item class.
Hide it from property browser by
This property is used by the
UITypeEditor assigned to
property and it is the image list from which this value editor takes the images
to be displayed in the combobox.
And voilа! Here is the combobox filled with images as desired (see Fig.5).
As a final stroke, we need the Windows Form Designer to automatically add the
code to our form's
InitializeComponent() function like the one
This is performed by implementing the type converter class that inherits
TypeConverter. We will use the
(as Microsoft does for
ListViewItem) to lessen the amount of
functions to override. The only functions we should override are
ConvertTo. We should convert only to
type used by the Windows Form Designer. The
InstanceDescriptor object by providing the
for the constructor that will appear in the code added by the designer (
Text, int ImageIndex) in our case). Then we associate this converter class with the
ImageListBoxItem by means of
attribute. Now the control is ready to be edited by the designer.
The code that is added automatically, will be split in two parts. First, the
member variables for all the items are added to your form class. Then in form's
InitializeComponent method the constructors are called for
items, the items are added to listbox and then their properties are set.
This article demonstrates the method of exposing the collection as a property.
This technique may be useful for a bunch of other controls (bands for
OutlookBar, column and row headers for custom grid controls, etc.). If you
spend a couple of days in implementing a set of classes as described in this
technique, this will gain you time in the projects where you are going to use
the designed controls. Instead of adding lines of code, simply click, type and
enjoy. I already did.
The principal problem that remains unresolved, is the localization for Method I.
I use the
[Localizable(true)] attribute for all members I
want to be localizable, but this doesn't seem to work in a proper manner. When
I set the
Localizable property for the form to
then try to switch between the languages, the
remain invariant. If anyone has already solved such a problem, please contact me
if possible. I have noticed that almost the same problem is encountered