
Snap 1: Shows the demo project first page, it's about adding columns and items in Details view mode.

Snap 2: Shows the demo project second page, it's about advanced functions of Details view mode.

Snap 3: Shows the demo project third page, it's about Thumbnails view mode and drag and drop function.
Introduction
Most Windows applications need a listview control for different purposes. For files browsing, for items listing ...etc., and so you may need a ListView control
in your .NET project. Using the Visual Studio Forms Designer, you can drag and drop the ListView from the ToolBox bar into your
Windows Form and you're ready to
go. This control is good for normal use like adding items, subitems, draw in details with columns ...etc., but when you need to customize this ListView, it gets
worse. OK, I'll cut it directly to the point. Why have I written this control?
I started a project called Emulators Organizer http://sourceforge.net/projects/emusorganizer/,
this project is written in .NET C#. However that project is about organizing emulators and
ROMs (or games) so it basically depends on a listview control. In
version 4.6 and older it uses the normal ListView that is provided with Windows. It worked well in old versions but when the program got advanced functions I
needed to add more customizations to that ListView. And that was a disaster because of:
- I had to use columns with reordering ability, the user can sort items when he/she clicks on a column and saves all column settings in the project.
Well, this was done after a lot of hard work. The result was a very complex code which wrapped around subitems and indexes (you should remember each index of
the subitem and what it represents).
- The items can be viewed (in details view) as images and texts. Well, you can do
that with an item in normal ListView (let's call the the ListView control provided in .NET like that) with the first item using
ImagesList. But the problems are:
- When you reorder the columns (specially the first one), the item image will still be drawn first at the left, showing glitches.
- You can make only items have an image and text in normal way (details view), but if you want to make subitems have images and texts or have images only
(like rating), you have to use the
DrawSubItem event in normal ListView. Doing so results
in a very, very slow control.
- If you want to use rating support (or capture mouse clicks over the subitems and make some calculations to do important things like change ration
for game or song) you use the
DrawSubItem event in the normal ListView but doing so will not solve the problem of 'clicking to change rating' or 'show view
rating when the cursor get over the subitem' and you have to write too much complex calculation code and the result is
a very slow control.
- The normal ListView control keeps flashing when scrolling.
- The thumbnails view (or large icons) is required in Emulators Organizer project to view games as snapshots at the control. Using the
ImagesList
will cause memory issues for a large number of images, also you'll have to mess around with item image indexes. So I had to use
the DrawItem event but
this results in a very slow view especially when you zoom in/out. The control hangs for seconds before responding...
- I can pick more reasons... but I think it's enough to make you hate the normal ListView as much as I do ;)
Background
So in version 5 of Emulators Organizer, I decided to write a ListView control that provides these features:
- Very fast draw without flashing.
- Get rid of column indexes and use another way (column ids).
- In Details view: each subitem can have image and text.
- In Details view: can add customized subitems like rating (5 starts rating like you see in Windows Media Player for songs).
- In Thumbnails view: draw thumbnails fast without hanging problems in zooming.
- In Thumbnails view: The images draw parameters (image size and coordinates) are calculated automatically and images drawn in "ratio stretch" form.
- Very easy to use and can be added in any project without problems. (no external components needed but .Net Framework)
The control is called EOListView. I renamed it to ManagedListView (or MLV) to make it sharable.
About MLV
First of all, download the 'demo project' from the top of this article. Extract the source; open the solution using Visual Studio.

You'll find two projects:
- ManagedListViewDemo: this is the demo project. We'll talk about it later in this article.
- MLV (ManagedListView): this is the managed ListView control. It includes the MLV control and its resources.
What is important is the MLV project, building it will generate the MLV.dll which includes the
ManagedListView control. The ManagedListView control is a UserControl
which inherits the UserControl class from the System.Windows.Forms namespace.
Open ManagedListView from Solution Explorer to take a look at the designer.

The white space is the ManagedListViewPanel custom control. It inherits the
Control class from System.Windows.Forms namespace. ManagedListView.cs is what
we use in the application; the ManagedListViewPanel can be used too but it requires additional
implementations that is all done in ManagedListView.cs. We'll talk about coding later in this article.
How to add MLV to a .NET project.
If we want to add the MLV to a .NET project, all we have to do is:
- Open Visual Studio
- Create a new .NET project (Windows Forms application project in this article)

- Now to add the control using the DLL only: in the ToolBox strip, right click, then choose "Choose items".

- In the Choose ToolBox Items window, click the Browse button.

- Browse for MLV.dll in your computer then click OK.
- Now you'll see ManagedListView and ManagedListViewPanel listed, make sure they are checked, then click
OK to close the window.

- After that, you'll find both ManagedListView and ManagedListViewPanel listed in the ToolBox strip, drag and drop the ManagedListView control from the
ToolBox strip into your form.

You need to do these steps just once, after that, you'll find the MLV control always listed in the ToolBox strip of Visual Studio ready to use in any project.
The
reference for MLV.dll will be added automatically in the project after dragging the control into that project. You can also use the
DLL the normal way (adding
reference, using code ....). It will be nice to use the MLV project directly from the source so you will not have to worry about build configurations.
Adding items to ManagedListView control
After we add the control to a Windows Form:

We need to add items. First, let's use the Details view. Click on the ManagedListview control in the
Forms Designer, then in Properties window, look for the ViewMode property and change it to Details.
After that, switch into the code mode for Form1. In Form1.cs, add the following code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MLV;namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ManagedListViewColumn column = new ManagedListViewColumn();
column.HeaderText = "Name"; column.ID = "name"; column.Width = 120;
managedListView1.Columns.Add(column);
column = new ManagedListViewColumn();
column.HeaderText = "Size";
column.ID = "size"; column.Width = 120;
managedListView1.Columns.Add(column);
for (int i = 0; i < 10; i++)
{
ManagedListViewItem item = new ManagedListViewItem();
ManagedListViewSubItem subitem = new ManagedListViewSubItem();
subitem.ColumnID = "name";
subitem.Text = "Item # " + (i + 1).ToString();
subitem.DrawMode = ManagedListViewItemDrawMode.Text;
item.SubItems.Add(subitem);
subitem = new ManagedListViewSubItem();
subitem.ColumnID = "size";
subitem.Text = "Item # " + (i + 1).ToString()+ " Size";
subitem.DrawMode = ManagedListViewItemDrawMode.Text;
item.SubItems.Add(subitem);
managedListView1.Items.Add(item);
}
}
}
}
Now build and run the application, then it should look like this:

