View a list of subclasses to edit only its subclauses

I wrote this small program that displays a list view and edits elements and subelements.

I want to change this to only edit subitems. And I would like to do the procedure of the list view window myself, that I do not need to forward WM_NOTIFY messages every time I do it now in WndProcMain . And the goal is that I do not use only one list view with editable sub-elements in my program, I will use it in many different windows.

The LVN_ENDLABELEDIT notification LVN_ENDLABELEDIT handled by WndProcList because the value of bEditing must be changed. This flag is used for WM_PAINT when subtitles should be edited. This is a correction, otherwise the text in the first subparagraph disappears because he thinks that the first element is being edited. However, I would also like to receive a message of type LVN_ENDLABELEDIT in the window owner window window procedure (in this case WndProcMain ), because I want to manipulate user input as well.

Ask if you have any questions.

Thank you in advance

Midas

 WNDPROC wpOrigEditProc; RECT rcSubItem; LRESULT CALLBACK WndProcEditList(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_WINDOWPOSCHANGING: { WINDOWPOS *pos = (WINDOWPOS*) lParam; pos->x = rcSubItem.left; pos->cx = rcSubItem.right; } break; default: return CallWindowProc(wpOrigEditProc, hWnd, uMsg, wParam, lParam); } return 1; } LRESULT CALLBACK WndProcList(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HWND hEdit; static RECT rc; static LVITEM lvI; static unsigned char bEditing = 0; switch (uMsg) { case WM_NOTIFY: switch (((NMHDR*) lParam)->code) { case NM_CLICK: lvI.iItem = ((NMITEMACTIVATE*) lParam)->iItem; lvI.iSubItem = ((NMITEMACTIVATE*) lParam)->iSubItem; break; case NM_DBLCLK: SendMessage(hWnd, LVM_EDITLABEL, lvI.iItem, 0); break; case LVN_BEGINLABELEDIT: { char text[32]; bEditing = 1; hEdit = (HWND) SendMessage(hWnd, LVM_GETEDITCONTROL, 0, 0); rcSubItem.top = lvI.iSubItem; rcSubItem.left = LVIR_LABEL; SendMessage(hWnd, LVM_GETSUBITEMRECT, lvI.iItem, (long) &rcSubItem); rcSubItem.right = SendMessage(hWnd, LVM_GETCOLUMNWIDTH, lvI.iSubItem, 0); wpOrigEditProc = (WNDPROC) SetWindowLong(hEdit, GWL_WNDPROC, (long) WndProcEditList); lvI.pszText = text; lvI.cchTextMax = 32; SendMessage(hWnd, LVM_GETITEMTEXT, lvI.iItem, (long) &lvI); SetWindowText(hEdit, lvI.pszText); } break; case LVN_ENDLABELEDIT: bEditing = 0; SetWindowLong(hEdit, GWL_WNDPROC, (long) wpOrigEditProc); if (!lvI.iSubItem) return 1; lvI.pszText = ((NMLVDISPINFO*) lParam)->item.pszText; if (!lvI.pszText) return 1; SendMessage(hWnd, LVM_SETITEMTEXT, lvI.iItem, (long) &lvI); break; default: return CallWindowProc((WNDPROC) GetClassLong(hWnd, GCL_WNDPROC), hWnd, uMsg, wParam, lParam); } break; case WM_PAINT: if (bEditing) { RECT rcItem; if (lvI.iSubItem > 0) { rcItem.left = LVIR_LABEL; if (SendMessage(hWnd, LVM_GETITEMRECT, lvI.iItem, (long) &rcItem)) ValidateRect(hWnd, &rcItem); } } default: return CallWindowProc((WNDPROC) GetClassLong(hWnd, GCL_WNDPROC), hWnd, uMsg, wParam, lParam); } return 0; } LRESULT CALLBACK WndProcMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HWND hList; static RECT rc; switch (uMsg) { case WM_NOTIFY: switch (((NMHDR*) lParam)->code) { case NM_CLICK: case NM_DBLCLK: case LVN_BEGINLABELEDIT: case LVN_ENDLABELEDIT: return CallWindowProc(WndProcList, ((NMHDR*) lParam)->hwndFrom, uMsg, wParam, lParam); } break; case WM_CREATE: { LVCOLUMN lvc; LVITEM lvI; unsigned int i; float vertex; char text[32]; hList = CreateWindow(WC_LISTVIEW, 0, WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_EDITLABELS, rc.left, rc.top, rc.right, rc.bottom, hWnd, 0, hInstance, 0); SendMessage(hList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); SetWindowLong(hList, GWL_WNDPROC, (long) WndProcList); lvc.mask = LVCF_WIDTH; lvc.cx = 30; SendMessage(hList, LVM_INSERTCOLUMN, 0, (LPARAM) &lvc); lvc.mask = LVCF_TEXT; lvc.pszText = "Vertex"; SendMessage(hList, LVM_INSERTCOLUMN, 1, (LPARAM) &lvc); SendMessage(hList, LVM_SETCOLUMNWIDTH, 1, LVSCW_AUTOSIZE_USEHEADER); lvI.mask = LVIF_TEXT; lvI.pszText = text; for (i = 0; i < 10; i++) { vertex = (float) i; lvI.iItem = i; lvI.iSubItem = 0; sprintf(text, "%d", i); SendMessage(hList, LVM_INSERTITEM, 0, (LPARAM) &lvI); lvI.iSubItem = 1; sprintf(text, "%f, %f, %f", vertex - 1, vertex, vertex + 1); SendMessage(hList, LVM_SETITEM, 0, (LPARAM) &lvI); } } break; case WM_SIZE: GetClientRect(hWnd, &rc); MoveWindow(hList, rc.left, rc.top, rc.right, rc.bottom, 1); SendMessage(hList, LVM_SETCOLUMNWIDTH, 1, LVSCW_AUTOSIZE_USEHEADER); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } 
