Results 1 to 10 of 10

Thread: Editable Listview control

  1. #1
    Red wine, not vodka! ZaiRoN's Avatar
    Join Date
    Oct 2001
    Location
    Italy
    Posts
    922
    Blog Entries
    17

    Editable Listview control

    Few days ago I started renewing my PE editor's gui, I wanted to replace some edit box controls with a listview control. Everything was going well until I had to edit the value inside a cell. With the original listview control you can change the text of the first subitem of a row only, but I would like to edit every single subitem. How can I solve the problem? I didn't want to waste time solving the problem so I decided to take a look at the usual programming places starting from Code Project. Hm, nothing. I'm not so good in searching information through the net, but seems like there are working samples on mfc, .net and vb only. No win32 programming stuff... Well, I decided to give it a try subclassing the control.
    I have never subclass-ed a control before, it's my first try. I don't know if there's a better approach. I don't even know if it's the correct way to solve the problem, but it seems to works well. Let's start.

    The steps to follow are:
    1. Create an edit box that will be used to insert the new text
    2. Set the new window procedure able to handle edit control's messages
    3. Apply/abort text modification

    I use VS creating a win32 project. Add a listview control to your dialog setting "Edit labels" to FALSE. If you set the option to TRUE the OS will handle subitem modification, I prefer to avoid this behaviour.

    The idea is to change the subitem's text when a double click occours. I catch the event in the main window procedure calling the function (named SubClass_ListView_Editable) which subclasses the control.

    The first step consists of creating the edit box over the clicked subitem. To create the edit box I need to know where to put it. LVM_GETSUBITEMRECT returns information about the rectangle for a subitem of a listview control. With this information I can create the new control:
    Code:
    ListView_GetSubItemRect(hListView, _lParam->iItem, _lParam->iSubItem, LVIR_LABEL, &r);
    //    Time to create the new edit box
    hEditable = CreateWindowEx(0, "EDIT", "Edit me", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE, r.left, r.top, r.right-r.left, r.bottom-r.top, _lParam->hdr.hwndFrom, NULL, hInst, 0);
    "r" is defined as a RECT structure:

    Code:
    typedef struct _RECT {
    LONG left;        //      x-coordinate of the upper-left corner of the rectangle
    LONG top;        //    y-coordinate of the upper-left corner of the rectangle
    LONG right;      //    x-coordinate of the lower-right corner of the rectangle
    LONG bottom;   //    y-coordinate of the lower-right corner of the rectangle
    } RECT, *PRECT;
    As you can see I use "r" inside CreateWindowEx function specifying the coordinates of the new control.
    The third parameter of CreateWindowEx is the text that will be shown in the control. I use a static text but you can leave it blank or display the subitem's text, it's up to you. Now, the new control has been created and I'm going to set some features:

    Code:
    SendMessage(hEditable, EM_LIMITTEXT, 8, 0);        //    It accepts no more than 8 chars
    SendMessage(hEditable, EM_SETSEL, 0, 8);        //    Text selected
    SetFocus(hEditable);                    //    Focus to the new box
    If you don't need a particular behaviour (limit text, accept only numbers...) you can avoid the first two calls but I think the third one is useful, it gives the focus to the new edit box.

    The control is complete, I have to add the new window procedure. This can be done using SetWindoLong function:

    Code:
    LONG SetWindowLong(
    HWND hWnd,    //    Handle of the new edit control
    int nIndex,        //    The attribute to change
    LONG dwNewLong    //    The new value
    );
    The function changes an attribute of a specified window, in this case I'm going to change the address of the dialog procedure. The aim is to add a new window procedure for handling edit box's messages only. I'll pass over all the other messages forwarding them towards the old window procedure.

    Code:
    wpOld = (WNDPROC)SetWindowLong(hEditable, GWL_WNDPROC, SubClass_ListView_WndProc);
    SetProp(hEditable, "WP_OLD", (HANDLE)wpOld);
    SubClass_ListView_WndProc represents the new dialog procedure.
    I have to save the address of the original window procedure because I have to restore it when I'll destroy the edit box. To save the address I use SetProp function, but if you prefer you can use global variables. To end this piece of code I save some more useful information: row and column of the subitem to change:

    Code:
    SetProp(hEditable, "ITEM", (HANDLE)_lParam->iItem);
    SetProp(hEditable, "SUBITEM", (HANDLE)_lParam->iSubItem);
    Which kind of messages will I have to catch? WM_KEYDOWN and WM_DESTROY only, all the other messages are passed to the original window procedure in this way:

    Code:
    return CallWindowProc((WNDPROC)GetProp(hEditable, "WP_OLD"), hwnd, uMsg, wParam, lParam);
    I catch WM_KEYDOWN because I have to handle ENTER ans ESC key. When you hit ENTER the text will be saved in the subitem, and when you click ESC the operation will be aborted:

    Code:
    case WM_KEYDOWN:
    if (LOWORD(wParam) == VK_RETURN)
    {
    ...
    // Item and suibtem to change
    LvItem.iItem = GetProp(hEditable, "ITEM");
    LvItem.iSubItem = GetProp(hEditable, "SUBITEM");
    // Where to store the new text
    LvItem.pszText = text;
    // Get new text and set it in the subitem
    GetWindowText(hEditable, text, sizeof(text));
    SendMessage(hListView, LVM_SETITEMTEXT, (WPARAM)GetProp(hEditable, "ITEM"), (LPARAM)&LvItem);
    DestroyWindow(hEditable);
    }
    else if (LOWORD(wParam) == VK_ESCAPE)
    DestroyWindow(hEditable);
    break;
    If you press ESC the edit box will be destroyed without changing the subitem's text.
    When ENTER is pressed I simply get the text storing it in the subitem. After that I can destroy the edit box.
    There's something more to say about VK_RETURN. Look at CreateWindowEx parameters, there's a ES_MULTILINE value. The value is necessary otherwise the application refuses to catch ENTER.
    The last thing I check in the new window procedure is WM_DESTROY message, I simply remove the saved properties and then I restore the original window procedure calling SetWindowLong again.

    What about mouse click when I'm changing the subitem's value? It's like VK_ESCAPE key, I remove the edit box aborting the operation. See the attached source code for details.

    The picture represents the subclassing method in action:


    The code can be optimized for sure, just fix/change/remove/add everything you need.
    Feel free to post your comment/suggestion/criticism here.
    Download the VS project from http://www.box.net/shared/static/kcurtvm8v7.zip

    Game over!

  2. #2
    Administrator dELTA's Avatar
    Join Date
    Oct 2000
    Location
    Ring -1
    Posts
    4,206
    Blog Entries
    5
    Nice tutorial ZaiRoN, appreciated as always.
    "Give a man a quote from the FAQ, and he'll ignore it. Print the FAQ, shove it up his ass, kick him in the balls, DDoS his ass and kick/ban him, and the point usually gets through eventually."

  3. #3
    And thanks for sharing with out Readers.

    Regards,
    JMI

  4. #4
    Registered User
    Join Date
    Oct 2003
    Location
    Bulgaria
    Posts
    16
    Hello Zairon,

    I don't know the reason why you have chosen this way to write your app. You could make use of C++, WTL(even MFC)....
    If you don't believe THIS is the right way to write huge programs then it is OK. (I hope asm fans are not reading this )
    Having said this, let me say a few words about the presented solution.

    1. Using an edit box to edit all the fields in a listview is the correct and commonly used approach

    2. Subclassing is a very powerful technique used for many purposes. But for your purpose it is still possible to do your job without it. How?

    - Take into account the following:
    If a dialog box or one of its controls currently has the input focus, then pressing the ENTER key causes Windows to send a WM_COMMAND message with the idItem (wParam) parameter set to the ID of the default command button. If the dialog box does not have a default command button, then the idItem parameter is set to IDOK by default.

    When an application receives the WM_COMMAND message with idItem set to the ID of the default command button, the focus remains with the control that had the focus before the ENTER key was pressed. Calling GetFocus() at this point returns the handle of the control that had the focus before the ENTER key was pressed. The application can check this control handle and determine whether it belongs to any of the edit controls in the dialog box.

    - Remove the ES_MULTILINE style, because you don't want to stop the above mechanism

    - If you need the EN_*** notifications in your DialogProc, make the dialog the parent of the edit box.

    Got the idea?
    (If not PM me since this is a reversing, not a programming forum and I don't want to torture forum members with my english )

    Regards,
    begemott
    Last edited by begemott; November 7th, 2007 at 09:55.

  5. #5
    Red wine, not vodka! ZaiRoN's Avatar
    Join Date
    Oct 2001
    Location
    Italy
    Posts
    922
    Blog Entries
    17
    Hello begemott.
    thanks for your comment.
    Using an edit box to edit all the fields in a listview is the correct and commonly used approach
    I don't want another edit box in the dialog, I wanted to edit fields on the fly. That's why I subclass...

  6. #6
    Registered User
    Join Date
    Oct 2003
    Location
    Bulgaria
    Posts
    16
    Hello Zairon,

    Please find attached my changes to main.cpp. It illustrates what I would like to say.

    Regards!
    begemott
    Attached Files Attached Files

  7. #7
    Red wine, not vodka! ZaiRoN's Avatar
    Join Date
    Oct 2001
    Location
    Italy
    Posts
    922
    Blog Entries
    17
    Hello begemott.
    Ah, now I understand what you wanted to say. Yes, you are right saying subclassing is useless. Thank you very much

  8. #8
    If the listview was larger and had scrollbars, your edit control wouldn't move with the cell and just stay there floating...

    More seriously, this:

    Doubleclick on item (0,0).
    Click on item (1,0).
    Receive exception:
    Code:
    EDITABLE_LISTVIEW caused an invalid page fault in
    module <unknown> at 0084:00000000.
    Registers:
    EAX=00000000 CS=016f EIP=00000000 EFLGS=00010286
    EBX=0063f246 SS=0177 ESP=0063f1a8 EBP=0063f214
    ECX=0040104c DS=0177 ESI=0000826a FS=3c87
    EDX=80007b90 ES=0177 EDI=0063f1fc GS=0000
    Bytes at CS:EIP:
    00 00 00 00 65 04 70 00 16 00 cd 06 00 80 00 00 
    Stack dump:
    0040104c 000008f8 00000082 00000000 00000000 0000826a 0063f268 0063f234 00401175 000008f8 fffffffc 00000000 00000007 0040118b 10e90000 00000903
    EIP=00000000...?

    I think the reason why you don't find articles on it is because it's simpler to have a separate edit box in the dialog and use that to change the entries in the ListView instead of trying to superimpose an edit control onto a ListView entry. Even M$ Excel doesn't use a ListView.

  9. #9
    Red wine, not vodka! ZaiRoN's Avatar
    Join Date
    Oct 2001
    Location
    Italy
    Posts
    922
    Blog Entries
    17
    The exception doesn't occur here... what's the OS you are running on?

  10. #10
    Win98SE. I suppose your OS (XP? 2000?) is being more "lenient" in handling the exception, i.e. ignores it and continues or something (leading you to the impression that nothing wrong happened)

    Try checking the code at 0040104c -- that's the last address inside your app indicated by the stack dump, and corresponds to a "pop esi" instruction:
    Code:
    00401046 FF15F8604000       call      CallWindowProcA
    0040104C 5E                 pop       esi

Similar Threads

  1. Reconstruct Delphi control classes
    By Cherry in forum Advanced Reversing and Programming
    Replies: 4
    Last Post: May 9th, 2009, 11:39
  2. Taking control of a prog
    By ReVeR in forum The Newbie Forum
    Replies: 9
    Last Post: December 22nd, 2004, 13:18
  3. TCB - Thread control block
    By Necr0Potenc3 in forum OllyDbg Support Forums
    Replies: 1
    Last Post: June 13th, 2004, 23:03
  4. Change control properties in VC++
    By !oz! in forum Advanced Reversing and Programming
    Replies: 3
    Last Post: May 28th, 2004, 15:36

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •