PlanetSquires Forums

Please login or register.

Login with username, password and session length
Advanced search  

Author Topic: EXAMPLE: Popup menu on ListView row/col  (Read 821 times)

Paul Squires

  • Administrator
  • Guru Member
  • *****
  • Posts: 8941
  • Windows 10
    • PlanetSquires Software
EXAMPLE: Popup menu on ListView row/col
« on: November 29, 2019, 08:11:26 AM »

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.

Code: [Select]
#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
Logged
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Pierre Bellisle

  • Junior Member
  • **
  • Posts: 90
Re: EXAMPLE: Popup menu on ListView row/col
« Reply #1 on: December 02, 2019, 04:32:12 AM »

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...

Code: [Select]
#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
' ========================================================================================
'
Logged

Paul Squires

  • Administrator
  • Guru Member
  • *****
  • Posts: 8941
  • Windows 10
    • PlanetSquires Software
Re: EXAMPLE: Popup menu on ListView row/col
« Reply #2 on: December 02, 2019, 08:24:39 AM »

Thanks Pierre, that's pretty cool! Very useful - thanks
Logged
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Pierre Bellisle

  • Junior Member
  • **
  • Posts: 90
Re: EXAMPLE: Popup menu on ListView row/col
« Reply #3 on: December 02, 2019, 10:25:37 AM »

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...
« Last Edit: December 02, 2019, 10:28:15 AM by Pierre Bellisle »
Logged