Introduction
There are many examples here on The Code Project that enhance the functionality of the ListView
. I was looking for a way to display and edit multiple images on subitems and be able to sort the ListView
by date, number, string, and images, but I wasn't able to find one that could do that. The subitems also had to be editable - either by a textbox, or by a user defined control. Finally, I wanted to be able to add controls to the subitems and provide some kind of "boolean" columns. The subitems that are in such a boolean column will be able to show two values - true or false - that can be represented by two images. If you set the Editable
property to true
, the user can click such a subitem and the value switches.
Background
Previously, you had to use the Win32 API to accomplish the above. Now, with version 2.0 of the .NET framework, this can be much easily done with the concept of ownerdraw. If the OwnerDraw
property of a ListView
is set to false
, you can do all of the drawing of the ListView
yourself. In this EXListView
class, I use ownerdraw extensively. Although using the Win32 API is faster than using ownerdraw from the .NET framework, this ListView
is still fast, even with 1000 items with subitems that contain multiple images.
Using the code
The code accompanying this article is the EXListView
class, the EXComboBox
class, and a simple form that uses these in a form. I added an executable, but you can easily compile it yourself with the command csc /t:winexe /r:System.Windows.Forms.dll EXListView.cs EXComboBox.cs MyForms.cs - the compiler csc.exe can be found on Windows XP in C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\.
Defining an instance and adding columns
If you have created an instance of the EXListView
class (for example: EXListView exlstv = new EXListView()
), you can begin adding columns. There are three different types of columns: EXColumnHeader
, EXEditableColumnHeader
(whose subitems can be edited by a default TextBox
or a user defined control), and the EXBoolColumnHeader
(whose subitems can only show true or false and can optionally be edited).
For example, to get a column header that can be edited by a default TextBox
, you do:
EXEditableColumnHeader editcol =
new EXEditableColumnHeader("Movie");
If you want to specify a control by which the subitems can be edited, you do:
ComboBox cmbx = new ComboBox();
cmbx.Items.AddRange(new string[] {"1", "2", "3", "4"});
EXEditableColumnHeader editcol =
new EXEditableColumnHeader("Position");
editcol.MyControl = cmbx;
A boolean column:
EXBoolColumnHeader boolcol = new EXBoolColumnHeader("OK");
boolcol.Editable = true;
boolcol.TrueImage = Image.FromFile("true.png");
boolcol.FalseImage = Image.FromFile("false.png");
Add (sub)items to the EXListView
A regular ListViewItem
:
EXListViewItem lstvitem = new EXListViewItem("Pete");
Items and subitems that can show images have a property MyValue
that can hold the real textual value of the (sub)item. This can be handy if you don't want to show text, but need some kind of value, for example, to insert into a database.
An item that can show only one image:
EXImageListViewItem imglstvitem = new EXImageListViewItem();
imglstvitem.MyImage = Image.FromFile("Pete.jpg");
imglstvitem.MyValue = "Pete";
An item that can show multiple images:
EXMultipleImagesListViewItem mimglstvitem =
new EXMultipleImagesListViewItem();
ArrayList images = new ArrayList(new object[]
{Image.FromFile("Pete.jpg"),
Image.FromFile("man.png")});
mimglstvitem.MyImages = images;
imglstvitem.MyValue = "Pete the man";
The same way, you can add subitems to the EXListView
. Please see the EXListView
class for more information.
Use the EXComboBox to edit items with images
The source also contains an extended ComboBox
class - the EXComboBox
class: this is a combobox that can hold images. You can use this combobox to edit (sub)items with images in the EXListView
.
EXListView lstv = new EXListView();
EXComboBox excmbx = new EXComboBox();
excmbx.MyHighlightBrush = Brushes.Goldenrod;
excmbx.ItemHeight = 20;
excmbx.Items.Add(new EXComboBox.EXImageItem(
Image.FromFile("music.png"), "Music"));
excmbx.Items.Add(new EXComboBox.EXImageItem(
Image.FromFile("comedy.png"), "Comedy"));
excmbx.Items.Add(new EXComboBox.EXMultipleImagesItem(
new ArrayList(new object[]
{Image.FromFile("love.png"),
Image.FromFile("comedy.png")}),
"Romantic comedy"));
lstv.Columns.Add(new EXEditableColumnHeader("Genre",
excmbx, 60));
Add and remove images during runtime
If you want to add or remove images during runtime, you can just set the MyImage
property to null
, or, in the case of a multiple-images (sub)item, use the ArrayList
methods, such as Add()
and RemoveAt()
. Examples:
If you want to remove the second image from an EXMulitpleImagesListViewSubItem
(second column) in the third row, you do:
EXMultipleImagesListViewSubItem subitem =
lstv.Items[2].SubItems[1]
as EXMultipleImagesListViewSubItem;
subitem.MyImages.RemoveAt(1);
lstv.Invalidate(subitem.Bounds);
You must invalidate this rectangle yourself.
Adjusting the heights of rows
Because all the drawing is done in the owner-draw events, the SmallImageList
property is only used to show the up and down arrows in the column headers and not to show images in the ListViewItem
. To adjust the row height, you assign an ImageList
and set the Size
to the appropriate size. This is a dirty hack, but the only way I could do it. If you can find another way - please let me know.
Adding controls
To add a control to a subitem, create a EXControlListViewSubItem
, then create the control, add the EXControlListViewSubItem
to the listviewitem, and at last, add the control to the EXControlListViewSubItem
with the EXListView
method AddControlToSubitem
:
EXListViewItem item = new EXListViewItem("Item 1");
EXControlListViewSubItem cs = new EXControlListViewSubItem();
ProgressBar b = new ProgressBar();
b.Minimum = 0;
b.Maximum = 1000;
b.Step = 1;
item.SubItems.Add(cs);
lstv.AddControlToSubItem(b, cs);
Points of interest
There is, at this point, an irritating bug in the EXListView
class: when you move your mouse pointer into the ListView
from left to right, the DrawItem
event will be fired, but accompanied by the DrawSubItem
event. So, the ListViewItem
will be repainted, but the subitems will not. If the content of the ListViewItem
(represented by the first subitem) is larger than the column, the content falls over the neighboring subitems.
History
- Created on 9 February 2006.
- Updated code and article: now you can edit columns with images as well - 15 February 2006.