Well, in the demo project, you'll find all you need to use all the control features.
The demo project
The demo project explains every feature of the control, consisting of three pages:
- The details view mode demo: this is about adding columns and items in details view. The idea is to add columns to the MLV control, then assign an
ID for
each column, after that when adding items, we add subitems to each item. The subitem index within
an item is not required or has no effect; the only
matter is the column ID. So when a subitem in any item at any index has column0 id, it will be listed for Column0. See the code of the demo project for more.

The columns get drawn with a simple rule: first column is drawn first and last column drawn last. This means the columns
are drawn as they are ordered in the
Columns collection. When you drag a column to reorder it, the column position in the collection will get changed as
the same as in the control.
- The second page is for advanced features of the details view mode:

As you see in this picture, we add images, images with texts, texts with custom fonts and colors ... and more without any problem ! Also you can
use the ImagesList property of the MLV control for images or using the
UserDraw mode to draw them as you like. You can reorder columns, sort
items via clicking on column, use mouse to change rating of any item. Please see the source for more details about using these features, all are explained at Form1.cs.
- The third page is for thumbnails view.

These picture are dragged from "Pictures" folder from user Documents. As you can see, thumbnails sizes are calculated automatically,
even you set the thumbnails size with a fixed value like (50x50), the images will drown in ratio stretch form in the rectangle 50x50. No flashing, no control hanging on zooming.
How this thing works
Let's talk about the code. As I said before, the control contains these parts:

- The
ManagedListViewPanel: which is the heart of the control. Everything goes in this component.
- The scroll bars. We use them to scroll the view port.
In ManagedListViewPanel.cs, we have two collections, Columns and
Items. Columns (ManagedListViewColumnsCollection.cs) which
is a collection of ManagedListViewColumn.cs; the columns draw only in Details view mode. The
Items collection is in ManagedListViewItemsCollection.cs
which is collection of ManagedListViewItem.cs; the items include SubItems which is a
List collection of
ManagedListViewSubItem.cs. The items are drawn in
Details view but it requires to fill subitems of each item otherwise it gets ignored. In Thumbnails view, items
are drawn without subitems; in this case the
subitems are ignored (not shown) and the image and text of the item itself are drawn.
But the question is: how does it draw?
To answer that, let's talk about the mechanism of MLV. MLV contains DrawSpace and ViewPort. The
DrawSpace (which is invisible) is the
space that can draw all the items (and columns in details view) at once. ViewSpace is the rectangle that the user can see the items through (and columns in details view).

