C
chris s.
I'm trying to get DrawItem and MeasureItem working on a ListView, with
a view to drawing items in LargeIcon view. I've managed to get
DrawItem working okay, using the code below. My problem is with the
MeasureItem event. The event is only ever called once, and most
annoyingly, are never called when the view isn't the details view.
I've tinkered about with the owner-drawn listview projects on
codeproject.com, and have discovered you need to handle ITEMPREPAINT
for non-details view listviews. The problem with this is, MeasureItem
isn't supported (or rather, I don't know how to handle it), and so you
end up having to handle or Mouse events yourself.
This removes the point of the control being derived from ListView - to
utilise all the functionality a listview has in.
I also tried creating ListViewItemCollections and ListViewItems
myself, but gave up with that as I was getting nowhere.
Hopefully you've read to this stage and haven't dropped off. If anyone
can suggest controls (commercial/free), websites, FAQs etc. that'd be
appreciated.
Cheers
...................................................
The code:
<pre>
// Majority of this is a merger of UtilityLibrary and google groups
examples
public class MyListView : ListView
{
private struct MEASUREITEMSTRUCT
{
public int CtlType;
public int CtlID;
public int itemID;
public int itemWidth;
public int itemHeight;
public IntPtr itemData;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
private struct DrawItemStruct
{
public int ctlType;
public int ctlID;
public int itemID;
public int itemAction;
public int itemState;
public IntPtr hWndItem;
public IntPtr hDC;
public RECT rcItem;
public IntPtr itemData;
}
private enum ReflectedMessages
{
OCM__BASE = (0x0400 + 0x1c00),
OCM_DRAWITEM = (OCM__BASE + 0x002B),
}
public event System.Windows.Forms.DrawItemEventHandler DrawItem;
public event MeasureItemEventHandler MeasureItem;
public const int LVS_OWNERDRAWFIXED = 0x0400;
private DrawMode drawMode;
public MyListView()
{
this.drawMode = DrawMode.Normal;
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style |= (drawMode != DrawMode.Normal) ? LVS_OWNERDRAWFIXED : 0;
return cp;
}
}
public virtual DrawMode DrawMode
{
get { return drawMode; }
set { drawMode = value; }
}
protected virtual void
OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
{
}
public virtual void OnMeasureItem(MeasureItemEventArgs e)
{
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case (int)ReflectedMessages.OCM_DRAWITEM:
{
DrawItemStruct dis =
(DrawItemStruct)m.GetLParam(typeof(DrawItemStruct));
Graphics graph = Graphics.FromHdc(dis.hDC);
Rectangle rect = new
Rectangle(dis.rcItem.left,dis.rcItem.top,dis.rcItem.right -
dis.rcItem.left,dis.rcItem.bottom - dis.rcItem.top);
int index = dis.itemID;
DrawItemState state = DrawItemState.None;
System.Windows.Forms.DrawItemEventArgs e = new
System.Windows.Forms.DrawItemEventArgs(graph, Font, rect, index,
state, ForeColor, BackColor);
if ( this.DrawItem != null )
{
this.DrawItem(this,e);
}
OnDrawItem(e);
graph.Dispose();
break;
}
case 8236:
this.WmReflectMeasureItem(ref m);
break;
}
}
private void WmReflectMeasureItem(ref Message m)
{
Graphics graphics1;
MeasureItemEventArgs args1;
MEASUREITEMSTRUCT measureitemstruct1 = (MEASUREITEMSTRUCT)
m.GetLParam( typeof(MEASUREITEMSTRUCT) );
if ((this.drawMode == DrawMode.OwnerDrawVariable) &&
(measureitemstruct1.itemID >= 0))
{
graphics1 = Graphics.FromHwnd(this.Handle);
args1 = new MeasureItemEventArgs(graphics1,
measureitemstruct1.itemID, 20);
try
{
if ( this.MeasureItem != null )
{
this.MeasureItem(this,args1);
}
this.OnMeasureItem(args1);
measureitemstruct1.itemHeight = args1.ItemHeight;
}
finally
{
graphics1.Dispose();
}
}
measureitemstruct1.itemHeight = 20;//this.ItemHeight;
Marshal.StructureToPtr(measureitemstruct1, m.LParam, false);
m.Result = ((IntPtr) 1);
}
}
</pre>
a view to drawing items in LargeIcon view. I've managed to get
DrawItem working okay, using the code below. My problem is with the
MeasureItem event. The event is only ever called once, and most
annoyingly, are never called when the view isn't the details view.
I've tinkered about with the owner-drawn listview projects on
codeproject.com, and have discovered you need to handle ITEMPREPAINT
for non-details view listviews. The problem with this is, MeasureItem
isn't supported (or rather, I don't know how to handle it), and so you
end up having to handle or Mouse events yourself.
This removes the point of the control being derived from ListView - to
utilise all the functionality a listview has in.
I also tried creating ListViewItemCollections and ListViewItems
myself, but gave up with that as I was getting nowhere.
Hopefully you've read to this stage and haven't dropped off. If anyone
can suggest controls (commercial/free), websites, FAQs etc. that'd be
appreciated.
Cheers
...................................................
The code:
<pre>
// Majority of this is a merger of UtilityLibrary and google groups
examples
public class MyListView : ListView
{
private struct MEASUREITEMSTRUCT
{
public int CtlType;
public int CtlID;
public int itemID;
public int itemWidth;
public int itemHeight;
public IntPtr itemData;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
private struct DrawItemStruct
{
public int ctlType;
public int ctlID;
public int itemID;
public int itemAction;
public int itemState;
public IntPtr hWndItem;
public IntPtr hDC;
public RECT rcItem;
public IntPtr itemData;
}
private enum ReflectedMessages
{
OCM__BASE = (0x0400 + 0x1c00),
OCM_DRAWITEM = (OCM__BASE + 0x002B),
}
public event System.Windows.Forms.DrawItemEventHandler DrawItem;
public event MeasureItemEventHandler MeasureItem;
public const int LVS_OWNERDRAWFIXED = 0x0400;
private DrawMode drawMode;
public MyListView()
{
this.drawMode = DrawMode.Normal;
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style |= (drawMode != DrawMode.Normal) ? LVS_OWNERDRAWFIXED : 0;
return cp;
}
}
public virtual DrawMode DrawMode
{
get { return drawMode; }
set { drawMode = value; }
}
protected virtual void
OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
{
}
public virtual void OnMeasureItem(MeasureItemEventArgs e)
{
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case (int)ReflectedMessages.OCM_DRAWITEM:
{
DrawItemStruct dis =
(DrawItemStruct)m.GetLParam(typeof(DrawItemStruct));
Graphics graph = Graphics.FromHdc(dis.hDC);
Rectangle rect = new
Rectangle(dis.rcItem.left,dis.rcItem.top,dis.rcItem.right -
dis.rcItem.left,dis.rcItem.bottom - dis.rcItem.top);
int index = dis.itemID;
DrawItemState state = DrawItemState.None;
System.Windows.Forms.DrawItemEventArgs e = new
System.Windows.Forms.DrawItemEventArgs(graph, Font, rect, index,
state, ForeColor, BackColor);
if ( this.DrawItem != null )
{
this.DrawItem(this,e);
}
OnDrawItem(e);
graph.Dispose();
break;
}
case 8236:
this.WmReflectMeasureItem(ref m);
break;
}
}
private void WmReflectMeasureItem(ref Message m)
{
Graphics graphics1;
MeasureItemEventArgs args1;
MEASUREITEMSTRUCT measureitemstruct1 = (MEASUREITEMSTRUCT)
m.GetLParam( typeof(MEASUREITEMSTRUCT) );
if ((this.drawMode == DrawMode.OwnerDrawVariable) &&
(measureitemstruct1.itemID >= 0))
{
graphics1 = Graphics.FromHwnd(this.Handle);
args1 = new MeasureItemEventArgs(graphics1,
measureitemstruct1.itemID, 20);
try
{
if ( this.MeasureItem != null )
{
this.MeasureItem(this,args1);
}
this.OnMeasureItem(args1);
measureitemstruct1.itemHeight = args1.ItemHeight;
}
finally
{
graphics1.Dispose();
}
}
measureitemstruct1.itemHeight = 20;//this.ItemHeight;
Marshal.StructureToPtr(measureitemstruct1, m.LParam, false);
m.Result = ((IntPtr) 1);
}
}
</pre>