Announcement

Collapse
No announcement yet.

editable listview problem

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • editable listview problem

    The code below (a compileable example, PBWin 9 and probably 8, no dependencies) appears to work, but when I transfer the "moving parts" into another application, the NM_DBLCLK is no longer trapped. I think that I have copied all the relevant stuff over, listview and dialog styles, listview initialisation code, and the lveditcell and lv_editproc functions.

    Either I've missed something (quite likely) or the code show contains a flaw which makes it non-transportable in this case. But I can't see it!

    Code:
    ' EDIT a listview
    ' Chris Holbrook Sep 2008
    '
    #COMPILE EXE
    #DIM ALL
    #INCLUDE "WIN32API.INC"
    #INCLUDE "COMMCTRL.INC"
    %IDD_DIALOG1             =  101
    %IDC_LV                     = 1001
    %IDC_EDITLVSUBITEM   = 1002
    %IDC_LABEL1               = 1003
    
    '-------------------------------------------------------------------------------------
    FUNCTION SampleListView(BYVAL hDlg AS DWORD, BYVAL lID AS LONG, BYVAL lColCnt AS LONG, BYVAL lRowCnt AS LONG) AS LONG
        LOCAL lCol   AS LONG
        LOCAL lRow   AS LONG
        LOCAL hCtl   AS DWORD
        LOCAL tLVC   AS LV_COLUMN
        LOCAL tLVI   AS LV_ITEM
        LOCAL szBuf  AS ASCIIZ * 32
        LOCAL lStyle AS LONG
    
        CONTROL HANDLE hDlg, lID TO hCtl
    
        lStyle = ListView_GetExtendedListViewStyle(hCtl)
        ListView_SetExtendedListViewStyle hCtl, lStyle OR %LVS_EX_FULLROWSELECT OR %LVS_EX_GRIDLINES
    
        ' Load column headers.
        tLVC.mask    = %LVCF_FMT OR %LVCF_TEXT OR %LVCF_SUBITEM
        tLVC.fmt     = %LVCFMT_LEFT
        tLVC.pszText = VARPTR(szBuf)
        FOR lCol = 0 TO lColCnt - 1
            szBuf       = USING$("Column #", lCol)
            tLVC.iOrder = lCol
            ListView_InsertColumn hCtl, lCol, tLVC
        NEXT lCol
    
        ' Load sample data.
        FOR lRow = 0 TO lRowCnt - 1
            tLVI.stateMask = %LVIS_FOCUSED
            tLVI.pszText   = VARPTR(szBuf)
            tLVI.iItem     = lRow
            FOR lCol = 0 TO lColCnt - 1
                szBuf         = USING$("Column # Row #", lCol, lRow)
                tLVI.iSubItem = lCol
                tLVI.lParam   = lRow
                IF lCol = 0 THEN
                    tLVI.mask = %LVIF_TEXT OR %LVIF_PARAM OR %LVIF_STATE
                    ListView_InsertItem hCtl, tLVI
                ELSE
                    tLVI.mask = %LVIF_TEXT
                    ListView_SetItem hCtl, tLVI
                END IF
            NEXT lCol
        NEXT lRow
    
        ' Auto size columns.
        FOR lCol = 0 TO lColCnt - 2
            ListView_SetColumnWidth hCtl, lCol, %LVSCW_AUTOSIZE
        NEXT lCol
        ListView_SetColumnWidth hCtl, lColCnt - 1, %LVSCW_AUTOSIZE_USEHEADER
    END FUNCTION
    
    '------------------------------------------------------------------------------
     FUNCTION LV_EditProc(BYVAL hCtl AS LONG, _
                        BYVAL Msg AS LONG, _
                        BYVAL wParam AS LONG, _
                        BYVAL lParam AS LONG) EXPORT AS LONG
    
        LOCAL tLVI                          AS LV_ITEM
        LOCAL l                             AS LONG
        STATIC lvrow, lvcol                 AS INTEGER
        STATIC hwnd, hLVwnd                       AS DWORD
        STATIC dwOrigEditProc               AS DWORD
        STATIC sz                           AS ASCIIZ * 255
    
        IF hCtl = 0 THEN           ' initial messages from parent
            dwOrigEditProc = wparam
            lvrow = HIWRD(lparam): lvcol = LOWRD(lparam)
            FUNCTION = 1
            EXIT FUNCTION
        END IF
    
        SELECT CASE Msg
            '
            CASE %WM_KEYUP
                SELECT CASE wParam
                    CASE %VK_ESCAPE
                        getprop getparent(hCtl), "LVWND" TO hLVWnd
                        enablewindow hLVWnd, %TRUE
                        destroywindow hCtl
                    CASE %VK_RETURN
                        sendmessage ( hCtl, %WM_GETTEXT, SIZEOF(sz), BYVAL VARPTR(sz))
                        getprop getparent(hCtl), "LVWND" TO hLVWnd
                        enablewindow hLVWnd, %TRUE
                        l = ListView_GetExtendedListViewStyle(hCtl)
                        ListView_SetExtendedListViewStyle (hCtl, l OR %LVS_EX_GRIDLINES OR %LVS_EX_FULLROWSELECT)
                        tLVI.mask     = %LVIF_TEXT
                        tLVI.iitem    = lvrow
                        tLVI.isubitem = lvcol
                        tLVI.pszText  = VARPTR(sz)
                        l = ListView_setItem(hLVWnd, BYVAL VARPTR(tLVI))
                        destroywindow hctl
                END SELECT
            '
            CASE %WM_DESTROY
                getprop getparent(hCtl), "LVWND" TO hLVWnd
                enablewindow hLVWnd, %TRUE
                SetWindowLong hctl, %GWL_WNDPROC, dwOrigEditProc
    
        END SELECT
        FUNCTION=CallWindowProc(dwOrigEditProc ,hCtl,Msg,wParam,lParam)
    END FUNCTION
    '------------------------------------------------------------------------------
    FUNCTION LVEditCell(hDlg AS LONG, hLV AS LONG, lvitm AS LONG, lvsubitem AS LONG) AS LONG
    
        LOCAL i AS LONG, lresult, NumColumns AS LONG, Offset AS LONG
        LOCAL r, rLV                    AS rect
        LOCAL dwStyle                   AS LONG
        STATIC dwOrigEditProc           AS DWORD
        LOCAL sz                        AS ASCIZ * 256
        LOCAL cx, cy, x, y, w, h AS LONG
        LOCAL tLVI                      AS LV_ITEM
        LOCAL hlistedit                 AS DWORD
        dwStyle = dwStyle OR %WS_BORDER OR %WS_CHILD OR %WS_VISIBLE OR %ES_AUTOHSCROLL
    
        tLVI.mask     = %LVIF_TEXT
        tLVI.iitem    = lvitm
        tLVI.isubitem = lvsubitem
        tLVI.pszText  = VARPTR(sz)
        tLVI.cchTextMax = SIZEOF(sz)-1
        lresult = listview_getitem ( hLV, BYVAL VARPTR(tLVI))
        CALL ListView_GetSubItemRect (hLV, lvitm, lvsubitem, %LVIR_LABEL, BYVAL VARPTR(r))
        DIALOG PIXELS hdlg,r.nleft, r.ntop +1 TO UNITS x, y
        DIALOG PIXELS hdlg, r.nright - r.nleft, r.nbottom -r.ntop TO UNITS w, h
        CONTROL GET LOC hdlg, getdlgCtrlId(hLV) TO cx, cy
        enablewindow ( hLV, %false) 'disable the underlying LV window
        CONTROL ADD "EDIT", hDlg, %IDC_EDITLVSUBITEM, TRIM$(sz,ANY $SPC + $NUL), _
                              cx + x + 1, cy + y, w, h, _
                              dwstyle
        CONTROL SET COLOR hdlg, %IDC_EDITLVSUBITEM, %WHITE, %RED
        hListEdit = getdlgItem(hdlg,%IDC_EDITLVSUBITEM)
        dwOrigEditProc = SetWindowLong(hListEdit,%GWL_WNDPROC,CODEPTR(LV_EditProc ))
        ' call the subclassed control directly to tell it what its original WndProc is
        ' and what the item & subitem are ... saves using a GLOBAL value!
        setprop hDlg, "LVWND", hLV
        LV_EditProc ( 0, 0, dwOrigEditProc, MAK (DWORD, lvsubitem, lvitm))
        CONTROL SET FOCUS hDlg, %IDC_EDITLVSUBITEM
        CALL SendMessage(hListEdit, %EM_SETSEL, 0 ,-1)
        FUNCTION = hListEdit
    END FUNCTION
    '----------------------------------------------------------------------
    CALLBACK FUNCTION ShowDIALOG1Proc()
        LOCAL lnotifyctl        AS LONG
        LOCAL hLV               AS LONG
        LOCAL tLVI              AS LV_ITEM
        LOCAL pNMLV             AS NM_LISTVIEW PTR
        LOCAL sztext            AS ASCIZ * 128
        LOCAL l, lrow, lcol     AS LONG
        STATIC hEdit            AS DWORD
    
        SELECT CASE AS LONG CBMSG
            CASE %WM_INITDIALOG
                
            CASE %WM_LBUTTONDOWN
                ' if there is an edit session in progress, kill it off
                IF iswindow(hEdit) THEN destroywindow hEdit
    
            CASE %WM_NOTIFY
                lNotifyCtl = LOWRD(CBWPARAM )
                SELECT CASE lNotifyCtl
                    CASE %IDC_LV
                        pNMLV = CBLPARAM
                        hLV = @pNMLV.hdr.hwndfrom
                        SELECT CASE @pNMLV.hdr.Code
                            CASE %NM_DBLCLK
                                IF @pNMLV.iitem = -1 THEN 'invalid row, presume we are adding a row.
                                    tLVI.stateMask = %LVIS_FOCUSED
                                    sztext = "new item"
                                    tLVI.pszText   = VARPTR(szText)
                                    tLVI.iItem     = Listview_getItemCount(hLV) ' 1st row is zero, this is a new row
                                    tLVI.iSubItem  = 0
                                    tLVI.mask = %LVIF_TEXT OR %LVIF_PARAM OR %LVIF_STATE
                                    listview_InsertItem ( hLV, BYVAL VARPTR(tLVI))
                                    hedit = LVeditCell ( CBHNDL, hLV, tLVI.iitem, 0)
                                    EXIT SELECT
                                END IF
                                hedit = LVeditCell ( CBHNDL, hLV, @pNMLV.iitem, @pNMLV.isubitem)
                        END SELECT
                END SELECT
            CASE %WM_DESTROY
                removeprop CBHNDL, "LVWND"
        END SELECT
    END FUNCTION
    '------------------------------------------------------------------------------
    FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
        LOCAL lRslt AS LONG
        LOCAL hDlg  AS DWORD
    
        DIALOG NEW hParent, "Editable Listview   Chris Holbrook Sep 2008", 136, 153, 301, 131, _
            %WS_POPUP  OR %WS_CHILD OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_NOFAILCREATE OR %WS_CAPTION OR _
            %DS_SETFONT OR %WS_SYSMENU, _
            %WS_EX_TRANSPARENT OR %WS_EX_CONTROLPARENT OR %WS_EX_TOPMOST OR %WS_EX_LEFT OR %WS_EX_LTRREADING _
            TO hDlg
            
        CONTROL ADD LABEL, hdlg, %IDC_LABEL1, _
        "Click to navigate, double click to edit, " + $CRLF + "<CR> to accept," + $CRLF + "<ESC> or click elsewhere to cancel", _
                          10, 100, 280, 30
                          
        CONTROL ADD "SysListView32", hDlg, %IDC_LV, "", 10, 10, 280, 90, _
             %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %LVS_REPORT OR %LVS_SHOWSELALWAYS, _
             %WS_EX_LEFT OR %WS_EX_CLIENTEDGE OR %WS_EX_RIGHTSCROLLBAR
    
        SampleListView hDlg, %IDC_LV, 4, 8
    
        DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt
    
        FUNCTION = lRslt
    END FUNCTION
    
    '==========================================================================
    FUNCTION PBMAIN()
        InitCommonControls()
    
        ShowDIALOG1 %HWND_DESKTOP
    END FUNCTION

  • #2
    How about the %IDs? or #INCLUDES?

    If we saw what it was ported to, our guesses might be more than guesses.
    Rod
    I want not 'not', not Knot, not Knott, not Nott, not knot, not naught, not nought, but aught.

    Comment


    • #3
      Thanks Rodney, that put me on the right road.

      Comment


      • #4
        LOWRD(wParam) is not necessarily the control ID on the WM_NOTIFY notification message.

        You need to use the ID in the NMHDR structure
        Incorrect:
        Code:
                   lNotifyCtl = LOWRD(CBWPARAM )
                    SELECT CASE lNotifyCtl
                        CASE %IDC_LV
                            pNMLV = CBLPARAM
                            hLV = @pNMLV.hdr.hwndfrom
                            SELECT CASE @pNMLV.hdr.Code
        Correct:
        Code:
              CASE %WM_NOTIFY
                   pNMLV = lparam 
                   lNotifyCtl = @pNMLV.hdr.idfrom 
                   SELECT CASE lNotifyCtl
                        CASE %IDC_LV
                          .....
        I believe I saw something in the "what's new" for 9x to return the CORRECT control ID on WM_NOTIFY, but this will always work regardless of version.

        MCM
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          Source: SDK:
          Code:
          lResult = SendMessage(      // returns LRESULT in lResult  
              (HWND) hWndControl,      // handle to destination control ,
              (UINT) WM_NOTIFY    // message ID 
              (WPARAM) wParam,      // = (WPARAM) (int) idCtrl;   
              (LPARAM) lParam      //      (LPARAM) (LPNMHDR) pnmh; );
          Parameters
          idCtrl
          Identifier of the common control sending the message. This identifier is not guaranteed to be unique. An application should use the hwndFrom or idFrom member of the NMHDR structure (passed as the lParam parameter) to identify the control. [Italics mine]
          pnmh
          Pointer to an NMHDR structure that contains the notification code and additional information. For some notification messages, this parameter points to a larger structure that has the NMHDR structure as its first member
          Just noticed for the first time: Very strange, the code shown in the documentation is for SENDing this message, even though the text explanation of the params is worded for RECEIVING it.

          Except... at the top of the page...
          The WM_NOTIFY message is sent by a common control to its parent window when an event has occurred or the control requires some information.
          Looks like the code example in my disk-based SDK (Dec 2002) is wrong. Wonder what the MS online doc shows.

          MCM
          Last edited by Michael Mattias; 12 Sep 2008, 08:35 AM.
          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            Originally posted by Michael Mattias View Post
            LOWRD(wParam) is not necessarily the control ID on the WM_NOTIFY notification message.
            well, I'll let you fight that one out with M$, the W32 Programmer's Reference states:

            The WM_NOTIFY message informs the parent window of a control that an event has occurred in the control or that the control requires some kind of information.

            WM_NOTIFY
            idCtrl = (int) wParam;
            pnmh = (LPNMHDR) lParam;
            I don't think this is the problem anyway.

            MCM Just saw your second post. Actually I've tried both variants and it makes no difference.
            Last edited by Chris Holbrook; 12 Sep 2008, 10:36 AM. Reason: just noticed MCMs second post

            Comment


            • #7
              got it!

              The NM_CLICK and NM_DBLCLK notifications were not getting through because they were being eaten by the (modeless) dialog's parent under WM_MOUSEACTIVATE. In the minature application which I posted, the dialog has no parent.

              Three years on I still find Windows hard going!

              Comment

              Working...
              X