Using ListView control under Win32 API
This article will show/teach how to use ListView under Win32 API (Visual C++ 6.0).
Introduction
This article is for those who did not find, any useful code examples and information regarding ListControl (ListView
). For me, it was really hard to get a working code for Win32 API, since I don't code in MFC. The only help I was able to get was win32.hlp which contains every API by Microsoft. Note that it did not have any examples in it. Also searching through forums can be helpful, but it is slow and sometimes you really wanna get some stuff going fast and clear. I decided to write this article for the coders who don't know any MFC (class) or just starting with ListControl via APIs. First, I must say that all examples I've seen so far lacked information for mon-MFC coders!. If you can't code or can't understand MFC, and are using the API, then this little article is for you.
So let's start.
#include <commctrl.h>
Add it into your C/CPP file so we can use its functions. We add a ListControl to our dialog (you can also create it via the CreateWindowEx
API!). Once we named it, (IDC_LIST
), we add 2 buttons which will do something to our ListView
. Once we've done it, we need to save its HANDLE (which is HWND
). We use a static HWND hList=NULL;
as a global variable (so we can access it in any function). Now we need to initialize our ListView
(I will do it in the WM_INITDIALOG
message). First we get the handle: hList=GetDlgItem(hWnd,IDC_LIST)
;, then we add a LVITEM LvItem; struct
(also a global variable). The LVITEM struct
consists of a few parameters:
typedef struct _LV_ITEM { UINT mask; // attributes of this data structure int iItem; // index of the item to which this structure refers int iSubItem; // index of the subitem to which this structure refers UINT state; // Specifies the current state of the item UINT stateMask; // Specifies the bits of the state member that are valid. LPTSTR pszText; // Pointer to a null-terminated string // that contains the item text int cchTextMax; // Size of the buffer pointed to by the pszText member int iImage; // index of the list view item's icon LPARAM lParam; // 32-bit value to associate with item } LV_ITEM;
We also add a LVCOLUMN LvCol;
(global variable). The LVCOLUMN struct
consists of a few parameters:
typedef struct _LV_COLUMN { UINT mask; // which members of this structure contain valid information int fmt; // alignment of the column heading and the subitem text int cx; // Specifies the width, in pixels, of the column. LPTSTR pszText; // Pointer to a null-terminated string // that contains the column heading int cchTextMax; // Specifies the size, in characters, of the buffer int iSubItem; // index of subitem } LV_COLUMN;
Now that we know about the struct
's members, let's initialize and use what we need: at the WM_INITDIALOG
message.
// Here we put the info on the Coulom headers // this is not data, only name of each header we like Memset(&LvCol,0,sizeof(LvCol)); // Zero Members LvCol.mask=LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM; // Type of mask LvCol.cx=0x28; // width between each coloum LvCol.pszText="Item"; // First Header Text LvCol.cx=0x42; // width of column
For more information that our list control can supply us, we can use the extended styles (full raw select):
SendMessage(hList,LVM_SETEXTENDEDLISTVIEWSTYLE, 0,LVS_EX_FULLROWSELECT); // Set style
Now, let us put some columns (as much as you like):
// Inserting Couloms as much as we want SendMessage(hList,LVM_INSERTCOLUMN,0,(LPARAM)&LvCol); // Insert/Show the coloum LvCol.pszText="Sub Item1"; // Next coloum SendMessage(hList,LVM_INSERTCOLUMN,1,(LPARAM)&LvCol); // ... LvCol.pszText="Sub Item2"; // SendMessage(hList,LVM_INSERTCOLUMN,2,(LPARAM)&LvCol); // LvCol.pszText="Sub Item3"; // SendMessage(hList,LVM_INSERTCOLUMN,3,(LPARAM)&LvCol); // LvCol.pszText="Sub Item4"; // SendMessage(hList,LVM_INSERTCOLUMN,4,(LPARAM)&LvCol); // LvCol.pszText="Sub Item5"; // SendMessage(hList,LVM_INSERTCOLUMN,5,(LPARAM)&LvCol); // ...same as above
Once we've added headers (columns) to our listview
, we need to add some items to it. This is done via the above mentioned LVITEM struct
.
memset(&LvItem,0,sizeof(LvItem)); // Zero struct's Members // Setting properties Of members: LvItem.mask=LVIF_TEXT; // Text Style LvItem.cchTextMax = 256; // Max size of test LvItem.iItem=0; // choose item LvItem.iSubItem=0; // Put in first coluom LvItem.pszText="Item 0"; // Text to display (can be from a char variable) (Items) SendMessage(hList,LVM_INSERTITEM,0,(LPARAM)&LvItem); // Send info to the Listview for(i=1;i<=5;i++) // Add SubItems in a loop { LvItem.iSubItem=i; sprintf(Temp,"SubItem %d",i); LvItem.pszText=Temp; SendMessage(hList,LVM_SETITEM,0,(LPARAM)&LvItem); // Enter text to SubItems }
How about inserting a new item?
// lets add a new Item: LvItem.iItem=1; // choose item LvItem.iSubItem=0; // Put in first coluom LvItem.pszText="Item 1"; // Text to display SendMessage(hList,LVM_INSERTITEM,0,(LPARAM)&LvItem); // Send to the Listview for(i=1;i<=5;i++) // Add SubItems in a loop { LvItem.iSubItem=i; sprintf(Temp,"SubItem %d",i); LvItem.pszText=Temp; SendMessage(hList,LVM_SETITEM,0,(LPARAM)&LvItem); // Enter text to SubItems }
Note: Temp is a char
variable: char Temp[255]=""
.
Adding an item via the button:
case IDC_ADDITEM: { int iItem; char ItemText[100]; iItem=SendMessage(hList,LVM_GETITEMCOUNT,0,0); // number of items GetDlgItemText(hWnd,IDC_ADD,ItemText,100); // get text from editobx if((lstrlen(ItemText))==0) // check if items exist { MessageBox(hWnd,"Please Write Some Text", "Error",MB_OK|MB_ICONINFORMATION); break; } LvItem.iItem=iItem; // choose item to enter to LvItem.iSubItem=0; // Put in first coluom (no need subitems) // Text to display (can be from a char variable) (Items) LvItem.pszText=ItemText; // Send item text to the Listview SendMessage(hList,LVM_INSERTITEM,0,(LPARAM)&LvItem); } break;
Adding a Subitem via the button:
case IDC_ADDSUBITEM: { int Item,i; char SubItemText[100]; Item=SendMessage(hList,LVM_GETITEMCOUNT,0,0); // number of items GetDlgItemText(hWnd,IDC_ADDSUB,SubItemText,100); // get text from editobx if((lstrlen(SubItemText))==0) // check if items exist { MessageBox(hWnd,"Please Write Some Text", "Error",MB_OK|MB_ICONINFORMATION); break; } // choose the item-1 (we can't add subitems to non existing item LvItem.iItem=Item-1; for(i=1;i<=5;i++) // add 5 subitems { // Text to display (can be from a char variable) (Items) LvItem.pszText=SubItemText; // Put in coluom of index i LvItem.iSubItem=i; SendMessage(hList,LVM_SETITEM,0, (LPARAM)&LvItem); // send Subitem text to listview } } break;
Put both item and SubItems.
case IDC_BOTH: { int itemIndex,j; char iSubItemText[100]=""; char iItemText[100]=""; itemIndex=SendMessage(hList,LVM_GETITEMCOUNT,0,0); // number of items GetDlgItemText(hWnd,IDC_ADD,iItemText,100); // get text GetDlgItemText(hWnd,IDC_ADDSUB,iSubItemText,100); // get text // we enter text to the editboxes? if((lstrlen(iSubItemText) && lstrlen(iItemText))==0) { MessageBox(hWnd,"Please Write Some Text", "Error",MB_OK|MB_ICONINFORMATION); break; } LvItem.iItem=itemIndex; // item will be put at itemIndex LvItem.iSubItem=0; // adding item, no need subitems LvItem.pszText=iItemText; // set pointer to the item text SendMessage(hList,LVM_INSERTITEM,0,(LPARAM)&LvItem); // put it for(j=1;j<=5;j++) // add 5 subitems { LvItem.pszText=iSubItemText; // Text to display LvItem.iSubItem=j; // Put in coluom at index j SendMessage(hList,LVM_SETITEM,0,(LPARAM)&LvItem); // put it :) } } break;
Deleting all items at the list control is done via one message to the list control:
SendMessage(hList,LVM_DELETEALLITEMS,0,0);
Deleting Specific Selected Item in the ListView
.
We use the flag (global variable) to help us notice when an item is focused or selected (has a blue color wrapping the item).
case IDC_DELSELITEM: if(flag) // we pressed an item? SendMessage(hList,LVM_DELETEITEM,iSelect,0); // delete the item selected flag=0; // reset the flag after we used the item break;
The next part is to show how a user selects an item and responds to the button or how do we use the mouse and affect the list view while clicking on it with double click, left click.. etc.
Affecting the ListView
must be done via the WM_NOTIFY
message, this is because the ListView
doesn't have it over its properties like ListBox has! We put the WM_NOTIFY
message under the switch(Message){...}
.
case WM_NOTIFY: // the message that is being sent always { switch(LOWORD(wParam)) // hit control { case IDC_LIST: // did we hit our ListView contorl?
To make us select the items (we are using full raw select), we need to use NMHDR
and its members:
// if code == NM_CLICK - Single click on an item if(((LPNMHDR)lParam)->code == NM_CLICK) { // do some stuff here }
A list of code we can use for our listview
are:
... ... NM_CLICK - pnmh = (NMHDR FAR *) lParam; NM_DBLCLK - pnmh = (NMHDR FAR *) lParam; NM_KILLFOCUS - pnmh = (NMHDR FAR *) lParam; NM_RCLICK - pnmh = (NMHDR FAR *) lParam; NM_RDBLCLK - pnmh = (NMHDR FAR *) lParam; NM_RETURN - pnmh = (NMHDR FAR *) lParam; NM_SETFOCUS - pnmh = (NMHDR FAR *) lParam; LVN_ENDLABELEDIT - pdi = (LV_DISPINFO FAR *) lParam; LVN_BEGINLABELEDIT -pdi = (LV_DISPINFO FAR *) lParam; ... ...
See win32.hlp for more useful messages.
So, let's continue from what we left, we need to select an item in order to delete it. We are using the code we learned/used from above:
if(((LPNMHDR)lParam)->code == NM_CLICK) { iSelect=SendMessage(hList,LVM_GETNEXTITEM, -1,LVNI_FOCUSED); // return item selected if(iSelect==-1) // no items { MessageBox(hWnd,"No Items in ListView", "Error",MB_OK|MB_ICONINFORMATION); break; } flag=1; // set flag so that we know there was a hit }
Note: iSelect
is a global variable.
Say we want to get the text from an item & subitems once we double click an item. We need to add a NM_DBLCLK
message and process it with our code:
if(((LPNMHDR)lParam)->code == NM_DBLCLK) { //... }
We use all the messages that the listview
has to offer us and from the code we used above, it wont be hard to get the item/sub items:
case WM_NOTIFY: { switch(LOWORD(wParam)) { case IDC_LIST: if(((LPNMHDR)lParam)->code == NM_DBLCLK) { char Text[255]={0}; char Temp[255]={0}; char Temp1[255]={0}; int iSlected=0; int j=0; iSlected=SendMessage(hList,LVM_GETNEXTITEM,-1,LVNI_FOCUSED); if(iSlected==-1) { MessageBox(hWnd,"No Items in ListView", "Error",MB_OK|MB_ICONINFORMATION); break; } memset(&LvItem,0,sizeof(LvItem)); LvItem.mask=LVIF_TEXT; LvItem.iSubItem=0; LvItem.pszText=Text; LvItem.cchTextMax=256; LvItem.iItem=iSlected; SendMessage(hList,LVM_GETITEMTEXT, iSlected, (LPARAM)&LvItem); sprintf(Temp1,Text); for(j=1;j<=5;j++) { LvItem.iSubItem=j; SendMessage(hList,LVM_GETITEMTEXT, iSlected, (LPARAM)&LvItem); sprintf(Temp," %s",Text); lstrcat(Temp1,Temp); } MessageBox(hWnd,Temp1,"test",MB_OK); } } }
Label Editing
Say we want to edit our item's text at runtime, how can we do it? We will use the LVN_BEGINLABELEDIT
and LVN_ENDLABELEDIT
messages.
if(((LPNMHDR)lParam)->code == LVN_BEGINLABELEDIT) { hEdit=ListView_GetEditControl(hList); }
When we give the listbox an "edit labels" dialog property, we are able to edit the item's text when we press on it with the mouse [select item -> click small left button]. Once we did it, we need to trap the editbox created at run-time by the listbox. We use the LVN_BEGINLABELEDIT
message to begin the label edit. Once it's trapped, we are saving the created edit's handle.
I used the ListView_GetEditControl
macro because it is shorter to write :), but you can use the SendMessage
as well. (Return a HWND
if using HWND hEdit=(HWND)SendMessage()
.) We save the handle into hEdit
.
Note: hEdit
is a global variable!
Once we saved the handle, we need to use LVN_ENDLABELEDIT
message to actually get the text from the created edit and save it over a text buffer.
if(((LPNMHDR)lParam)->code == LVN_ENDLABELEDIT) { int iIndex; char text[255]=""; iIndex=SendMessage(hList,LVM_GETNEXTITEM, -1,LVNI_FOCUSED); // get selected item LvItem.iSubItem=0; // we get the item only (change for sub) LvItem.pszText=text; // text type GetWindowText(hEdit, text, sizeof(text)); // get the text into a buffer SendMessage(hList,LVM_SETITEMTEXT, (WPARAM)iIndex,(LPARAM)&LvItem); // put new text }
ListView Colors
You won't believe how much time I took to dig this up to actually work! I mean, 99.99% examples were MFC only!
So here we go, finally, pure WinAPI code for listview colors.
Note: This code is found deeply inside MSDN!
First we handle the WM_NOTIFY
message to use a NM_CUSTOMDRAW
notification. We don't need to use any owner drawn listview, and since this is custom drawn, we can paint the items/subitems at our will.
if(((LPNMHDR)lParam)->code == NM_CUSTOMDRAW) { SetWindowLong(hWnd, DWL_MSGRESULT, (LONG)ProcessCustomDraw(lParam)); return TRUE; }
We will set a new style which will be a result of our painting function.
LRESULT ProcessCustomDraw (LPARAM lParam) { LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam; switch(lplvcd->nmcd.dwDrawStage) { case CDDS_PREPAINT : //Before the paint cycle begins //request notifications for individual listview items return CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: //Before an item is drawn if (((int)lplvcd->nmcd.dwItemSpec%2)==0) { //customize item appearance lplvcd->clrText = RGB(255,0,0); lplvcd->clrTextBk = RGB(200,200,200); return CDRF_NEWFONT; } else{ lplvcd->clrText = RGB(0,0,255); lplvcd->clrTextBk = RGB(255,255,255); return CDRF_NEWFONT; } break; //Before a subitem is drawn case CDDS_SUBITEM | CDDS_ITEMPREPAINT: if (iSelect == (int)lplvcd->nmcd.dwItemSpec) { if (0 == lplvcd->iSubItem) { //customize subitem appearance for column 0 lplvcd->clrText = RGB(255,0,0); lplvcd->clrTextBk = RGB(255,255,255); //To set a custom font: //SelectObject(lplvcd->nmcd.hdc, // <your custom HFONT>); return CDRF_NEWFONT; } else if (1 == lplvcd->iSubItem) { //customize subitem appearance for columns 1..n //Note: setting for column i //carries over to columnn i+1 unless // it is explicitly reset lplvcd->clrTextBk = RGB(255,0,0); lplvcd->clrTextBk = RGB(255,255,255); return CDRF_NEWFONT; } } } return CDRF_DODEFAULT; }
There you code, change/implement code at your will. This is a very good working skeleton of a custom draw, painting items! :-)
---Sub item Colors---
Sub item colors are really cool when you don't wanna have same color for a row. The basic idea is to send a CDRF_NOTIFYSUBITEMDRAW
notification to our result. We will send this message first thing when our item is going to be repainted!
switch(lplvcd->nmcd.dwDrawStage) { case CDDS_PREPAINT : //Before the paint cycle begins { //request notifications for individual listview items return CDRF_NOTIFYITEMDRAW; } break; case CDDS_ITEMPREPAINT: //Before an item is drawn { // Notify we want SubItems to be repainted return CDRF_NOTIFYSUBITEMDRAW; } break; }
The only thing left is to trap this notification and to start drawing the sub items with any color/back-color we want, in a single item.
switch(lplvcd->nmcd.dwDrawStage) { case CDDS_PREPAINT : //Before the paint cycle begins //request notifications for individual listview items return CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: //Before an item is drawn { return CDRF_NOTIFYSUBITEMDRAW; } break; //Before a subitem is drawn case CDDS_SUBITEM | CDDS_ITEMPREPAINT: { switch(lplvcd->iSubItem) { case 0: { lplvcd->clrText = RGB(255,255,255); lplvcd->clrTextBk = RGB(240,55,23); return CDRF_NEWFONT; } break; case 1: { lplvcd->clrText = RGB(255,255,0); lplvcd->clrTextBk = RGB(0,0,0); return CDRF_NEWFONT; } break; case 2: { lplvcd->clrText = RGB(20,26,158); lplvcd->clrTextBk = RGB(200,200,10); return CDRF_NEWFONT; } break; case 3: { lplvcd->clrText = RGB(12,15,46); lplvcd->clrTextBk = RGB(200,200,200); return CDRF_NEWFONT; } break; case 4: { lplvcd->clrText = RGB(120,0,128); lplvcd->clrTextBk = RGB(20,200,200); return CDRF_NEWFONT; } break; case 5: { lplvcd->clrText = RGB(255,255,255); lplvcd->clrTextBk = RGB(0,0,150); return CDRF_NEWFONT; } break; } } } return CDRF_DODEFAULT;
ListView BackGround Image
Isn't it cool to have a picture (JPG, BMP, GIF...) on your ListView
color? It makes it a bit more pro look. I guess it is more about personal taste. Anyhow, the ListView
will use the OLE object to connect to the image and will draw it over the control.
First, the ListView
will use a struct
to store information regarding the image and its properties. The LVBKIMAGE struct
:
typedef struct tagLVBKIMAGE { ULONG ulFlags; // the way the pic behaves (tile, none, normal..) HBITMAP hbm; // reserved for future!! (HBITMAP) LPTSTR pszImage; // path/url to our bitmap if used with LVBKIF_SOURCE_URL UINT cchImageMax;//Size of the buffer at the address in pszImage int xOffsetPercent; // x pos on listview int yOffsetPercent; // y pos on listview } LVBKIMAGE, *LPLVBKIMAGE;
First thing to do is to initialize the OLE object! Microsoft suggests 2 ways for that: CoInitialize(NULL);
or OleInitialize(NULL)
. The code itself is pretty much self explained!
case WM_INITDIALOG: { LVBKIMAGE plvbki={0}; // set all members to 0 (or use memset) char url[]="C:\\a.jpg"; // path to our image //memset(&plvbki,0,sizeof(plvbki)); plvbki.ulFlags=LVBKIF_SOURCE_URL; // image from a path plvbki.pszImage=url; // the path plvbki.xOffsetPercent=40; // position plvbki.yOffsetPercent=15; // on the list view OleInitialize(NULL); // intialize OLE COM Object SendMessage(hList,LVM_SETTEXTBKCOLOR, 0,(LPARAM)CLR_NONE); // see below SendMessage(hList,LVM_SETBKIMAGE,0, (LPARAM)(LPLVBKIMAGE)&plvbki); // do it } break;
You can see that I have used CLR_NONE
. Now, if your list view is not using custom control (Listview colors, see section above), then this line will set the background color of each row to transparent!
The only thing left to do is to close the OLE COM object. This is what Microsoft suggested anyway, doing it by: CoUninitialize();
or OleUninitialize();
. When your application is closed, funny, it causes a crash here. I'll check it later :-)
---Adding Item's Renaming Cancel Event---
Heya, ever wanted that effect when you edit a folder's name and you typed the wrong name, and wanna start over and go back to the original folder's name by ESC key? (you probably know it..)
We want to add the same effect to our listview
's items too! For this, we need to learn about Windows messages and process them. Usually think about an EverLasting look that monitors the window you are using, and listens for every message that you process:
Mouse, KeyBoard...etc., you should already be familiar with them. This is what we need to to:
- Save the original text of item
- Monitor for a key press
- If ESC key pressed, re-save old text else
we get the new text and show it on the new item's caption.
Let's start by creating our *Global* vars (so we can access then from any function/message).
//*Global Variables* bool escKey=0; // check whatever we pressed ESC char tempstr[100]=""; // holds original text TCHAR tchar; // holds the char we pressed (WM_CHAR message) MSG msg; // Message struct to be filled
And now for the re-coding & adding some stuff:
We first begin with adding the below code to the WM_INITDIALOG
:
ShowWindow(hWnd,SW_NORMAL); UpdateWindow(hWnd); while(TRUE) { if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { // We must Process this! this is our ticket out of the loop & process if(msg.message==WM_QUIT) { break; // Kill while look } TranslateMessage(&msg); // Translate the Message (VK->WM_CHAR..etc) DispatchMessage(&msg); // dispatches a message to a window procedure } }
Notice the WM_QUIT
. To break the while
loop, we must process this when a user hits the [X]. The while
will be broken and we will go to the WM_CLOSE
message where we must add 1 line:
case WM_CLOSE: { PostQuitMessage(0); EndDialog(hWnd,0); // kill dialog } break;
Now for the main part, you already know the messages that Windows sends to the ListView
in order to edit an item's caption right?
LVN_BEGINLABELEDIT
LVN_ENDLABELEDIT
We already have a code for them, but we need to add a slight addition:
if(((LPNMHDR)lParam)->code == LVN_BEGINLABELEDIT) { hEdit=ListView_GetEditControl(hList); GetWindowText(hEdit, tempstr, sizeof(tempstr)); // NEW }
Here we save the text before we actually edit it! That's great and the last part:
if(((LPNMHDR)lParam)->code == LVN_ENDLABELEDIT) { int iIndex; char text[255]=""; tchar = (TCHAR)msg.wParam;// NEW if(tchar == 0x1b) // NEW escKey=1; // NEW iIndex=SendMessage(hList,LVM_GETNEXTITEM,-1,LVNI_FOCUSED); if(iIndex==-1) break; LvItem.iSubItem=0; // main item only if(escKey==0) // NEW { LvItem.pszText=text; GetWindowText(hEdit, text, sizeof(text)); SendMessage(hList,LVM_SETITEMTEXT, (WPARAM)iIndex,(LPARAM)&LvItem); } else{ // All Below is NEW LvItem.pszText=tempstr; // put old text on item's caption SendMessage(hList,LVM_SETITEMTEXT, (WPARAM)iIndex,(LPARAM)&LvItem); escKey=0; } } break;
We first begin by checking tchar
's content...why?
Ok, well, you probably know that ENTER /ESC and some more are keys that will finish up the editing of the text! So we can't check for those keys in the while()
loop, because it will be skipped by the LVN_ENDLABELEDIT
message. So first thing we check is, if ESC was hit.
Using a boolean variable, we can determine whenever a cancel event has occurred, or we can actually process the new text.
Selecting (Highlighting) items/All Items
It is not really hard, we use this piece of code:
Select Item: ListView_SetItemState(hList, -1, 0, LVIS_SELECTED); // deselect all items SendMessage(hList,LVM_ENSUREVISIBLE , (WPARAM)item,FALSE); // if item is far, scroll to it ListView_SetItemState(hList,item, LVIS_SELECTED ,LVIS_SELECTED); // select item ListView_SetItemState(hList,item, LVIS_FOCUSED ,LVIS_FOCUSED); // optional Select All Item: ListView_SetItemState(hList, -1, 0, LVIS_SELECTED); // deselect all items ListView_SetItemState(hList,-1, LVIS_SELECTED ,LVIS_SELECTED);
The -1 means ALL items. If you want an individual item, just write its index number.
Bugs and Help
It could be that you are using Win2000 for compiling this source code and you may happen to get into a few problems of linking or compiling..
Problem
The thread exits with code 0x0 (exit 0) // something like this :).
When you compile your exe and try to run it, it wont run. Nothing is loading..??? This is because we didn't link with the comctl32.lib. Add it to your link settings: Project -> Settings -> Link Tab -> general -> add comctl32.lib into "object/libary modules" -> click OK. Rebuild project.. and try to run. It's suppose to work. Another way of solving this is to add this code to your WinMain
:
// add this code if win2000/nt/xp doesn't // load ur winmain (experimental only) // also add comctl32.lib INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwICC = ICC_LISTVIEW_CLASSES; InitCtrls.dwSize = sizeof(INITCOMMONCONTROLSEX); BOOL bRet = InitCommonControlsEx(&InitCtrls);
It could work, I haven't tried it..
Yet another approach is directly via the: InitCommonControls();
function!! Write it in the case WM_INITDIALOG:
---F.A.Q---
Q. Why sometimes I get wrong result from the LVM_GETNEXTITEM
when I select items out of the range?
A. This is because we have to set a flag called LVNI_SELECTED
:
SendMessage(hList,LVM_GETNEXTITEM,-1,LVNI_FOCUSED|LVNI_SELECTED);
Q. How do I attach a popup menu when clicking on an item (right button...etc.)?
A. Really easy. First make the menu in the resource editor, and use this code:
HMENU hMenu = LoadMenu (NULL, MAKEINTRESOURCE (IDR_MENU)); HMENU hPopupMenu = GetSubMenu (hMenu, 0); POINT pt; SetMenuDefaultItem (hPopupMenu, -1, TRUE); GetCursorPos (&pt); SetForegroundWindow (hWnd); TrackPopupMenu (hPopupMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL); SetForegroundWindow (hWnd); DestroyMenu (hPopupMenu); DestroyMenu (hMenu);
Note: Make sure you pressed on an item, use a if(selected!=-1)
or alike code. That way we prevent from showing the popup menu anytime/anywhere.
Q. How do I color the: entire text color, list view back color, text back color?
A. It is not really hard. We can do it like this:
int count=SendMessage(GetDlgItem(hWnd, IDC_LIST),LVM_GETITEMCOUNT ,0,0); SendMessage(GetDlgItem(hWnd,IDC_LIST), LVM_SETTEXTCOLOR,0,(LPARAM)SetColor()); SendMessage(GetDlgItem(hWnd,IDC_LIST), LVM_SETTEXTBKCOLOR,0,(LPARAM)SetColor()); SendMessage(GetDlgItem(hWnd,IDC_LIST), LVM_SETBKCOLOR,0,(LPARAM)SetColor()); SendMessage(GetDlgItem(hWnd,IDC_LIST), LVM_REDRAWITEMS ,0,count); UpdateWindow(hWnd); .. .. .. LONG SetColor() { static CHOOSECOLOR cc ; static COLORREF crCustColors[16] ; cc.lStructSize = sizeof (CHOOSECOLOR) ; cc.hwndOwner = NULL ; cc.hInstance = NULL ; cc.rgbResult = RGB (0x80, 0x80, 0x80) ; cc.lpCustColors = crCustColors ; cc.Flags = CC_RGBINIT | CC_FULLOPEN ; cc.lCustData = 0 ; cc.lpfnHook = NULL ; cc.lpTemplateName = NULL ; ChooseColor (&cc); return cc.rgbResult; }
Note: However, this code has a really tiny bug. If the user presses the cancel/[X] from the color dialog, the default color of it will be returned, and colors your default list box color. However, it's not really hard to fix.
Q. How can I edit an item's caption using a button/menu...etc., just like renaming a folder with right click on mouse option?
A. Send the message: LVM_EDITLABEL
to the list view. Example: SendMessage(hList,LVM_EDITLABEL ,(WPARAM)index,(LPARAM)0);
.
We come to the end of this article. I hope everyone enjoyed this as much as it took me time to find and add a proper code ;-).