In order to keep this post as brief as possible, let me just say that I need to move all selected items in the listview below certain (unselected) item.
Browsing through listview documentation I came upon LVM_SORTITEMSEX
message.
How to use the above message to achieve my goal.
So far, by using this message, I was able to move all selected items to the bottom of the list -> listview is sorted in such a way that unselected items precede selected ones.
I just can not figure out how to implement moving the selected items below certain item.
Below are the images of what I get, and what I want to achieve:
The left image shows what I get when I use the code submitted below, while the right one shows the result I aim for.
Here are the 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 have passed listview handle as third parameter to ListView_SortItemsEx
so I can use ListView_GetItem
in CompareFunc
.
If I understand this correctly, you want to rearrange items with drag and drop, and you want the sort function to do it. This could get complicated if it is to be done inside the sort proc. Another solution is to find the arrangement first.
LVITEM::lParam
ListView_SortItems
(not ListView_SortItemsEx
)The only problem is that maybe lParam
was used for other reasons. We have to save lParam
, and then restore it after sort is done.
Also it's better if ListView has LVS_SHOWSELALWAYS
.
Note, this method moves items before the "redMark". In your example you should set redMark = 3
to move selection before "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);
}
Introduction:
LVM_SORTITEMSEX
, all items must have unique lParam
'slParam
's are also not enough to know the original order of items.lParam
's instead of preparing desired order of items separately, but this is rather dirty and prone to errors.Generic solution
lParam
'sLVM_SORTITEMSEX
, prepare a vector of lParam
's in desired order:
lParam
's into vector.lParam
into vector.lParam
into vector.lParam
's: start of the list, then selected items retaining their original order, then unselected items retaining their original order.lParam
's in vector and make answer based on this positions. For example, if you find that first position is less then second, you return a negative number. The typical approach is return (firstPos - secondPos)
, that will handle all relative orders of firstPos
and secondPos
in one line of code.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With