Move all selected items below the specified using LVM_SORTITEMSEX

Introduction:

To keep this post as concise as possible, let me just say that I need to move all the selected items in the list below a specific (unselected) item.

View documentation listview I came to LVM_SORTITEMSEX message .

Question:

How to use the above message to achieve my goal.

MY EFFORTS TO SOLVE THIS:

So far, using this message, I managed to move all the selected items to the bottom of the list -> listview is sorted so that the non-selected items precede the selected ones.

I just can't figure out how to implement moving selected elements below a specific element.

Below are images of what I get and what I want to achieve:

enter image description here

The left image shows what I get when I use the code below, and the right one shows the result I'm aiming for.

The following are relevant code snippets:

 // 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); 

I passed the listview handler as the third parameter to ListView_SortItemsEx , so I can use ListView_GetItem in CompareFunc .

+6
source share
2 answers

If I understand this correctly, you want to rearrange the elements with drag and drop and you want the sort function to do this. This can get complicated if you need to do this inside proc. Another solution is to first find a location.

  • Use vector to store items before "redMark"
  • Add selected items that appear after "redMark"
  • Add unselected items that appear after "redMark"
  • Save this order until LVITEM::lParam
  • Call ListView_SortItems (not ListView_SortItemsEx )

The only problem is that maybe lParam used for other reasons. We need to save lParam and then restore it after the sorting is complete.

It is also better if the ListView has LVS_SHOWSELALWAYS .

Note that this method moves items before "redMark". In your example, you should set redMark = 3 to move the selection to "Item 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
source

Introduction:

  • For LVM_SORTITEMSEX all elements must have a unique lParam
  • To pass several parameters to sort the callback, create a struct and pass pointer for it.
  • As soon as you start sorting, the original order of the elements is lost, and you can no longer reference it if it is not stored somewhere.
  • If you can repeat the operation of moving sorted elements, then carefully crafted lParam also not enough to know the original order of the elements.
  • You can use lParam instead of preparing the desired order of elements separately, but it is rather dirty and error prone.

Common decision

  • Make sure all items have a unique lParam
  • Before calling LVM_SORTITEMSEX prepare the lParam vector in the desired order:
    • List the items in the list from the beginning to the red label item, add their lParam to the vector.
    • List the items in the list from the "red label" to the end of the list; if an item is selected, add lParam to the vector.
    • List the items in the list from the "red label" to the end of the list, if the item is NOT selected, add its lParam to the vector.
    • Now you have the lParam order: the beginning of the list, then the selected items keep their original order, and then the unselected items keep their original order.
  • Pass this vector to sort the callback
  • In it, search positions of two lParam in a vector and make an answer based on these positions. For example, if you find that the first position is less than a second, you return a negative number. The typical approach is return (firstPos - secondPos) , which will handle all the relative orders of firstPos and secondPos in one line of code.
  • In this case, your sorting calllback will apply the prepared element order for the list.
+1
source

Source: https://habr.com/ru/post/988605/


All Articles