listview hittesting不正确

时间:2015-03-12 15:03:25

标签: listview winapi visual-c++ hittest

引言:

我正在尝试确定用户是否点击了列表视图上方或下方/下方。

Listview处于报告模式,具有扩展样式的完整行选择和网格线。

问题:

根据LVHITTESTINFO的文档,我无法获得正确的结果。

我努力解决问题:

我的复选框的标题与我主窗口中LVHITTESTINFO中的值相同。

这些是LVHT_ABOVE,LVHT_BELOW,LVHT_NOWHERE,LVHT_TOLEFT,LVHT_TORIGHT和LVHT_ONITEM。

我的目标是检查与hittesting返回的结果具有相同标题的标题。

我已经在主窗口中捕获了鼠标以响应WM_LBUTTONDOWN并在WM_LBUTTONUP处理程序中进行了测试。

这是我编写可在此处发布的最小SSCCE /代码段的最简单方法。

以下是相关代码:

case WM_LBUTTONDOWN:
    // reset checkboxes
    CheckDlgButton(hWnd, 3000, BST_UNCHECKED);
    CheckDlgButton(hWnd, 3100, BST_UNCHECKED);
    CheckDlgButton(hWnd, 3200, BST_UNCHECKED);
    CheckDlgButton(hWnd, 3300, BST_UNCHECKED);
    CheckDlgButton(hWnd, 3400, BST_UNCHECKED);
    CheckDlgButton(hWnd, 3500, BST_UNCHECKED);
    // capture the mouse
    SetCapture(hWnd);
    break;
case WM_LBUTTONUP:
{
    // extract coordinates
    POINT pt = { 0 };
    pt.x = GET_X_LPARAM(lParam);
    pt.y = GET_Y_LPARAM(lParam);
    // do the hittesting
    ClientToScreen(hWnd, &pt);
    ScreenToClient(GetDlgItem(hWnd, 2000), &pt);

    LVHITTESTINFO lvhti = { 0 };
    lvhti.pt = pt;

    ListView_HitTest(GetDlgItem(hWnd, 2000), &lvhti);
    // check appropriate checkboxes
    if ((lvhti.flags & LVHT_ABOVE) == LVHT_ABOVE)
        CheckDlgButton(hWnd, 3000, BST_CHECKED);
    if ((lvhti.flags & LVHT_BELOW) == LVHT_BELOW)
        CheckDlgButton(hWnd, 3100, BST_CHECKED);
    if ((lvhti.flags & LVHT_NOWHERE) == LVHT_NOWHERE)
        CheckDlgButton(hWnd, 3200, BST_CHECKED);
    if ((lvhti.flags & LVHT_ONITEM) == LVHT_ONITEM)
        CheckDlgButton(hWnd, 3300, BST_CHECKED);
    if ((lvhti.flags & LVHT_TOLEFT) == LVHT_TOLEFT)
        CheckDlgButton(hWnd, 3400, BST_CHECKED);
    if ((lvhti.flags & LVHT_TORIGHT) == LVHT_TORIGHT)
        CheckDlgButton(hWnd, 3500, BST_CHECKED);
    // release mouse capture
    ReleaseCapture();
}
    break;

测试原则:

按以下方式进行测试:单击主窗口客户区,按住鼠标左键,然后将光标拖到项目/上方(或下方)列表视图上。然后我释放调用WM_LBUTTONUP消息的按钮,并检查相应的复选框。

测试结果:

在Windows 7上测试

  • 在项目的第一个子项目上释放鼠标左键时,不会检查任何内容。
  • 当在项目的第二个(第三个等)子项目复选框上释放鼠标左键时,会检查LVHT_ABOVE和LHVT_ONITEM。
  • 当释放listview之外的按钮时,我正确地获得了LVHT_NOWHERE。
  • 当在滚动条上释放按钮时,我得到LVHT_NOWHERE。
  • 当释放上面的标题控件的第一列时,我没有检查任何内容,但对于其他列,我得到了LVHT_ONITEM和LVHT_ABOVE。

在Windows XP上测试

  • 在项目的第一个子项目上释放鼠标左键时,不会检查任何内容。
  • 当在项目的第二个(第三个等)子项目复选框上释放鼠标左键时,会检查LVHT_ABOVE和LHVT_ONITEM。
  • 当释放listview之外的按钮时,我得到LVHT_ABOVE / LVHT_BELOW和LVHT_TOLEFT / LVHT_TORIGHT的正确组合。
  • 当在滚动条上释放按钮时,我得到垂直的LVHT_TORIGHT,以及水平滚动条的LVHT_BELOW。
  • 当释放上面的标题控件时,我得到不一致的行为:当在标题项的最顶部释放时,我得到LVHT_NOWHERE,但在其他情况下,它的行为与我上面描述的相同(对于Windows 7)。

重要:

  • 通过互联网搜索,我发现LVHT_ABOVE和LVHT_ONITEMSTATEICON具有相同的值。这意味着我必须解释测试结果,其中我将LVHT_ABOVE和LVHT_ONITEM作为LVHT_ONITEM。
  • 在调试时,我发现在第一列上方释放鼠标按钮或第一项时给出了掩码4,这意味着我得到的是LVHT_ONITEMLABEL而不是LVHTI_ONITEM。

问题:

如何调整上述代码以获得正确的测试结果?

如果我的问题的解决方案很复杂并且需要太多的空间和精力来发布,我会对指示和指南感到满意,或者至少有一些链接指向我正确的方向。

如果需要进一步的信息,我会更新我的帖子。

1 个答案:

答案 0 :(得分:1)

由于LVHT_ONITEMSTATEICONLVHT_ABOVE具有相同的值,因此您必须查看请求的y坐标以区分它们。 LVHT_ABOVE表示命中结果位于客户区上方,因此只能在y坐标小于零时设置,而LVHT_ONITEMSTATEICON只能在命中结果打开时设置客户区内的图标。

LVHT_ONITEM...标志仅在LVHT_ONITEM不存在时才有意义。 LVHT_ONITEM表示匹配结果是整个项目的整体,而LVHT_ONITEM...表示匹配结果是项目的特定部分。

试试这个:

if (((lvhti.flags & LVHT_ABOVE) == LVHT_ABOVE) && (pt.y < 0))
    // above the client area ...

...

if ((lvhti.flags & LVHT_ONITEM) == LVHT_ONITEM)
    // on an item as a whole...
else
{
    if ((lvhti.flags & LVHT_ONITEMICON) == LVHT_ONITEMICON)
        // on an item's icon...
    if ((lvhti.flags & LVHT_ONITEMLABEL) == LVHT_ONITEMLABEL)
        // on an item's text...
    if ((lvhti.flags & LVHT_ONITEMSTATEICON) == LVHT_ONITEMSTATEICON)
        // on an item's state icon...
}

另外,不要忘记查看ListView_HitTest()和/或LVHITTESTINFO::iItem字段的返回值。它们会告诉您列表项索引,如果命中结果不在项目上,则为-1。