c++ - Properly handle subitem editing ( or canceling of subitem editing ) in listview with editable subitems -


introduction:

i trying implement listview control editable subitems. in-place editing of items/subitems use edit control.

i believe have managed code placing of edit control above item/subitem.

problem:

i not know on events should end/cancel subitem editing ( hide edit control, set subitem text etc ) , how should it.

to clarify, speak of moment when user finishes/cancels in place editing.

at point edit control no longer needed, should hide ( not recreating every time; believe creating once , showing/hiding when needed more efficient ).

i targeting behavior properties window has in visual studio ( see attached image see window refer ).

enter image description here

i want achieve editing/canceling same way window when user presses esc key/clicks on window/clicks on scrollbar etc.

my efforts solve this:

using google, found few examples old , not address of relevant cases, why ask here help.

however, able find out 1 of events must consider en_killfocus, case when user presses esc/enter key , case when user clicks on other edit control.

edit:

i have managed handle esc , enter keys, case when user clicks on sibling control or switches windows alt + tab. have updated sscce relevant changes

question:

in order achieve default behavior grid ( if there 1 windows apps ), messages/events must handle?

can point out should edit subitem , hide edit control, , should hide edit control?

edit:

my problem remained handle case when user clicks on listview scrollbars, or on background of main window. not know how handle , appreciate can get.

relevant information:

i use visual studio 2013, on windows 7 x86;

i developing in c++ using raw winapi;

sscce:

below solution have far. have tried thoroughly comment it, if more info required leave comment , update post.

