Win32 c++ [Listview] [LV_VIEW_ICON] Item Arranging in Descending order

NorthernSun 101 Reputation points
2021-04-03T18:24:37.593+00:00

Hello. I am trying to make a Listview (WC_LISTVIEW) arrange items in descending order at all times.

When I use the LVS_SORTDESCENDING style and I insert multiple items at once it seems to work fine, but when I add items one at a time the previously inserted items don't get re-arranged in descending order. I assume this is because LV_VIEW_ICON allows you to reposition items manually, so that is probably why it won't re-arrange previously inserted items once inserted. However, I want to have items arranged in descending order at all times because I won't allow the manual moving of items. How would I do this? By using LVM_SETITEMPOSITION after item insert? I have tried that but it seems to be very slow.

Thanks in advance.

Reproducible code:

#include <windows.h>
#include <CommCtrl.h>
#include <string>

::HINSTANCE g_hInstance = NULL;

// function fwd decls.
::HWND createWindow();
::HWND createListview(::HWND hParentWnd);
::HWND createButton(::HWND hParentWnd, int x, int y, std::string text);

int insertItem(::HWND hWndListview, std::string text);
void removeAllItems(::HWND hWndListview);

::HWND g_hWndWindow;
::HWND g_hWndListview;
::HWND g_hWndButton1;
::HWND g_hWndButton2;
::HWND g_hWndButton3;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* szCmdLine, int iCmdShow) {

 g_hInstance = hInstance;

 g_hWndWindow   = createWindow();
 g_hWndListview = createListview(g_hWndWindow);
 g_hWndButton1  = createButton(g_hWndWindow, 440, 30, "Add 5 Items");
 g_hWndButton2  = createButton(g_hWndWindow, 440, 90, "Add 10 Items");
 g_hWndButton3  = createButton(g_hWndWindow, 440, 150, "Remove All Items");

 MSG msg = { 0 };

 while ((GetMessage(&msg, NULL, 0, 0))) {

 TranslateMessage(&msg);
 DispatchMessage(&msg);

 }

 return 1;
}

::LRESULT CALLBACK windowProc(::HWND hWnd, ::UINT msg, ::WPARAM wParam, ::LPARAM lParam) {

 switch (msg) {

 case WM_COMMAND:
 {

 switch (HIWORD(wParam)) {

 case BN_CLICKED:
 {

 if (!g_hWndListview)
 break;

 int itemCount = ::SendMessage(g_hWndListview, LVM_GETITEMCOUNT, 0, 0);

 ::HWND hButtonWnd = (::HWND)lParam;

 if (hButtonWnd == g_hWndButton1) {

 for (int i = 0; i < 5; i++) {

 insertItem(g_hWndListview, "Item " + std::to_string(itemCount + i));

 }

 } else if (hButtonWnd == g_hWndButton2) {

 for (int i = 0; i < 10; i++) {

 insertItem(g_hWndListview, "Item " + std::to_string(itemCount + i));

 }

 } else if (hButtonWnd == g_hWndButton3) {

 removeAllItems(g_hWndListview);

 }

 break;

 }

 }

 break;

 }

 }

 return DefWindowProc(hWnd, msg, wParam, lParam);

}

::HWND createWindow() {

 ::WNDCLASS wndClass = { };

 wndClass.cbClsExtra    = 0;
 wndClass.cbWndExtra    = 0;
 wndClass.hbrBackground = (::HBRUSH)::GetStockObject(GRAY_BRUSH);
 wndClass.hCursor       = ::LoadCursor(NULL, IDC_ARROW);
 wndClass.hIcon         = ::LoadIcon(NULL, IDC_ICON);
 wndClass.hInstance     = g_hInstance;
 wndClass.lpfnWndProc   = windowProc;
 wndClass.lpszClassName = "Window1";
 wndClass.lpszMenuName  = NULL;
 wndClass.style         = CS_HREDRAW | CS_VREDRAW/* | CS_OWNDC | CS_PARENTDC*/;

 if (!::RegisterClass(&wndClass))
 return 0;

 ::HWND hWnd = ::CreateWindowEx(0,
                                "Window1",
                                "Window1",
                                WS_OVERLAPPEDWINDOW,
                                20, 20, 800, 800,
                                NULL,
                                (::HMENU)NULL,
                                g_hInstance,
                                0);

 ::ShowWindow(hWnd, SW_SHOW);
 ::UpdateWindow(hWnd);

 return hWnd;

}

