Ever wanted to display images in the header of a ListView control in the .NET CF application? Sure we can do that. The Windows CE SDK documentation reveals that practically all messages that are available on a desktop are supported by the ListView. In particular we should be interested in HDM_SETITEM message which supposed to be send to the ListView header with the pointer to the HDITEM structure. Fifteen minutes later I had this class ready:
public class ListViewHeaderIcon
{
IntPtr hWndHeader;
ListView listView;
public ListViewHeaderIcon(ListView listView)
{
this.listView = listView;
//Get HWND of the header
hWndHeader = SendMessage(listView.Handle, LVM_GETHEADER,
IntPtr.Zero, IntPtr.Zero);
}
public void SetHeaderImage(int columnIndex, int imageIndex, bool placeOnRight)
{
//Make sure that we have handle of the header
if (hWndHeader == IntPtr.Zero)
throw new Exception("Handle of header does not exist.");
//Create HDITEM and populate with values
HDITEM hdItem = new HDITEM();
hdItem.mask = HDI_TEXT | HDI_IMAGE | HDI_FORMAT;
hdItem.fmt = HDF_STRING | HDF_IMAGE | (placeOnRight ? HDF_BITMAP_ON_RIGHT : 0);
hdItem.iImage = imageIndex;
hdItem.pszText = Marshal.StringToBSTR(listView.Columns[columnIndex].Text);
//Sending HDM_SETITEM message
SendMessage(hWndHeader, HDM_SETITEM, (IntPtr)columnIndex, ref hdItem);
}
#region P/Invokes
const uint HDM_FIRST = 0x1200;
const uint HDM_SETITEM = HDM_FIRST + 12;
const uint HDI_FORMAT = 0x0004;
const uint HDI_TEXT = 0x0002;
const uint HDI_BITMAP = 0x0010;
const uint HDI_IMAGE = 0x0020;
const int HDF_STRING = 0x4000;
const int HDF_BITMAP = 0x2000;
const int HDF_IMAGE = 0x0800;
const int HDF_BITMAP_ON_RIGHT = 0x1000;
const uint LVM_FIRST = 0x1000;
const uint LVM_GETHEADER = LVM_FIRST + 31;
[DllImport("coredll.dll")]
static extern IntPtr SendMessage(IntPtr hWnd,
uint uMsg, IntPtr wParam, ref HDITEM lParam);
[DllImport("coredll.dll")]
static extern IntPtr SendMessage(IntPtr hWnd,
uint uMsg, IntPtr wParam, IntPtr lParam);
struct HDITEM
{
public uint mask;
public int cxy;
public IntPtr pszText;
public IntPtr hbm;
public int cchTextMax;
public int fmt;
public int lParam;
public int iImage;
public int iOrder;
public uint type;
public IntPtr pvFilter;
}
#endregion
}
As you can see, the SetHeaderImage accepts the index of the column, index of the image from the ImageList that is ssigned to the SmallImageList property of the ListView and a boolean that you can use to show the image left or right justified.
In order to use this class, drop ther ListView on your form, populate it with some data. Don't forget to add the ImageList with some images and assign it to the SmallImageList property:
ListViewHeaderIcon header = new ListViewHeaderIcon(listView1);
for (int i = 0; i < listView1.Columns.Count; i++)
{
header.SetHeaderImage(i, i, false);
}
Here is a screen shot of the sample: