ListView HitTest implementation

In my last blog entry, I showed a workaround for getting mouse events for the CF ListView.  Since I can’t leave well enough alone, I decided I’d actually try implementing the HitTest method (now that we have a valid x,y click coordinate).  Here’s the result:

namespace OpenNETCF.Windows.Forms
  public enum ListViewHitTestLocations
    None = 0x01,
    Image = 0x02,
    Label = 0x04,
    StateImage = 0x08,

    AboveClientArea = 0x08,
    BelowClientArea = 0x10,
    RightOfClientArea = 0x20,
    LeftOfClientArea = 0x40,

  public class ListViewHitTestInfo
    public ListViewItem Item { get; set; }
    public ListViewHitTestLocations Location { get; set; }
    public ListViewItem.ListViewSubItem SubItem { get; set; }

namespace OpenNETCF.Core
  using OpenNETCF.Windows.Forms;
  using System.Runtime.InteropServices;
  using System.Diagnostics;

  public static partial class Extensions
    private const int LVM_FIRST = 0x1000;
    private const int LVM_GETCOUNTPERPAGE = LVM_FIRST + 40;
    private const int LVM_SUBITEMHITTEST = LVM_FIRST + 57;

    private struct LVHITTESTINFO
      public int x;
      public int y;
      public ListViewHitTestLocations flags;
      public int iItem;
      public int iSubItem;

    public static int GetVisibleRowCount(this ListView lv)
      return Win32Window.SendMessage(lv.Handle, LVM_GETCOUNTPERPAGE, 0, 0).ToInt32();

    public static ListViewHitTestInfo HitTest(this ListView lv, int x, int y)
      info.x = x;
      info.y = y;
      GCHandle pInfo = GCHandle.Alloc(info, GCHandleType.Pinned);
        Win32Window.SendMessage(lv.Handle, LVM_SUBITEMHITTEST, 0, pInfo.AddrOfPinnedObject());
        LVHITTESTINFO result = (LVHITTESTINFO)pInfo.Target;
        ListViewHitTestInfo lvhti = new ListViewHitTestInfo();

        lvhti.Location = result.flags;
        switch (lvhti.Location)
          case ListViewHitTestLocations.Image:
          case ListViewHitTestLocations.Label:
          case ListViewHitTestLocations.StateImage:
            lvhti.Item = lv.Items[result.iItem];
            lvhti.SubItem = lvhti.Item.SubItems[result.iSubItem];
        return lvhti;

    public static ListViewHitTestInfo HitTest(this ListView lv, Point point)
      return lv.HitTest(point.X, point.Y);

And usage looks like this:

public partial class Foo : Form
  public Foo()
    listView.Items.Add(new ListViewItem(new string[] { “Item A”, “Sub A1”, “Sub A2”, “Sub A3” }));
    listView.Items.Add(new ListViewItem(new string[] { “Item B”, “Sub B1”, “Sub B2”, “Sub B3” }));
    listView.Items.Add(new ListViewItem(new string[] { “Item C”, “Sub C1”, “Sub C2”, “Sub C3” }));
    listView.Items.Add(new ListViewItem(new string[] { “Item D”, “Sub D1”, “Sub D2”, “Sub D3” }));
    listView.Items.Add(new ListViewItem(new string[] { “Item E”, “Sub E1”, “Sub E2”, “Sub E3” }));

    ClickFilter filter = new ClickFilter(listView);
    filter.MouseDown += new MouseEventHandler(filter_MouseDown);


  void filter_MouseDown(object sender, MouseEventArgs e)
    ListView lv = sender as ListView;
    ListViewHitTestInfo hti = lv.HitTest(e.X, e.Y);

    if (hti.Item != null)
      Debug.WriteLine(string.Format(“Item: {0}, SubItem: {1}”, hti.Item.Text, hti.SubItem.Text));

All of this will end up in SDF v. Next, but why do we have to implement fundamental things like this that should already be there?  It was excusable to be missing in v 1.0.  Maybe even in v 2.0, but in 3.5?  Really?


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s