+4
source share
2 answers

I sent an answer related to the question, but it was in C #. There was never much experience in winapi, and had not played with him as an amateur for a long time. The answer at the end of this post looks promising - http://cboard.cprogramming.com/windows-programming/122733-%5Bc%5D-editing-subitems-listview-win32-api.html

+1
source

The first problem that occurred to me in trying to compile your code is the fact that you either do not compile Unicode or use the non-Unicode sprintf function to format the text. This is the first thing you need to fix: Windows applications have been fully running Unicode for over a decade. Replace each instance of char declarations with wchar_t (or TCHAR ), prefix string literals with L (or surround them with the TEXT() macro) and quickly replace the calls with sprintf with wsprintf . As the documentation indicates, there are, of course, more efficient functions than wsprintf , but the same is true for sprintf , and this allows you to compile the code with minimal effort.

Another thing that seems non-idiomatic to me is using the Get / SetClassLong and Get / SetWindowLong . Currently, I always write code with 64-bit portability, so I would replace them with Get / SetClassLongPtr and Get / SetWindowLongPtr macros that automatically allow the correct function call, depending on whether you compile for x86 or x64. However, this is not a bargain.

You first asked if there was a way to process the WM_NOTIFY message directly from a subclass control by forwarding them automatically. Unfortunately this is not possible. The Win32 model is such that parents always own their children, and therefore they are responsible for handling the events. I agree with your intuition regarding separation of concerns, but the only way to do this is to explicitly redirect the message from the parent to the appropriate child control. Structures like MFC (which encapsulate the Win32 API) do this for you, apparently automatically, you still have to redirect notifications from parent to child. They do this using something called "message reflection", which you can read here . There is nothing stopping you from implementing something similar in your own application, but at some point you need to stop and ask yourself if you should use one of the many available graphical interfaces only for this kind of thing.

In any case, as I understand it, the main question of your question is this:

I want to change this to only edit subitems.

This seems like a pretty simple fix. All you have to do is check the LVN_BEGINLABELEDIT message LVN_BEGINLABELEDIT , which the user actually asked to edit the subitem. Since you used it elsewhere in your code, you know that the LVITEM.iSubItem element provides you with either one index index, item, or 0 if the structure refers to the element and not to the sub-item.

So insert this line so that lvI.iSubItem not 0 at the top of the LVN_BEGINLABELEDIT handler:

 if (lvI.iSubItem == 0) return TRUE; // prevent editing 

As indicated in the documentation for the LVN_BEGINLABELEDIT message, returning FALSE allows the user to edit the label and returning TRUE prevents them from being edited. Since we return TRUE , we prevent editing anything other than subitems before editing.

It seems to me that you have already tried to do something similar in the LVN_ENDLABELEDIT message LVN_ENDLABELEDIT with this line:

 if (!lvI.iSubItem) return 1; 

but it's too late! If editing is already completed, you have already given the user the impression that they were able to edit the main element that you do not want to do. Take this line and it will work as expected.

Please note that your implementation has at least one blatant mistake: you do not prohibit the user from changing the contents of the subtitle to a line longer than 32 characters, but your code that fills the edit control accepts only a line up to 32 characters long:

 TCHAR text[32]; // ... snip ... lvI.pszText = text; lvI.cchTextMax = 32; SendMessage(hWnd, LVM_GETITEMTEXT, lvI.iItem, (long) &lvI); SetWindowText(hEdit, lvI.pszText); 

Writing this code will be correct (and I suspect why you didn’t), a huge pain in the ass. As a rule, I will create a string buffer that, it seems to me, is long enough, try to get the text of the subitem and check the return value of the LVM_GETITEMTEXT message. The return value tells me how many characters were copied to the string buffer. If the number of characters to be copied indicates that it completely filled the available space in the string buffer, I will make the buffer larger (maybe doubled the size), and then try to send the LVM_GETITEMTEXT message again. As far as I remember, MFC does something similar. Told you that it was a pain, but it would be worth it to get everything right.

A simpler solution (albeit a more limited one) would be to prohibit the user from setting the length of one of the subitems to a text string longer than 32 characters. Then you do not have to worry about how to handle long entries, because you know that it will never be, and the user will never be confused with the behavior of your control. To do this, send the EM_LIMITTEXT control a message at the end of the LVN_BEGINLABELEDIT handler:

 case LVN_BEGINLABELEDIT: { // ... snip ... lvI.cchTextMax = 32; SendMessage(hWnd, LVM_GETITEMTEXT, lvI.iItem, (long) &lvI); SetWindowText(hEdit, lvI.pszText); // (begin new code) SendMessage(hEdit, EM_LIMITTEXT, lvI.cchTextMax, 0); } 

Now the user cannot enter more than the permissible number of characters, so your code knows that you will never have to deal with something more than there (if you do not write the code to place them yourself, in this case ... )


All of the above, I think, I agree with Hans:

Wow, you'll fight glitches forever. There is little point with universal grid controls.

+1
source

Source: https://habr.com/ru/post/1388326/


All Articles