Here is one very simple way of showing and responding to a popup menu when you right click on a ListView row and column. It simply takes the text from the selected row/col and copies it to the clipboard. Also notice the use of the TPM_RETURNCMD flag in the TrackPopupMenu call which allows you deal immediately with the returned selected value from the popup menu.
#Define IDM_COPYLISTVIEWTEXT WM_USER + 1
''
''
Function frmMain_ListView1_RightClick( ByRef sender As wfxListView, ByRef e As EventArgs ) As LRESULT
' Only disply the popup menu if a valid row has been selected
if e.ListViewRow = -1 then exit function
' Create the menu (only one item for this example)
Dim hPopUpMenu As HMENU = CreatePopupMenu()
AppendMenu( hPopUpMenu, MF_ENABLED or MF_STRING, IDM_COPYLISTVIEWTEXT, _
"Copy cell data to clipboard" )
' Display the popup menu and receive the selected item. We use the TPM_RETURNCMD flag
' to retrieve the result here rather than have to get it from the Form handler.
dim as long nSelected = _
TrackPopupMenu( hPopUpMenu, TPM_RETURNCMD or TPM_NONOTIFY, _
e.x, e.y, 0, frmMain.hWindow, ByVal Null )
select case nSelected
case IDM_COPYLISTVIEWTEXT
' Get the text from the row/col and copy it to the clipboard
dim as CWSTR wszText = frmMain.ListView1.Item(e.ListViewRow).SubItem(e.ListViewColumn).Text
AfxSetClipboardText( wszText )
end select
' Finally, destroy the popup menu
DestroyMenu hPopUpMenu
Function = 0
End Function
Hi Paul,
You may use the TrackPopupMenuEx's protected rectangle feature.
Try to move the window so one cell is pretty near the bottom of the screen, the menu will flip over the cell and not above it.
This might be inconvenient because the user won't see the cell text anymore.
Here is an example of what I mean, using José's listview demo code, to exclude the cell from being hidden.
All is in WM_NOTIFY...
#Define JumpCompiler "<D:\Free\64\fbc.exe>" 'Compiler to use "< = left delimiter, >" = right delimiter
#Define JumpCompilerCmd "<-s gui -w pedantic -i D:\Free\bi\Roca\WinFBx\ "D:\Free\bas\~~Default.rc">" 'Command line to send to compiler (gui or console) '•Line 6
' ########################################################################################
' Microsoft Windows
' File: CW_COMMCTRL_ListView.fbtpl
' Contents: CWindow with a ListView
' Compiler: Free Basic
' Copyright (c) 2016 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
#Define UNICODE
#INCLUDE ONCE "Afx/CWindow.inc"
USING Afx
#Define IDC_LISTVIEW 1001
#Define IDM_rcProtected WM_USER + 1
#Define IDM_COPYLISTVIEWTEXT WM_USER + 2
DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)
' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
' // Set process DPI aware
AfxSetProcessDPIAware
' // Create the main window
DIM pWindow AS CWindow
pWindow.Create(NULL, "Right click in ListView", @WndProc)
pWindow.ClassStyle = CS_DBLCLKS ' // Change the window style to avoid flicker
pWindow.SetClientSize(565, 320)
pWindow.Center
' // Adds a listview
DIM hListView AS HWND
hListView = pWindow.AddControl("ListView", , IDC_LISTVIEW)
' // Add some extended styles
DIM 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
DIM lvc AS LVCOLUMNW, wszText AS WSTRING * 260
lvc.mask = LVCF_FMT OR LVCF_WIDTH OR LVCF_TEXT OR LVCF_SUBITEM
FOR i AS LONG = 0 TO 4
wszText = "Column " & STR(i)
lvc.pszText = @wszText
lvc.cx = pWindow.ScaleX(110)
lvc.iSubItem = i
ListView_InsertColumn(hListView, i, @lvc)
NEXT
' // Populate the ListView with some data
DIM lvi AS LVITEMW
lvi.mask = LVIF_TEXT
FOR i AS LONG = 0 to 29
lvi.iItem = i
lvi.iSubItem = 0
wszText = "Column 0 Row" + WSTR(i)
lvi.pszText = @wszText
ListView_InsertItem(hListView, @lvi)
FOR x AS LONG = 1 TO 4
wszText = "Column " & WSTR(x) & " Row" + WSTR(i)
ListView_SetItemText(hListView, i, x, @wszText)
NEXT
NEXT
' // Select the fist item (ListView items are zero based)
ListView_SelectItem(hListView, 0)
' // Set the focus in the ListView
SetFocus hListView
' // Dispatch Windows messages
FUNCTION = pWindow.DoEvents(nCmdShow)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Main window callback procedure
' ================================================================e========================
FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
SELECT CASE uMsg
CASE WM_COMMAND
SELECT CASE GET_WM_COMMAND_ID(wParam, lParam)
' // If ESC key pressed, close the application sending an WM_CLOSE message
CASE IDCANCEL
IF GET_WM_COMMAND_CMD(wParam, lParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
END SELECT
CASE WM_NOTIFY
DIM pListviewNotify AS NMLISTVIEW PTR = CAST(NMLISTVIEW PTR, lParam) 'Set pointer to listview notification
IF pListviewNotify->hdr.hwndFrom = GetDlgItem(hWnd, IDC_LISTVIEW) THEN 'It's the right listview
IF pListviewNotify->hdr.code = NM_RCLICK THEN 'Right click was made
SetWindowText(hWnd, "Item = " + STR$(pListviewNotify->iItem) + ", subitem = " + STR$(pListviewNotify->iSubItem))
IF pListviewNotify->iItem <> -1 THEN 'Click was made on an item
DIM rcProtected AS TPMPARAMS 'The menu will never appear in this rectangle
rcProtected.rcExclude.Top = pListviewNotify->iSubItem 'Use rcProtected to get item rectangle
rcProtected.rcExclude.Left = LVIR_LABEL 'Returns the bounding rectangle of the item text
SendMessage(pListviewNotify->hdr.hwndFrom, LVM_GETSUBITEMRECT, _
CAST(wParam, pListviewNotify->iItem), _
CAST(lParam, @rcProtected.rcExclude)) 'Get Item rect
rcProtected.cbSize = SIZEOF(TPMPARAMS) 'Set version
MapWindowPoints(pListviewNotify->hdr.hwndFrom, HWND_DESKTOP, CAST(LPPOINT, @rcProtected.rcExclude), 2) 'Make rect relative to listView
DIM hPopUpMenu AS HMENU = CreatePopupMenu() 'Create menu
AppendMenu(hPopUpMenu, MF_ENABLED or MF_STRING, IDM_rcProtected, "Menu won't be over item ever")
AppendMenu(hPopUpMenu, MF_ENABLED or MF_STRING, IDM_COPYLISTVIEWTEXT, "Copy cell data to clipboard")
#IF 01
'The following will never set the menu over the cell
DIM AS LONG nSelected = TrackPopupMenuEx(hPopUpMenu, TPM_RETURNCMD OR TPM_LEFTALIGN OR TPM_RIGHTBUTTON OR TPM_VERTICAL, _
rcProtected.rcExclude.Left, rcProtected.rcExclude.Bottom, hWnd, @rcProtected)
#ELSE
'The following can set the menu over the cell
DIM AS LONG nSelected = TrackPopupMenuEx(hPopUpMenu, TPM_RETURNCMD OR TPM_LEFTALIGN OR TPM_RIGHTBUTTON OR TPM_VERTICAL, _
rcProtected.rcExclude.Left, rcProtected.rcExclude.Bottom, hWnd, 0)
#ENDIF
SELECT CASE nSelected
CASE IDM_rcProtected
MessageBox(hWnd, "Move the window so one cell is pretty near the bottom of the screen " + _
" the menu will flip above the cell and not over it", "TrackPopupMenuEx", MB_OK OR MB_TOPMOST)
CASE IDM_COPYLISTVIEWTEXT
'Get the text from the row/col and copy it to the clipboard
DIM AS CWSTR wszText
ListView_GetITemText(pListviewNotify->hdr.hwndFrom, pListviewNotify->iItem, pListviewNotify->iSubItem, wszText, SIZEOF(wszText))
AfxSetClipboardText(wszText)
END SELECT
END IF
END IF
END IF
CASE WM_SIZE
' // Resize the ListView control and its header
IF wParam <> SIZE_MINIMIZED THEN
' // Retrieve the handle of the ListView control
DIM hListView AS HWND = GetDlgItem(hwnd, IDC_LISTVIEW)
' // Retrieve a pointer to the CWindow class
DIM pWindow AS CWindow PTR = AfxCWindowPtr(hwnd)
' // Move the ListView control
IF pWindow THEN pWindow->MoveWindow hListView, 5, 5, pWindow->ClientWidth - 10, pWindow->ClientHeight - 10, CTRUE
END IF
CASE WM_DESTROY
' // End the application
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
' // Pass unprocessed messages to DefWindowProc
FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
'
Thanks Pierre, that's pretty cool! Very useful - thanks
Happy that you like this exclusion, I remember finding it rather obscure at first glance. :-)
And, of course, thank to José for the listview code example...