PlanetSquires Forums

Support Forums => Other Software and Code => Topic started by: Dan English on November 15, 2012, 05:22:13 PM

Title: XPButton issue
Post by: Dan English on November 15, 2012, 05:22:13 PM
I'm seeing unexpected behaviour involving an XPButton control.

Picture this -- I have a parent window with an XPButton that triggers a child window.  This child window is modally opened over top of the parent.  The child window contains a big listview control.  I have the listview's double-click event coded to close the child window (and return the clicked row's lParam).

So when I double-click the child window's listview control, the child gets closed as expected.  If it so happens that the mouse cursor's x/y co-ordinate is in the same x/y co-ordinate of the parent's XPButton dimensions, the child window appears again.  Basically, the parent XPButton's BN_CLICKED message is triggered again, thus opening up the child.

This doesn't happen if I double-click the child listview over top of a normal command button on the parent.

Has anyone seem something like this, and know how I can stop the behaviour?
Title: Re: XPButton issue
Post by: Paul Squires on November 16, 2012, 08:39:49 AM
Seems pretty odd. Maybe you can disable the button just prior to displaying your popup Form and the re-enable it once that Form closes. The XP Button is Jose's. Not sure if he is investigating the issue.
Title: Re: XPButton issue
Post by: José Roca on November 16, 2012, 11:43:55 AM
Maybe the parent window is being re-enabled too early, or something else. (code not shown). If it fires it is because the program is sending a BN_CLICKED message to it. The part of the program that sends that message is the one to inspect.

This test works fine:


#COMPILE EXE
#DIM ALL
%UNICODE = 1

' // Include files for external files
%USEXPBUTTON = 1
#INCLUDE ONCE "CWindow.inc"        ' // CWindow class
#INCLUDE ONCE "ListViewCtrl.inc"   ' // ListView control wrapper functions

%IDC_POPUP = 1001
%IDC_LISTVIEW = 1002

' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpszCmdLine AS WSTRINGZ PTR, BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   IF AfxGetWindowsVersion => 6 THEN SetProcessDPIAware

   ' // Create an instance of the class
   LOCAL pWindow AS IWindow
   pWindow = CLASS "CWindow"
   IF ISNOTHING(pWindow) THEN EXIT FUNCTION

   ' // Create the main window
   ' // Note: CW_USEDEFAULT is used as the default value When passing 0's as the width and height
   pWindow.CreateWindow(%NULL, "CWindow with an XPButton", 0, 0, 500, 350, 0, 0, CODEPTR(WindowProc))

   ' // Center the window
   pWindow.CenterWindow

   ' // Add buttons
   pWindow.AddXPButton(pWindow.hwnd, %IDC_POPUP, "&Popup", 250, 250, 75, 23)
   pWindow.AddXPButton(pWindow.hwnd, %IDCANCEL, "&Close", 350, 250, 75, 23)

   ' // Default message pump (you can replace it with your own)
   pWindow.DoEvents(nCmdShow)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Main callback function.
