Introduction
For those of you who are making the move from C++ to C#, you may be looking for a HitTest
method such as the one commonly found around the web for C++. A HitTest
method is a method used on C#'s ListView
control (CListCtrl
in C++/MFC) that when the mouse is clicked on the control, gives the programmer the row and column numbers where the mouse was clicked. At first, this was very basic. But, as I got responses from it for certain cases that would not work, the code became a littler more involved. Anyway, here is the latest.
Using the code
Simply add a ListView
control to a Form
, add some columns, and be sure to...
- Set
ListView
"View" property to Details
(HitTest pointless otherwise).
- Set
ListView
"Full Row Select" property to true
(If not, HitTest fails on all but first column).
- Add some rows to the control (HitTest returns
false
if clicked on non-existent row).
- You may choose to enable "Allow Column Reordering" or not. The code works for both.
For the HitTest
to work properly, we must add a few other things to your class. First, we must add a structure to our class. Somewhere inside your class definition, add the code below to be able to create "C++ - like" RECT
objects.
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
Next, we must add some messaging methods. We must add a SendMessage
method that will allow us to get each subitem's bounding rectangle. A different parameterized SendMessage
method must be added to allow us to get the column order array for the case that the columns in the ListView
have been reordered by the user.
[DllImport("User32.dll")]
private static extern int SendMessage(IntPtr hWnd,
int msg , int wParam , ref RECT lParam);
[DllImport("User32.dll")]
private static extern int SendMessage(IntPtr hWnd,
int msg , int wParam , int[] lParam);
Lastly, the HitTest
method is as follows...
private bool HitTest(Point hitPoint, out int row, out int column)
{
const int LVM_GETSUBITEMRECT = 0x1038;
const int LVM_COLUMNORDERARRAY = 0x103B;
const int LVIR_BOUNDS = 0;
bool retval = false;
RECT subItemRect;
row = column = -1;
ListViewItem item = m_lvListView.GetItemAt(hitPoint.X, hitPoint.Y);
if(item != null && m_lvListView.Columns.Count > 1)
{
if(m_lvListView.AllowColumnReorder)
{
int[] columnOrder = new int[m_lvListView.Columns.Count];
if(SendMessage(m_lvListView.Handle,
LVM_COLUMNORDERARRAY, m_lvListView.Columns.Count,
columnOrder) != 0)
{
int i;
RECT[] subItemRects = new RECT[m_lvListView.Columns.Count];
for(i = 1; i < m_lvListView.Columns.Count; i++)
{
subItemRects[columnOrder[i]].top = i;
subItemRects[columnOrder[i]].left = LVIR_BOUNDS;
SendMessage(m_lvListView.Handle,
LVM_GETSUBITEMRECT, item.Index,
ref subItemRects[columnOrder[i]]);
}
for(i = 0; i < columnOrder.Length; i++)
if(columnOrder[i] == 0)
break;
if(i > 0)
{
subItemRects[i].left = subItemRects[i-1].right;
subItemRects[i].right = subItemRects[i].left
+ m_lvListView.Columns[0].Width;
}
else
{
subItemRects[0].left = subItemRects[1].left -
m_lvListView.Columns[0].Width;
subItemRects[0].right = subItemRects[1].left;
}
for(int index = 0; index < subItemRects.Length; index++)
{
if(hitPoint.X >= subItemRects[index].left &
hitPoint.X <= subItemRects[index].right)
{
row = item.Index;
column = columnOrder[index];
retval = true;
break;
}
}
}
}
else
{
for(int index = 1; index <= m_lvListView.Columns.Count-1;
index++)
{
subItemRect = new RECT();
subItemRect.top = index;
subItemRect.left = LVIR_BOUNDS;
if(SendMessage(m_lvListView.Handle,
LVM_GETSUBITEMRECT, item.Index, ref subItemRect) != 0)
{
if(hitPoint.X < subItemRect.left)
{
row = item.Index;
column = 0;
retval = true;
break;
}
if(hitPoint.X >= subItemRect.left & hitPoint.X <=
subItemRect.right)
{
row = item.Index;
column = index;
retval = true;
break;
}
}
}
}
}
return retval;
}
History
HitTest test application 1.0.0.
HitTest test application 1.0.1.
- Use "out" parameters instead of "ref". More suitable.
HitTest test application 2.0.0.
- Now gives the correct column number if the
ListView
control is horizontally scrolled.
- Now allows the programmer to "Allow Column Reordering" and it will still work and give the correct initial column number.