#region WIN32STDSTUFF [StructLayoutAttribute(LayoutKind.Sequential)] public struct RECT { public int left; public int top; public int right; public int bottom; } #endregion #region NM_CustomStructures [StructLayoutAttribute(LayoutKind.Sequential)] public struct NMHDR { public IntPtr hwndFrom; public int idFrom; public int code; } [StructLayoutAttribute(LayoutKind.Sequential)] public struct NMCUSTOMDRAW { public NMHDR hdr; public int dwDrawStage; public IntPtr hdc; public RECT rc; public uint dwItemSpec; public uint uItemState; public IntPtr lItemlParam; } [StructLayoutAttribute(LayoutKind.Sequential)] public struct NMLVCUSTOMDRAW { public NMCUSTOMDRAW nmcd; public uint clrText; public uint clrTextBk; public int iSubItem; } public enum HeaderControlMessages : int { HDM_FIRST = 0x1200, HDM_GETITEMRECT = (HDM_FIRST + 7), HDM_HITTEST = (HDM_FIRST + 6), HDM_SETIMAGELIST = (HDM_FIRST + 8), HDM_GETITEMW = (HDM_FIRST + 11), HDM_ORDERTOINDEX = (HDM_FIRST + 15) } #endregion #region Win32Messages public enum Msg { WM_NULL = 0x0000, WM_ERASEBKGND = 0x0014, WM_NOTIFY = 0x004E, WM_COMMAND = 0x0111, WM_USER = 0x0400 } public enum NotififyMessages { NM_FIRST = (0-0), NM_CUSTOMDRAW = (NM_FIRST-12), NM_NCHITTEST = (NM_FIRST-14) } public enum ReflectedMessages { OCM__BASE = (Msg.WM_USER+0x1c00), OCM_COMMAND = (OCM__BASE + Msg.WM_COMMAND), OCM_NOTIFY = (OCM__BASE + Msg.WM_NOTIFY) } public enum OwnerDrawnReturnFlags { CDRF_DODEFAULT = 0x00000000, CDRF_NEWFONT = 0x00000002, CDRF_SKIPDEFAULT = 0x00000004, CDRF_NOTIFYPOSTPAINT = 0x00000010, CDRF_NOTIFYITEMDRAW = 0x00000020, CDRF_NOTIFYSUBITEMDRAW = 0x00000020, CDRF_NOTIFYPOSTERASE = 0x00000040 } public enum OwnerDrawnStateFlags : uint { CDIS_SELECTED = 0x0001, CDIS_GRAYED = 0x0002, CDIS_DISABLED = 0x0004, CDIS_CHECKED = 0x0008, CDIS_FOCUS = 0x0010, CDIS_DEFAULT = 0x0020, CDIS_HOT = 0x0040, CDIS_MARKED = 0x0080, CDIS_INDETERMINATE = 0x0100 } public enum OwnerDrawnDrawStateFlags { CDDS_PREPAINT = 0x00000001, CDDS_POSTPAINT = 0x00000002, CDDS_PREERASE = 0x00000003, CDDS_POSTERASE = 0x00000004, CDDS_ITEM = 0x00010000, CDDS_ITEMPREPAINT = (CDDS_ITEM | CDDS_PREPAINT), CDDS_ITEMPOSTPAINT = (CDDS_ITEM | CDDS_POSTPAINT), CDDS_ITEMPREERASE = (CDDS_ITEM | CDDS_PREERASE), CDDS_ITEMPOSTERASE = (CDDS_ITEM | CDDS_POSTERASE), CDDS_SUBITEM = 0x00020000 } #endregion public class OwnerDrawnListView : System.Windows.Forms.ListView { [DllImportAttribute("user32.dll", CharSet=CharSet.Auto)] public static extern int SendMessage(IntPtr hWnd, HeaderControlMessages msg, int wParam, int lParam); public OwnerDrawnListView() { SetStyle(ControlStyles.UserPaint, true); FullRowSelect = true; View = View.Details; } protected override void WndProc(ref Message newmsg) { base.WndProc(ref newmsg); switch (newmsg.Msg) { // case (int)Msg.WM_ERASEBKGND: // IntPtr hDC = (IntPtr)newmsg.WParam; // PaintBackground(hDC); // break; // WM_NOTIFY message come from the header. case (int)Msg.WM_NOTIFY: NMHDR nm1 = (NMHDR)newmsg.GetLParam(typeof(NMHDR)); switch(nm1.code) { case (int)NotififyMessages.NM_CUSTOMDRAW: // HeaderOwnerDraw(ref newmsg); break; default: break; } break; // Messages from the list itself are reflected using OCM_NOTIFY. case (int)ReflectedMessages.OCM_NOTIFY: NMHDR nm2 = (NMHDR) newmsg.GetLParam(typeof(NMHDR)); switch (nm2.code) { case (int)NotififyMessages.NM_CUSTOMDRAW: // ListOwnerDraw(ref newmsg); break; default: break; } break; // Default default: break; } } private bool ListOwnerDraw(ref Message m) { m.Result = (IntPtr)OwnerDrawnReturnFlags.CDRF_DODEFAULT; NMCUSTOMDRAW nmcd = (NMCUSTOMDRAW)m.GetLParam(typeof(NMCUSTOMDRAW)); IntPtr thisHandle = Handle; if ( nmcd.hdr.hwndFrom != Handle) return false; switch (nmcd.dwDrawStage) { case (int)OwnerDrawnDrawStateFlags.CDDS_PREPAINT: // Ask for Item painting notifications m.Result = (IntPtr)OwnerDrawnReturnFlags.CDRF_NOTIFYITEMDRAW; break; case (int)OwnerDrawnDrawStateFlags.CDDS_ITEMPREPAINT: m.Result = (IntPtr)OwnerDrawnReturnFlags.CDRF_NOTIFYSUBITEMDRAW; break; case (int)(OwnerDrawnDrawStateFlags.CDDS_ITEMPREPAINT | OwnerDrawnDrawStateFlags.CDDS_SUBITEM): // Draw our new custom background. OwnerDrawListItem(ref m); break; default: break; } return false; } void OwnerDrawListItem(ref Message m) { NMLVCUSTOMDRAW lvcd = (NMLVCUSTOMDRAW)m.GetLParam(typeof(NMLVCUSTOMDRAW)); // Return a structure describing the outcome of the owner drawing process. // // -> Customer drawing goes here <- // Marshal.StructureToPtr(lvcd, m.LParam, true); m.Result = (IntPtr)OwnerDrawnReturnFlags.CDRF_SKIPDEFAULT; }As you can see the owner drawing technique allows you the ultimate freedom when designing UI features but it does take some effort.