question

NorthernSun-8235 avatar image
0 Votes"
NorthernSun-8235 asked Castorix31 answered

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

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


c++windows-api-general
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

1 Answer

Castorix31 avatar image
0 Votes"
Castorix31 answered

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


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.