' ========================================================================================
FUNCTION WindowProc (BYVAL hwnd AS DWORD, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG

   STATIC hInstance AS DWORD        ' // Instance handle
   STATIC lpc AS CREATESTRUCT PTR   ' // Pointer to the creation parameters
   STATIC pWindow AS IWindow        ' // Reference to the IWindow interface

   SELECT CASE uMsg

      CASE %WM_CREATE
         ' // Pointer to the creation parameters
         lpc = lParam
         ' // Instance handle
         hInstance = @lpc.hInstance
         ' // Get a reference to the IWindow interface from the CREATESTRUCT structure
         pWindow = CWindow_GetObjectFromCreateStruct(lParam)
         EXIT FUNCTION

      CASE %WM_COMMAND
         SELECT CASE LO(WORD, wParam)
            CASE %IDCANCEL
               ' // If the Escape key has been pressed...
               IF HI(WORD, wParam) = %BN_CLICKED THEN
                  ' // ... close the application by sending a WM_CLOSE message
                  SendMessage hwnd, %WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF
            CASE %IDC_POPUP
               PopupDialog(pWindow)
               EXIT FUNCTION
         END SELECT

      CASE %WM_DESTROY
         ' // End the application
         PostQuitMessage 0
         EXIT FUNCTION

   END SELECT

   ' // Pass unprocessed messages to Windows
   FUNCTION = DefWindowProc(hwnd, uMsg, wParam, lParam)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Popup dialog
' ========================================================================================
SUB PopupDialog (BYVAL pParentWindow AS IWindow)

   ' // Create an instance of the class
   LOCAL pPopup AS IWindow
   pPopup = CLASS "CWindow"
   IF ISNOTHING(pPopup) THEN EXIT SUB

   ' // Create the main window
   LOCAL hwnd AS DWORD
   hwnd = pPopup.CreateWindow(pParentWindow.hwnd, "Popup dialog", 0, 0, 500, 350, _
          %WS_VISIBLE OR %WS_CAPTION OR %WS_POPUPWINDOW, %WS_EX_WINDOWEDGE, _
          CODEPTR(PopupWindowProc))

   ' // Center the window
   pPopup.CenterWindow

   ' // Add a ListView control
   LOCAL hListView AS DWORD
   hListView = pPopup.AddListView(hwnd, %IDC_LISTVIEW, "", 5, 5, pParentWindow.ClientWidth - 10, pParentWindow.ClientHeight - 10)

   ' // Add some extended styles
   LOCAL dwExStyle AS DWORD
   dwExStyle = ListView_GetExtendedListViewStyle(hListView)
   dwExStyle = dwExStyle OR %LVS_EX_FULLROWSELECT OR %LVS_EX_GRIDLINES
   ListView_SetExtendedListViewStyle(hListView, dwExStyle)

   ' // Add the header's column names
   LOCAL i AS LONG
   FOR i = 0 TO 4
      ListView_AddColumn(hListView, i, "Column" & STR$(i), 110)
   NEXT

   ' // Populate the ListView with some data
   FOR i = 0 to 29
      ListView_AddItem(hListView, i, 0, "Column 0 Row" + STR$(i))
      ListView_SetItemText(hListView, i, 1, "Column 1 Row" + STR$(i))
      ListView_SetItemText(hListView, i, 2, "Column 2 Row" + STR$(i))
      ListView_SetItemText(hListView, i, 3, "Column 3 Row" + STR$(i))
      ListView_SetItemText(hListView, i, 4, "Column 4 Row" + STR$(i))
   NEXT

   ' // Select the fist item
   ListView_SelectItem hListView, 0
   ' // Set the focus in the ListView
   SetFocus hListView

   ' // Default message pump
   pPopup.DoEvents

END SUB
' ========================================================================================

' ========================================================================================
' Editor options callback function.
' ========================================================================================
FUNCTION PopupWindowProc (BYVAL hwnd AS DWORD, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG

   STATIC pWindow AS IWindow           ' // Reference to the IWindow interface
   LOCAL  pnmhdr AS NMHDR PTR

   SELECT CASE uMsg

      CASE %WM_CREATE
         ' // Get a reference to the IWindow interface from the CREATESTRUCT structure
         pWindow = CWindow_GetObjectFromCreateStruct(lParam)
         ' // Disable the owner of the modal window
         EnableWindow GetWindow(hwnd, %GW_OWNER), %FALSE

      CASE %WM_COMMAND

         SELECT CASE LO(WORD, wParam)

            CASE %IDCANCEL
               IF HI(WORD, wParam) = %BN_CLICKED THEN
                  SendMessage hwnd, %WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF

         END SELECT

      CASE %WM_SIZE
         ' // Resize the ListView control and its header
         IF wParam <> %SIZE_MINIMIZED THEN
            LOCAL hListView AS DWORD
            hListView = GetDlgItem(hwnd, %IDC_LISTVIEW)
            pWindow.MoveWindow hListView, 5, 5, pWindow.ClientWidth - 10, pWindow.ClientHeight - 10, %TRUE
         END IF

      CASE %WM_NOTIFY
         pnmhdr = lParam
         SELECT CASE @pnmhdr.idFrom
            CASE %IDC_LISTVIEW
               SELECT CASE @pnmhdr.code
                  CASE %NM_DBLCLK
                     SendMessage hwnd, %WM_CLOSE, 0, 0
                     EXIT FUNCTION
               END SELECT
         END SELECT

      CASE %WM_CLOSE
         ' // The owner window is enabled in WM_CLOSE rather than WM_DESTROY to
         ' // prevent the application from losing the focus. In WM_DESTROY the
         ' // modal window has already been removed from the screen by the system.
         ' // Because the remaining windows are disabled, the system gives the
         ' // focus to another application.
         EnableWindow GetWindow(hwnd, %GW_OWNER), %TRUE

      CASE %WM_DESTROY
         ' // Close the main window
         PostQuitMessage 0
         EXIT FUNCTION

   END SELECT

   FUNCTION = DefWindowProc(hwnd, uMsg, wParam, lParam)

END FUNCTION
' ========================================================================================

Title: Re: XPButton issue
Post by: Dan English on November 16, 2012, 01:01:47 PM
Hmm, in the child window (where the double-click event is processed), I don't believe I'm doing anything crazy.  Here's my code that sets a textbox, then closes the form:


'--------------------------------------------------------------------------------
Function FORM1_CUSTOM ( _
                              hWndForm      As Dword, _  ' handle of Form
                              wMsg          As Long,  _  ' type of message
                              wParam        As Dword, _  ' first message parameter
                              lParam        As Long   _  ' second message parameter
                              ) As Long

    Local pNMLV As NM_LISTVIEW Ptr

    Select Case wMsg
        Case %WM_NOTIFY
            pNMLV = lParam       
            Select Case @pNMLV.hdr.idfrom       
                Case IDC_FORM1_LISTVIEW1        ' notify message from the listview control
                    Select Case @pNMLV.hdr.Code
                        Case %NM_DBLCLK   'left double-click
                            ' Send selected value to target control, and close selector
                            FF_TextBox_SetText(gTargetHwnd, Trim$(FF_ListView_GetItemText(HWND_FORM1_LISTVIEW1, @pNMLV.iItem, 0)))
                            FF_CloseForm hWndForm                           
                    End Select
            End Select                     
    End Select

End Function


Is there maybe a better way to deal with double-clicking a listview?
Title: Re: XPButton issue
Post by: José Roca on November 16, 2012, 01:49:16 PM
Are you using the latest version of the headers?
Title: Re: XPButton issue
Post by: Dan English on November 16, 2012, 02:10:27 PM
I'm fairly certain I've got the latest headers... all the .inc files are dated August 3, 2012 12:00 AM.
Title: Re: XPButton issue
Post by: Paul Squires on November 16, 2012, 03:02:29 PM
I wonder if it is related to closing the Form while you are still inside the Listview double click notification.....

Maybe under %NM_DBLCLK try a PostMessage to the Form using a user defined message (catch it via the CUSTOM) handler. Try setting your textbox value and closing the Form from that handler.

Something along these lines:


%MSG_USER_CLOSEFORM = %WM_USER + 100

      Case IDC_FORM1_LISTVIEW1        ' notify message from the listview control                   
         Select Case @pNMLV.hdr.Code                       
            Case %NM_DBLCLK   'left double-click                           
                  FF_TextBox_SetText(gTargetHwnd, Trim$(FF_ListView_GetItemText(HWND_FORM1_LISTVIEW1, @pNMLV.iItem, 0)))
                 PostMessage hWndForm, %MSG_USER_CLOSEFORM, 0, 0
         End Select

Case %MSG_USER_CLOSEFORM
              FF_CloseForm hWndForm                                               
End Select

Title: Re: XPButton issue
Post by: Dan English on November 16, 2012, 03:18:26 PM
Thanks Paul, but that doesn't seem to eliminate the issue.

I was able to implement your idea, and confirmed the necessary code path was followed, but the child form still pops up.