c – 使用可编辑的子项正确处理listview中的子项目编辑(或取消子
介绍:
我正在尝试使用可编辑的子项实现listview控件.对于项目/子项目的就地编辑,我使用编辑控件. 我相信我已经成功地将编辑控件放置在item / subitem上面. 问题: 我不知道我应该在哪些事件上结束/取消子项目编辑(隐藏编辑控件,设置子项目文本等)以及我该怎么做. 为了澄清,我说的是用户完成/取消现场编辑的时刻. 此时不再需要编辑控件,所以我应该隐藏它(我不喜欢每次都重新创建它;我相信创建它一次然后在需要时显示/隐藏它更有效). 我的目标是Visual Studio中的属性窗口的行为(请参阅附图,以查看我所指的窗口). ? 当用户按下ESC键/点击另一个窗口/点击滚动条等时,我希望以与此窗口相同的方式实现编辑/取消. 我努力解决这个问题: 使用谷歌,我发现很少的例子,但它们已经陈旧,并没有解决所有相关案例,所以这就是我在这里寻求帮助的原因. 但是,我能够发现我必须考虑的事件之一是 编辑: 我设法处理ESC和ENTER键,以及用户点击另一个兄弟控件或用ALT TAB切换窗口的情况.我已经更新了SSCCE的相关更改 题: 为了实现网格的默认行为(如果有一个用于Windows应用程序),我必须处理哪些消息/事件? 你还能指出我应该在哪里编辑子项并隐藏编辑控件,我应该在哪里隐藏编辑控件? 编辑: 我唯一的问题是当用户点击listview滚动条或主窗口的背景时处理这种情况.我只是不知道如何处理这个,并希望得到我能得到的所有帮助. 相关信息: 我在Windows 7 x86上使用Visual Studio 2013; 我使用原始WinAPI在C中开发; SSCCE: 以下是我到目前为止的解决方案.我试图彻底评论它,但如果需要更多信息,请留言,我将更新我的帖子. #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 with 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 the focus take it away and give to listview if (GetFocus() == GetDlgItem(GetParent(hwnd),5000)) SetFocus(hwnd); // use WM_NEXTDLGCTL for dialogbox !!!! break; case WM_NCDESTROY: ::RemoveWindowSubclass(hwnd,ListViewSubclassProc,uIdSubclass); return DefSubclassProc(hwnd,message,wParam,lParam); } return ::DefSubclassProc(hwnd,lParam); } // subclass procedure for edit control LRESULT CALLBACK InPlaceEditControl_SubclassProc(HWND hwnd,DWORD_PTR dwRefData) { switch (message) { case WM_GETDLGCODE: return (DLGC_WANTALLKEYS | DefSubclassProc(hwnd,lParam)); case WM_KILLFOCUS: ShowWindow(hwnd,SW_HIDE); return DefSubclassProc(hwnd,lParam); case WM_CHAR: //Process this message to avoid message beeps. switch (wParam) { case VK_RETURN: return 0L; case VK_ESCAPE: return 0L; default: return ::DefSubclassProc(hwnd,lParam); } break; case WM_KEYDOWN: switch (wParam) { case VK_RETURN: { // get listview handle HWND hwndLV = GetDlgItem(GetParent(hwnd),2000); // get edit control's client rectangle RECT rc = { 0 }; GetClientRect(hwnd,&rc); // since edit control lies inside item rectangle // we can test any coordinate inside edit control's // client rectangle // I chose ( rc.left,rc.top ) MapWindowPoints(hwnd,hwndLV,(LPPOINT)&rc,(sizeof(RECT) / sizeof(POINT))); // get item and subitem indexes LVHITTESTINFO lvhti = { 0 }; lvhti.pt.x = rc.left; lvhti.pt.y = rc.top; ListView_SubItemHitTest(hwndLV,&lvhti); // get 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 to listview // this triggers EN_KILLFOCUS // which will hide edit control SetFocus(hwndLV); } return 0L; case VK_ESCAPE: SetFocus(GetDlgItem(GetParent(hwnd),2000)); return 0L; default: return ::DefSubclassProc(hwnd,lParam); } break; case WM_NCDESTROY: ::RemoveWindowSubclass(hwnd,InPlaceEditControl_SubclassProc,lParam); } return ::DefSubclassProc(hwnd,lParam); } // main window procedure LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,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,250,200,hwnd,(HMENU)2000,hInst,0); // in place edit control HWND hwndEdit = CreateWindowEx(0,WC_EDIT,ES_AUTOHSCROLL | WS_CHILD | WS_BORDER,265,100,25,(HMENU)5000,0); // edit control must have the same font as listview HFONT hf = (HFONT)SendMessage(hwndLV,WM_GETFONT,0); if (hf) SendMessage(hwndEdit,WM_SETFONT,(WPARAM)hf,(LPARAM)TRUE); // subclass edit control,so we can edit subitem with ENTER,or // cancel editing with ESC SetWindowSubclass(hwndEdit,0); // set extended listview styles ListView_SetExtendedListViewStyle(hwndLV,LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER); // subclass listview SetWindowSubclass(hwndLV,0); // add some columns LVCOLUMN lvc = { 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,L"Column %d",nIndex); lvc.iSubItem = nIndex; lvc.cx = 60; lvc.pszText = txt; ListView_InsertColumn(hwndLV,nIndex,&lvc); } // add some items LVITEM lvi; lvi.mask = LVIF_TEXT; for (lvi.iItem = 0; lvi.iItem < 10000; lvi.iItem++) { for (long nIndex = 0; nIndex < 5; nIndex++) { wchar_t txt[50]; swprintf_s(txt,L"Item %d%d",lvi.iItem,nIndex); lvi.iSubItem = nIndex; lvi.pszText = txt; if (!nIndex) // item SendDlgItemMessage(hwnd,2000,LVM_INSERTITEM,reinterpret_cast<LPARAM>(&lvi)); else // sub-item SendDlgItemMessage(hwnd,LVM_SETITEM,reinterpret_cast<LPARAM>(&lvi)); } } } return 0L; case WM_NOTIFY: { if (((LPNMHDR)lParam)->code == NM_DBLCLK) { switch (((LPNMHDR)lParam)->idFrom) { case 2000: // remember,this was our listview's ID { LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam; // SHIFT/ALT/CTRL/their combination,must not be pressed if ((lpnmia->uKeyFlags || 0) == 0) { // store item/subitem rectangle RECT rc = { 0,0 }; // helper values,needed for handling partially visible items int topIndex = ListView_GetTopIndex(lpnmia->hdr.hwndFrom); int visibleCount = ListView_GetCountPerPage(lpnmia->hdr.hwndFrom); // if item is vertically partially visible,make it fully visible if ((topIndex + visibleCount) == lpnmia->iItem) { // get the rectangle of the above item -> lpnmia->iItem - 1 ListView_GetSubItemRect(lpnmia->hdr.hwndFrom,lpnmia->iItem - 1,lpnmia->iSubItem,LVIR_LABEL,&rc); // ensure clicked item is visible ListView_EnsureVisible(lpnmia->hdr.hwndFrom,lpnmia->iItem,FALSE); } else // item is fully visible,just get its ectangle ListView_GetSubItemRect(lpnmia->hdr.hwndFrom,&rc); RECT rcClient = { 0 }; // listview client rectangle,needed if item partially visible GetClientRect(lpnmia->hdr.hwndFrom,&rcClient); // item is horizontally partially visible -> from the right side if (rcClient.right < rc.right) { // show the whole item ListView_Scroll(lpnmia->hdr.hwndFrom,rc.right - rcClient.right,0); // adjust rectangle so edit control is properly displayed rc.left -= rc.right - rcClient.right; rc.right = rcClient.right; } // item is horizontally partially visible -> from the left side if (rcClient.left > rc.left) { // show the whole item ListView_Scroll(lpnmia->hdr.hwndFrom,rc.left - rcClient.left,0); // adjust rectangle so edit control is properly displayed rc.right += rcClient.left - rc.left; rc.left = rcClient.left; } // it is time to position edit control,we start by getting its window handle HWND hwndEdit = GetDlgItem(hwnd,5000); // get item text and set it as edit control's text wchar_t text[51]; ListView_GetItemText(lpnmia->hdr.hwndFrom,text,50); Edit_SetText(hwndEdit,text); // select entire text Edit_SetSel(hwndEdit,-1); // map listview client rectangle to parent rectangle // so edit control can be properly placed above the item MapWindowPoints(lpnmia->hdr.hwndFrom,(sizeof(RECT) / sizeof(POINT))); // move the edit control SetWindowPos(hwndEdit,HWND_TOP,rc.left,rc.top,rc.right - rc.left,rc.bottom - rc.top,SWP_SHOWWINDOW); // set focus to 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,lParam); } return 0; } // WinMain int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) { // store hInstance in global variable for 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,400,NULL,hInstance,0); ShowWindow(hwnd,nCmdShow); UpdateWindow(hwnd); while (GetMessage(&Msg,0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } 解决方法
更新:
第二个想法,我之前发布的方法是错误的.我认为在编辑框中使用SetCapture是一种设计错误,它会干扰其他一些事情.我要删除旧答案,假装没有人看到它! 你自己的方法可以检查KILLFOCUS,你只需要ListView的子类来检查滚动消息来模仿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(...); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |