使用LVM_SORTITEMSEX

时间:2015-06-06 02:58:34

标签: c++ c listview winapi

引言:

为了使这篇文章尽可能简短,我只想说我需要将列表视图中的所有选定项目移动到某个(未选中)项目下面。

浏览listview文档我遇到了LVM_SORTITEMSEX message

问题:

如何使用上述信息来实现我的目标。

我努力解决这个问题:

到目前为止,通过使用此消息,我能够将所有选定的项目移动到列表的底部 - > listview的排序方式是未选择的项目位于选定的项目之前。

我无法弄清楚如何在特定项目下方移动所选项目。

下面是我得到的图像,以及我想要实现的目标:

enter image description here

左图显示了我使用下面提交的代码时得到的结果,而右图显示了我的目标结果。

以下是相关的代码段:

// compare function -> see the documentation
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM hwnd)
{
    LVITEM lvi1 = { 0 }, lvi2 = { 0 };
    // get selected state of the first item
    lvi1.iItem = (int)lParam1;
    lvi1.iSubItem = 0;
    lvi1.mask = LVIF_STATE;
    lvi1.stateMask = LVIS_SELECTED;
    ListView_GetItem((HWND)hwnd, &lvi1);
    // get selected state of the second item
    lvi2.iItem = (int)lParam2;
    lvi2.iSubItem = 0;
    lvi2.mask = LVIF_STATE;
    lvi2.stateMask = LVIS_SELECTED;
    ListView_GetItem((HWND)hwnd, &lvi2);
    // if first is selected and second is not selected, swap them
    if ((lvi1.state & LVIS_SELECTED) && (0 == (lvi2.state & LVIS_SELECTED)))
        return 1;

    return 0;
}

// somewhere in code, on button click for example
ListView_SortItemsEx(hwndListView, CompareFunc, hwndListView);

我已将listview句柄作为ListView_SortItemsEx的第三个参数传递给我,因此我可以在ListView_GetItem中使用CompareFunc

2 个答案:

答案 0 :(得分:1)

如果我理解正确,你想通过拖放重新排列项目,并且你想要排序功能。如果要在sort proc中完成,这可能会变得复杂。另一种解决方案是首先找到安排。

  • 使用向量在" redMark"
  • 之前存储项目
  • 添加在" redMark"
  • 之后显示的所选项目
  • 添加在" redMark"
  • 之后显示的未选择的项目
  • 将此订单保存到LVITEM::lParam
  • 致电ListView_SortItems(不是ListView_SortItemsEx

唯一的问题是,出于其他原因可能会使用lParam。我们必须保存lParam,然后在排序完成后将其恢复。

如果ListView具有LVS_SHOWSELALWAYS,那么它也会更好。

注意,此方法会在" redMark"之前移动项目。在您的示例中,您应该设置redMark = 3以在"项目60"

之前移动选择
int CALLBACK CompareFunc(LPARAM lp1, LPARAM lp2, LPARAM)
{
    return lp1 > lp2;
}

void sort()
{
    int redMark = 3;
    int count = ListView_GetItemCount(hwndListView);

    std::vector<int> order;
    std::vector<LPARAM> saveLParam(count);

    //add everything before redMark
    for (int i = 0; i < redMark; i++)
        order.push_back(i);

    //add highlighted items
    for (int i = redMark; i < count; i++)
        if (ListView_GetItemState(hwndListView, i, LVIS_SELECTED))
            order.push_back(i);

    //add the rest
    for (int i = redMark; i < count; i++)
        if (!ListView_GetItemState(hwndListView, i, LVIS_SELECTED))
            order.push_back(i);

    if (order.size() != count)
    {
        assert(0);
        return;
    }

    //set lParam
    for (int i = 0; i < count; i++)
    {
        LVITEM item = { 0 };
        item.iItem = order[i];
        item.mask = LVIF_PARAM;

        //save old LParam value
        ListView_GetItem(hwndListView, &item);
        saveLParam[i] = item.lParam;

        //set new lParam
        item.lParam = i;
        ListView_SetItem(hwndListView, &item);
    }

    ListView_SortItems(hwndListView, CompareFunc, 0);

    //restore old lParam
    for (int i = 0; i < count; i++)
    {
        LVITEM item = { 0 };
        item.iItem = order[i];
        item.mask = LVIF_PARAM;
        item.lParam = saveLParam[order[i]];
        ListView_SetItem(hwndListView, &item);
    }

    ::SetFocus(hwndListView);
}

答案 1 :(得分:1)

<强>简介

  • 对于LVM_SORTITEMSEX,所有商品必须具有唯一的lParam
  • 要将多个参数传递给排序回调,请创建一个结构并将指针传递给它。
  • 一旦您开始排序,项目的原始顺序就会丢失,如果没有保存在某处,您将无法再参考它。
  • 如果您可以重复移动已排序项目的操作,那么精心设计的lParam也不足以了解项目的原始顺序。
  • 你可以搞乱lParam而不是单独准备所需的物品订单,但这很脏,很容易出错。

通用解决方案

  • 确保所有商品都有唯一的lParam
  • 在致电LVM_SORTITEMSEX之前,请按所需顺序准备lParam的向量:
    1. 列出列表中从开头到“红色标记”项目的项目,将他们的lParam添加到矢量中。
    2. 将列表中的项目从“红色标记”项目枚举到列表末尾,如果选择了项目,则将其lParam添加到矢量中。
    3. 将列表中的项目从“红色标记”项目枚举到列表末尾,如果未选择项目,则将其lParam添加到矢量中。
    4. 现在您拥有lParam的订单:列表的开头,然后选择的项目保留其原始订单,然后是未选择的项目保留其原始订单。
  • 将该向量传递给排序回调
  • 在其中,在向量中查找两个lParam的位置,并根据此位置进行回答。例如,如果您发现第一个位置小于第二个位置,则返回一个负数。典型的方法是return (firstPos - secondPos),它将在一行代码中处理firstPossecondPos的所有相对订单。
  • 这将使您的排序calllback将准备好的项目顺序列入列表。