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?
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.
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
' ========================================================================================
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?
Are you using the latest version of the headers?
I'm fairly certain I've got the latest headers... all the .inc files are dated August 3, 2012 12:00 AM.
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
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.