::HWND createListview(::HWND hParentWnd) {

 ::HWND hWnd = ::CreateWindowEx(0,
                                WC_LISTVIEW,
                                WC_LISTVIEW,
                                WS_CHILD | LVS_ICON | LVS_SORTDESCENDING,
                                20, 20, 400, 400,
                                hParentWnd,
                                (::HMENU)NULL,
                                g_hInstance,
                                0);

 if (!hWnd)
 return NULL;

 ::HIMAGELIST hImageList = ImageList_Create(32, 32, ILC_COLOR32 | ILC_MASK, 0, 0);

 ::SendMessage(hWnd, LVM_SETIMAGELIST, LVSIL_NORMAL, (::LPARAM)hImageList);

 ListView_SetBkColor(hWnd, RGB(0, 255, 0));

 ::ShowWindow(hWnd, SW_SHOW);
 ::UpdateWindow(hWnd);

 return hWnd;

}

::HWND createButton(::HWND hParentWnd, int x, int y, std::string text) {

 ::HWND hWnd = ::CreateWindowEx(0,
                                WC_BUTTON,
                                text.c_str(),
                                WS_CHILD,
                                x, y, 120, 50,
                                hParentWnd,
                                (::HMENU)NULL,
                                g_hInstance,
                                0);

 if (!hWnd)
 return NULL;

 ::ShowWindow(hWnd, SW_SHOW);
 ::UpdateWindow(hWnd);

 return hWnd;

}

int insertItem(::HWND hWndListview, std::string text) {

 ::LVITEM lvItem = { 0 };

 lvItem.mask       = LVIF_TEXT;
 lvItem.pszText    = (char*)text.c_str();
 lvItem.cchTextMax = text.length();
 lvItem.iItem      = 1;

 int insertedItemIndex = ::SendMessage(hWndListview, LVM_INSERTITEM, 0, (::LPARAM)&lvItem);

 // Simple code to manually position items.
 /*
 int itemCount = ::SendMessage(g_hWndListview, LVM_GETITEMCOUNT, 0, 0);

 for (int i = 0; i < itemCount; i++) {

 int itemsPerRow = 5;

 int x = i % itemsPerRow * 40;
 int y = i / itemsPerRow * 40;

 ::SendMessage(hWndListview, LVM_SETITEMPOSITION, i, MAKELPARAM(x, y));

 }*/

 return insertedItemIndex;

}

void removeAllItems(::HWND hWndListview) {

 ::SendMessage(hWndListview, LVM_DELETEALLITEMS, 0, 0);

}
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,427 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,540 questions
0 comments No comments
{count} votes

Accepted answer
  1. Castorix31 81,831 Reputation points
    2021-04-03T19:00:19.297+00:00

    From MSDN doc :

    For the LVS_SORTASCENDING and LVS_SORTDESCENDING styles, item indexes are sorted based on item text in ascending or descending order, respectively. Because the LVS_LIST and LVS_REPORT views display items in the same order as their indexes, the results of sorting are immediately visible to the user. The LVS_ICON and LVS_SMALLICON views do not use item indexes to determine the position of icons. With those views, the results of sorting are not visible to the user.

    So you can sort items after having inserted them.

    A sort function from one of my old codes (sorted by ascii, you can change the sort algorithm)=>

    #define ASCENDING_SORT 0
    #define DESCENDING_SORT 1
    
    struct LVCOMPAREINFO
    {
        HWND hwndLV;
        int nCol; 
        BOOL bAscending;
    };
    LVCOMPAREINFO g_lvci;
    int CALLBACK ListViewCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
    
    int CALLBACK ListViewCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
    {
        WCHAR buf1[MAX_PATH], buf2[MAX_PATH];
        LVCOMPAREINFO* lpsd = (struct LVCOMPAREINFO*)lParamSort;
    
        ListView_GetItemText(lpsd->hwndLV, (int)lParam1, lpsd->nCol, buf1, sizeof(buf1));
        ListView_GetItemText(lpsd->hwndLV, (int)lParam2, lpsd->nCol, buf2, sizeof(buf2));
    
        if (lpsd->bAscending == ASCENDING_SORT)
            return(_wcsicmp(buf1, buf2));
        else if (lpsd->bAscending == DESCENDING_SORT)
            return(_wcsicmp(buf1, buf2) * -1);    
        return 0;
    }
    

    After items have been inserted (you should test if style is LVS_SORTDESCENDING or not):

     g_lvci.hwndLV = g_hWndListview;
     g_lvci.nCol = 0;
     g_lvci.bAscending = DESCENDING_SORT;
     ListView_SortItemsEx(g_hWndListview, ListViewCompareFunc, &g_lvci);
    
    0 comments No comments

0 additional answers

Sort by: Most helpful