The ViewPort position is determined via HscrollOffset and VscrollOffset. The size of
DrawSpace is calculated (or can be calculated) depending on view mode, items
count, font.. etc. HscrollOffset and VscrollOffset can be
of ranged 0 to whatever you like, however we keep the maximum value so the ViewPort stays in
the DrawSpace. The ViewPort is the ManagedListViewPanel itself!! The height is the height of that control, same for the width.
In draw operations, the control only think about what it can draw in the ViewPort. Using correct calculations depending on
parameters (items count, item height, vertical scroll value 'VscrollOffset', horizontal scroll value 'HscrollOffset', ....etc.) we can figure out what items
we should draw and the coordinates of each. This will make things faster, and I think it's better than creating elements that draw itself.
Like every custom control, we override the OnPaint method to draw:
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
if (viewMode == ManagedListViewViewMode.Details)
DrawDetailsView(pe);
else
DrawThumbailsView(pe);
if (DrawSelectRectangle)
pe.Graphics.DrawRectangle(new Pen(new SolidBrush(Color.Gray)),
SelectRectanglex, SelectRectangley,
SelectRectanglex1 - SelectRectanglex, SelectRectangley1 - SelectRectangley);
}
Now we use the DrawDetailsView method to draw the details view,
and the DrawThumbailsView method for thumbnails. Let's take a look at
the thumbnails view draw code:
private void DrawThumbnailsView(PaintEventArgs pe)
{
Size CharSize = TextRenderer.MeasureText("TEST", this.Font);
itemTextHeight = CharSize.Height * 2;
int vLines = this.Height / (spaceBetweenItemsThunmbailsView + ThumbnailsHeight + itemTextHeight);
int hLines = this.Width / (spaceBetweenItemsThunmbailsView + ThumbnailsWidth);
if (hLines == 0) hLines = 1; int passedRows = VscrollOffset / (spaceBetweenItemsThunmbailsView + ThumbnailsHeight + itemTextHeight);
int itemIndex = passedRows * hLines; if (itemIndex >= items.Count) return;
int y = 2;
for (int i = 0; i < vLines + 2; i++)
{
int x = spaceBetweenItemsThunmbailsView;
for (int j = 0; j < hLines; j++)
{
int offset = VscrollOffset % (spaceBetweenItemsThunmbailsView + ThumbnailsHeight + itemTextHeight);
if (highlightItemAsOver)
{
if (itemIndex == overItemSelectedIndex)
{
pe.Graphics.FillRectangle(Brushes.LightGray,
new Rectangle(x - 2, y - offset - 2, ThumbnailsWidth + 4, ThumbnailsHeight + itemTextHeight + 4));
}
}
if (items[itemIndex].Selected)
pe.Graphics.FillRectangle(Brushes.LightSkyBlue,
new Rectangle(x - 2, y - offset - 2, ThumbnailsWidth + 4, ThumbnailsHeight + itemTextHeight + 4));
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.Trimming = StringTrimming.EllipsisCharacter;
string textToDraw = "";
Image imageToDraw = null;
switch (items[itemIndex].DrawMode)
{
case ManagedListViewItemDrawMode.Text:
case ManagedListViewItemDrawMode.Image:
case ManagedListViewItemDrawMode.TextAndImage:
if (items[itemIndex].ImageIndex < ImagesList.Images.Count)
{
imageToDraw = ImagesList.Images[items[itemIndex].ImageIndex];
}
textToDraw = items[itemIndex].Text;
break;
case ManagedListViewItemDrawMode.UserDraw:
ManagedListViewItemDrawArgs args = new ManagedListViewItemDrawArgs(itemIndex);
if (DrawItem != null)
DrawItem(this, args);
imageToDraw = args.ImageToDraw;
textToDraw = args.TextToDraw;
break;
}
if (imageToDraw != null)
{
Size siz = CalculateStretchImageValues(imageToDraw.Width, imageToDraw.Height);
int imgX = x + (ThumbnailsWidth / 2) - (siz.Width / 2);
int imgY = (y - offset) + (ThumbnailsHeight / 2) - (siz.Height / 2);
pe.Graphics.DrawImage(imageToDraw, new Rectangle(imgX, imgY, siz.Width, siz.Height));
}
pe.Graphics.DrawString(textToDraw, this.Font, Brushes.Black,
new Rectangle(x, y + ThumbnailsHeight + 1 - offset, ThumbnailsWidth, itemTextHeight), format);
x += ThumbnailsWidth + spaceBetweenItemsThunmbailsView;
itemIndex++;
if (itemIndex == items.Count)
break;
}
y += ThumbnailsHeight + itemTextHeight + spaceBetweenItemsThunmbailsView;
if (itemIndex == items.Count)
break;
}
}
With these few lines, we were able to draw thumbnails without problems using correct calculations. Details view works the same but we make different calculations
since we have lines this time. The code is not presented here since its long. However you can check it out in the source if interesting.
The functions
Drawing is half of this control, the other half is the 'events'. So when the user moves the mouse, presses a key on the keyboard, or clicks on
an item... we should take
care of it all.
In any UserControl, you can implement them all using methods with 'On' keyword. Like
OnMouseDown, OnMouseMove, ..etc.
In MLV control, most things are done in OnMouseMove and OnMouseClick. So when the user moves the mouse over the control and the view mode is Details, we use
calculations to figure out where the cursor is, what item it's over, if the left button of the mouse is pressed what should we do ...etc.
Also the code will not be presented here because it's too long, you can check it out in the source
zip if interested.
ManagedListView.cs members
These are the members that related to MLV in ManagedListView.cs:
Properties
ManagedListViewViewMode ViewMode: the view mode, Details or Thumbnails.
ManagedListViewColumnsCollection Columns: the columns collection that drown in Details view. Remember that the first column draw first and the last column draw last.
ManagedListViewItemsCollection Items: the items collection. You may need to add subitems for each item in order to use in Details view.
bool AllowItemsDragAndDrop: You should enable this property to allow the items to be dragged in the control. Drag and drop work only after implementing the
ItemsDrag event which not rise until this property is true.
bool AllowColumnsReorder: to allow the user to reorder the columns in Details view.
bool ChangeColumnSortModeWhenClick: if enabled, the sort mode of the clicked column gets changed automatically.
int ThumbnailsHeight: the height of the thumbnail when draw in Thumbnails mode. However this value is the height of rectangle that present the
thumbnail, the image will draw in that rectangle but in ratio stretch mode to keep the aspect ratio.
int ThumbnailsWidth: the width of the thumbnail when draw in Thumbnails mode. However this value is the width of rectangle that present the
thumbnail, the image will draw in that rectangle but in ratio stretch mode to keep the aspect ratio.
int WheelScrollSpeed: how many pixels to pass when using the mouse wheel. This help when you have a large count of items so you may want to speed up the wheel scrolling.
bool DrawHighlight: if enabled, the item get highlighted with gray color each time the mouse get over it.
ImageList ImagesList: use it to draw item images in both view modes. Just like normal
ListView, you assign an ImagesList object to it then each item
provides the index of the image for it.
List<ManagedListViewItem> SelectedItems: get a list of currently selected items.
bool AllowDrop: overrides the AllowDrop property of
UserControl. Get or set if this control can accept dropped data.
Font Font: overrides the Font property of the UserControl. the font of this control. This value is important because of the draw calculations depend on font size.
Events
DrawColumn: Raised when the control need to draw a column. The column information will be sent along with this event args.
DrawItem: Raised when the control need to draw an item. The item information will be sent along with this event args. Please note that this event raised only
with Thumbnails View Mode.
DrawSubItem: Raised when the control need to draw a sub item. The sub item information will be sent along with this event args.
Note: raised only if the sub item
draw mode property equal UserDraw.
MouseOverSubItem: Raised when the mouse get over a sub item. Also useful information will be sent with this event args.
SelectedIndexChanged: raised when the user select/unselect items.
ColumnClicked: raised when the user click on column.
EnterPressed: raised when the user pressed the return key after selecting item. This event will relieve from implementing KeyDown event and checking what is
the pressed key and if one item is selected.
ItemDoubleClick: raised when the user double click on item.
SwitchToColumnsContextMenu: raised when the control needs to switch to the columns context menu. So you can use 2 context menus for MLV control, one
for columns and another for items. Using this event you can choose what one to use.
SwitchToNormalContextMenu: raised when the control needs to switch to the normal context menu.
AfterColumnResize: raised when the user finished resizing a column.
ItemsDrag: raised when the user dragged item(s). Note that this will not do drag and drop. This just give you information in the args to do drag and drop.
ViewModeChanged: raised when the user changed the view mode.
Methods
ManagedListViewItem GetItemAtCursorPoint(): Retrieve item at current cursor point.
ManagedListViewItem GetItemAtPoint(Point point): Retrieve item at point.
int GetItemIndexAtCursorPoint(): Retrieve item index at current cursor point.
int GetItemIndexAtPoint(Point point): Retrieve item index at point.
void ScrollToItem(int itemIndex): Scroll view port into item. This is very useful to jump directly to item.
void ScrollToItem(ManagedListViewItem item): Scroll view port into item.
Points of Interest
This control is a customizable list view control. You can draw columns as you like, items as you like without 'slow' problems. However that doesn't mean you get
the fastest thing but let's say it's better than normal ListView. No need to install any external components in the target pc to get it working (except the
.net framework of course). If you don't like the look of the columns for example go ahead and change the way it get drown, it's your choice ;)
Remember this is a .net library uses System.Windows.Forms namespace so it targets Windows platform only.
TODO:
- Adding support for more view modes.
- Find a way to add columns and item directly using Visual Studio forms designer.
- Fixing bugs if found.
FAQ:
Start programming at 2008, my first successful project was AHD Subtitles Maker "ahd-subtitles-maker.webnode.com".
I'm studying Physics in Hims university.
Programming is my favorite hobby that fill my empty times.