INTRODUCTION AND RELEVANT INFORMATION:
I am trying to implement listview control with editable items and subitems. Instead of regular listview look, items and subitems should have edit control, checkbox or combo box.
I am using
raw WinAPI
and
C++
. I am targeting
Windows XP
onwards.
MY EFFORTS TO SOLVE THE PROBLEM:
After researching here and on the Internet, I was able to only find examples in
MFC
. They all use
LVN_BEGINLABELEDIT
technique to implement this behavior.
Unfortunately I do not understand entirely this concept so I have decided to start from scratch ( I consider this also to be the best approach for improving ones programming skills ).
MY CONCEPT:
I have decided to catch
NM_DBLCLK
for listview and to get coordinates from there using
ListView_GetItemRect
or
ListView_GetSubItemRect
macro.
Then I would simply move the combobox/checkbox/edit control over corresponding item/subitem ( combobox/edit control/checkbox would be created as separate, hidden windows ).
After user finishes with input ( by pressing enter or changing focus ) I would simply hide the combobox/checkbox/edit control.
MY CURRENT RESULTS:
At the moment, I am stuck with the dimensions of combobox/edit control/checkbox not being the same as item/subitem dimensions, when moved above the item/subitem.
QUESTION:
Can my code example submitted below be improved to properly adjust combobox/edit control/checkbox window size to the size of the item/subitem? For now, I will only focus on this part of the problem, to keep this question as short as possible.
Here is the instruction for creating small application that illustrates the problem. Notice that I have tried to keep things as minimal as I could:
1.) Create
default Win32 project
in Visual Studio ( I use VS 2008 ).
2.) Add the following
WM_CREATE
handler to main window's procedure:
case WM_CREATE:
{
HWND hEdit = CreateWindowEx( 0,WC_EDIT, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_CENTER | ES_AUTOHSCROLL,
250, 10, 100, 20, hWnd, (HMENU)1500, hInst, 0 );
HWND hComboBox = CreateWindowEx( 0,WC_COMBOBOX, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWNLIST,
100, 10, 100, 20, hWnd, (HMENU)1600, hInst, 0 );
HWND hwndLV = CreateWindowEx( 0, WC_LISTVIEW,
L"Editable Subitems",
WS_CHILD | WS_VISIBLE | WS_BORDER |
LVS_REPORT | LVS_SINGLESEL,
150, 100, 250, 150, hWnd, (HMENU)2000, hInst, 0 );
ListView_SetExtendedListViewStyle( GetDlgItem( hWnd, 2000 ),
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER );
LVCOLUMN lvc = {0};
lvc.iSubItem = 0;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
for (long nIndex = 0; nIndex < 5; nIndex++ )
{
wchar_t txt[50];
swprintf_s( txt, 50, L"Column %d", nIndex + 1 );
lvc.iSubItem = nIndex;
lvc.cx = 60;
lvc.pszText = txt;
ListView_InsertColumn( GetDlgItem( hWnd,2000 ), nIndex, &lvc );
}
LVITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.iItem = 0;
for( lvi.iItem = 0; lvi.iItem < 10; lvi.iItem++ )
for (long nIndex = 0; nIndex < 5; nIndex++ )
{
wchar_t txt[50];
swprintf_s( txt, 50, L"Item %d%d", lvi.iItem + 1, nIndex + 1 );
lvi.iSubItem = nIndex;
lvi.pszText = txt;
if( ! nIndex ) SendDlgItemMessage( hWnd, 2000,
LVM_INSERTITEM, 0,
reinterpret_cast<LPARAM>(&lvi) );
else SendDlgItemMessage( hWnd, 2000,
LVM_SETITEM, 0,
reinterpret_cast<LPARAM>(&lvi) );
}
}
return 0L;
3.) Add the following handler for
WM_NOTIFY
in main window's procedure:
case WM_NOTIFY:
{
if( ((LPNMHDR)lParam)->code == NM_DBLCLK )
{
switch( ((LPNMHDR)lParam)->idFrom )
{
case 2000: {
LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam;
if( ( lpnmia->uKeyFlags || 0 ) == 0 )
{
RECT rc = { 0, 0, 0, 0 };
if( (lpnmia->iSubItem) <= 0 ) {
RECT rcHelp = { 0, 0, 0, 0 };
ListView_GetItemRect( lpnmia->hdr.hwndFrom,
lpnmia->iItem, &rcHelp, LVIR_BOUNDS );
ListView_GetItemRect( lpnmia->hdr.hwndFrom,
lpnmia->iItem, &rc, LVIR_LABEL );
rc.left = rcHelp.left;
}
else {
ListView_GetSubItemRect( lpnmia->hdr.hwndFrom,
lpnmia->iItem, lpnmia->iSubItem,
LVIR_BOUNDS, &rc );
}
POINT p;
p.x = rc.left;
p.y = rc.top;
ClientToScreen( lpnmia->hdr.hwndFrom, &p );
ScreenToClient( hWnd, &p );
MoveWindow( GetDlgItem( hWnd, 1500 ),
p.x, p.y,
rc.right - rc.left,
rc.bottom - rc.top, TRUE );
HWND previousWnd = SetFocus( GetDlgItem( hWnd, 1500 ) );
}
}
break;
default:
break;
}
}
}
break;
And
this[
^] is the result I get.
You can clearly see that top and bottom border of the edit control are not drawn properly. As for combobox, the width is properly adjusted, but height remains the same.
I have tried substituting
MoveWindow
call with
SetWindowPos
but the result was the same.
After further tampering, I have found out that
NMITEMACTIVATE
bugs when returning the rectangle of a subitem, if listview doesn't have
LVS_EX_FULLROWSELECT
style set. You can see this by simply commenting out the part in my
WM_CREATE
handler where I set this style. Maybe I am doing something wrong and this "bug" may be caused by my code, but I don't see the problem.
EDITED on September, 17th 2014:
After testing the values for
iItem
and
iSubItem
members of
NMITEMACTIVATE
structure when listview doesn't have
LVS_EX_FULLROWSELECT
I can verify that the bug is not in my code. It always returns
iItem
to be 0, no matter which subitem I click. This explains the faulty behavior I got when removing this style.
If any further info is required please leave a comment and I will act as soon as possible.
Thank you for your time and efforts to help.