#include <windows.h> #include <windowsx.h>   // various listview macros etc #include <commctrl.h> #include <stdio.h>      // swprintf_s()  // enable visual styles #pragma comment( linker, "/manifestdependency:\"type='win32' \                          name='microsoft.windows.common-controls' version='6.0.0.0' \                          processorarchitecture='*' publickeytoken='6595b64144ccf1df' \                          language='*'\"")  // link common controls library #pragma comment( lib, "comctl32.lib")   //global variables hinstance hinst;  // listview subclass procedure lresult callback listviewsubclassproc(hwnd hwnd, uint message,      wparam wparam, lparam lparam,     uint_ptr uidsubclass, dword_ptr dwrefdata) {     switch (message)     {     case wm_vscroll:     case wm_hscroll:         // if edit control has focus take away , give listview         if (getfocus() == getdlgitem(getparent(hwnd), 5000))             setfocus(hwnd);  // use wm_nextdlgctl dialogbox !!!!         break;     case wm_ncdestroy:         ::removewindowsubclass(hwnd, listviewsubclassproc, uidsubclass);         return defsubclassproc(hwnd, message, wparam, lparam);     }     return ::defsubclassproc(hwnd, message, wparam, lparam); }  // subclass procedure edit control lresult callback inplaceeditcontrol_subclassproc(hwnd hwnd, uint message, wparam wparam, lparam lparam,     uint_ptr uidsubclass, dword_ptr dwrefdata) {     switch (message)     {     case wm_getdlgcode:         return (dlgc_wantallkeys | defsubclassproc(hwnd, message, wparam, lparam));     case wm_killfocus:         showwindow(hwnd, sw_hide);         return defsubclassproc(hwnd, message, wparam, lparam);     case wm_char:         //process message avoid message beeps.         switch (wparam)         {         case vk_return:             return 0l;         case vk_escape:             return 0l;         default:             return ::defsubclassproc(hwnd, message, wparam, lparam);         }         break;     case wm_keydown:         switch (wparam)         {         case vk_return:         {             // listview handle             hwnd hwndlv = getdlgitem(getparent(hwnd), 2000);             // edit control's client rectangle             rect rc = { 0 };             getclientrect(hwnd, &rc);             // since edit control lies inside item rectangle             // can test coordinate inside edit control's             // client rectangle             // chose ( rc.left, rc.top )             mapwindowpoints(hwnd, hwndlv, (lppoint)&rc, (sizeof(rect) / sizeof(point)));             // item , subitem indexes             lvhittestinfo lvhti = { 0 };             lvhti.pt.x = rc.left;             lvhti.pt.y = rc.top;             listview_subitemhittest(hwndlv, &lvhti);             // edit control's text             wchar_t txt[50] = l"";             edit_gettext(hwnd, txt, 50);             // edit cell text             listview_setitemtext(hwndlv, lvhti.iitem, lvhti.isubitem, txt);             // restore focus listview             // triggers en_killfocus             // hide edit control             setfocus(hwndlv);         }             return 0l;         case vk_escape:             setfocus(getdlgitem(getparent(hwnd), 2000));             return 0l;         default:             return ::defsubclassproc(hwnd, message, wparam, lparam);         }         break;     case wm_ncdestroy:         ::removewindowsubclass(hwnd, inplaceeditcontrol_subclassproc, uidsubclass);         return defsubclassproc(hwnd, message, wparam, lparam);      }     return ::defsubclassproc(hwnd, message, wparam, lparam); } // main window procedure lresult callback wndproc(hwnd hwnd, uint msg, wparam wparam, lparam lparam) {     switch (msg)     {     case wm_create:     {         //================ create controls         rect rec = { 0 };         getclientrect(hwnd, &rec);          hwnd hwndlv = createwindowex(0, wc_listview,             l"", ws_child | ws_visible | ws_border | ws_clipsiblings | lvs_report,              50, 50, 250, 200, hwnd, (hmenu)2000, hinst, 0);         // in place edit control         hwnd hwndedit = createwindowex(0, wc_edit, l"", es_autohscroll | ws_child | ws_border,             200, 265, 100, 25, hwnd, (hmenu)5000, hinst, 0);         // edit control must have same font listview         hfont hf = (hfont)sendmessage(hwndlv, wm_getfont, 0, 0);         if (hf)             sendmessage(hwndedit, wm_setfont, (wparam)hf, (lparam)true);         // subclass edit control, can edit subitem enter, or         // cancel editing esc         setwindowsubclass(hwndedit, inplaceeditcontrol_subclassproc, 0, 0);         // set extended listview styles         listview_setextendedlistviewstyle(hwndlv, lvs_ex_fullrowselect | lvs_ex_gridlines | lvs_ex_doublebuffer);         // subclass listview         setwindowsubclass(hwndlv, listviewsubclassproc, 0, 0);          // add columns         lvcolumn lvc = { 0 };          lvc.mask = lvcf_fmt | lvcf_width | lvcf_text | lvcf_subitem;         lvc.fmt = lvcfmt_left;          (long nindex = 0; nindex < 5; nindex++)         {             wchar_t txt[50];             swprintf_s(txt, 50, l"column %d", nindex);              lvc.isubitem = nindex;             lvc.cx = 60;             lvc.psztext = txt;              listview_insertcolumn(hwndlv, nindex, &lvc);         }          // add items         lvitem lvi;          lvi.mask = lvif_text;          (lvi.iitem = 0; lvi.iitem < 10000; lvi.iitem++)         {             (long nindex = 0; nindex < 5; nindex++)             {                 wchar_t txt[50];                 swprintf_s(txt, 50, l"item %d%d", lvi.iitem, nindex);                  lvi.isubitem = nindex;                 lvi.psztext = txt;                  if (!nindex)  // item                      senddlgitemmessage(hwnd, 2000, lvm_insertitem, 0, reinterpret_cast<lparam>(&lvi));                 else          // sub-item                     senddlgitemmessage(hwnd, 2000, lvm_setitem, 0, reinterpret_cast<lparam>(&lvi));             }         }     }         return 0l;     case wm_notify:     {         if (((lpnmhdr)lparam)->code == nm_dblclk)           {             switch (((lpnmhdr)lparam)->idfrom)             {             case 2000: // remember, our listview's id             {                 lpnmitemactivate lpnmia = (lpnmitemactivate)lparam;                  // shift/alt/ctrl/their combination, must not pressed                 if ((lpnmia->ukeyflags || 0) == 0)                 {                     // store item/subitem rectangle                     rect rc = { 0, 0, 0, 0 };                     // helper values, needed handling partially visible items                     int topindex = listview_gettopindex(lpnmia->hdr.hwndfrom);                     int visiblecount = listview_getcountperpage(lpnmia->hdr.hwndfrom);                     // if item vertically partially visible, make visible                     if ((topindex + visiblecount) == lpnmia->iitem)                     {                         // rectangle of above item -> lpnmia->iitem - 1                         listview_getsubitemrect(lpnmia->hdr.hwndfrom, lpnmia->iitem - 1, lpnmia->isubitem, lvir_label, &rc);                         // ensure clicked item visible                         listview_ensurevisible(lpnmia->hdr.hwndfrom, lpnmia->iitem, false);                     }                     else // item visible, ectangle                         listview_getsubitemrect(lpnmia->hdr.hwndfrom, lpnmia->iitem, lpnmia->isubitem, lvir_label, &rc);                      rect rcclient = { 0 };  // listview client rectangle, needed if item partially visible                     getclientrect(lpnmia->hdr.hwndfrom, &rcclient);                     // item horizontally partially visible -> right side                     if (rcclient.right < rc.right)                       {                         // show whole item                         listview_scroll(lpnmia->hdr.hwndfrom, rc.right - rcclient.right, 0);                         // adjust rectangle edit control displayed                         rc.left -= rc.right - rcclient.right;                         rc.right = rcclient.right;                     }                     // item horizontally partially visible -> left side                     if (rcclient.left > rc.left)                       {                         // show whole item                         listview_scroll(lpnmia->hdr.hwndfrom, rc.left - rcclient.left, 0);                         // adjust rectangle edit control displayed                         rc.right += rcclient.left - rc.left;                         rc.left = rcclient.left;                     }                     // time position edit control, start getting window handle                     hwnd hwndedit = getdlgitem(hwnd, 5000);                     //  item text , set edit control's text                     wchar_t text[51];                     listview_getitemtext(lpnmia->hdr.hwndfrom, lpnmia->iitem, lpnmia->isubitem, text, 50);                     edit_settext(hwndedit, text);                     // select entire text                     edit_setsel(hwndedit, 0, -1);                     // map listview client rectangle parent rectangle                     // edit control can placed above item                     mapwindowpoints(lpnmia->hdr.hwndfrom, hwnd, (lppoint)&rc, (sizeof(rect) / sizeof(point)));                     // move edit control                     setwindowpos(hwndedit, hwnd_top, rc.left, rc.top, rc.right - rc.left,                          rc.bottom - rc.top, swp_showwindow);                     // set focus our edit control                     hwnd previouswnd = setfocus(hwndedit);                 }             }                 break;             default:                 break;             }         }     }         break;     case wm_close:         ::destroywindow(hwnd);         return 0l;     case wm_destroy:     {         ::postquitmessage(0);     }         return 0l;     default:         return ::defwindowproc(hwnd, msg, wparam, lparam);     }     return 0; }  // winmain int winapi winmain(hinstance hinstance, hinstance hprevinstance, lpstr lpcmdline,     int ncmdshow) {     // store hinstance in global variable later use     hinst = hinstance;      wndclassex wc;     hwnd hwnd;     msg msg;      // register main window class     wc.cbsize = sizeof(wndclassex);     wc.style = 0;     wc.lpfnwndproc = wndproc;     wc.cbclsextra = 0;     wc.cbwndextra = 0;     wc.hinstance = hinst;     wc.hicon = loadicon(hinstance, idi_application);     wc.hcursor = loadcursor(null, idc_arrow);     wc.hbrbackground = getsyscolorbrush(color_window);     wc.lpszmenuname = null;     wc.lpszclassname = l"main_window";     wc.hiconsm = loadicon(hinstance, idi_application);      if (!registerclassex(&wc))     {         messagebox(null, l"window registration failed!", l"error!",              mb_iconexclamation | mb_ok);          return 0;     }      // initialize common controls     initcommoncontrolsex iccex;     iccex.dwsize = sizeof(initcommoncontrolsex);     iccex.dwicc = icc_listview_classes | icc_standard_classes;     initcommoncontrolsex(&iccex);      // create main window     hwnd = createwindowex(0, l"main_window", l"grid control",         ws_overlappedwindow, 50, 50, 400, 400, null, null, hinstance, 0);      showwindow(hwnd, ncmdshow);     updatewindow(hwnd);      while (getmessage(&msg, null, 0, 0) > 0)     {         translatemessage(&msg);         dispatchmessage(&msg);     }     return msg.wparam; } 

update:

on second thought, method posted earlier wrong. think it's design error use setcapture in edit-box that, can interfere bunch of other things. going delete old answer , pretend nobody saw it!

your own method fine checking killfocus, need subclass listview check scroll messages mimic lvn_xxxlabeledit

void hideedit(bool save) {     //save or not...     showwindow(hedit, sw_hide); }  lresult callback editproc... {     if (msg == wm_killfocus)         hideedit(1);      if (msg == wm_char)     {         if (wparam == vk_escape){             hideedit(0);             return 0;         }         if (wparam == vk_return){             hideedit(1);             return 0;         }     }      return defsubclassproc(...); }  lresult callback listproc... {     if (msg == wm_vscroll || msg == wm_hscroll) hideedit(1);     return defsubclassproc(...); } 

Comments

Popular posts from this blog

c++ - Difference between pre and post decrement in recursive function argument -

php - Nothing but 'run(); ' when browsing to my local project, how do I fix this? -

php - How can I echo out this array? -