Question about subclassing a listview

Started by Wilko Verweij, April 13, 2013, 11:45:45 AM

Previous topic - Next topic

Wilko Verweij

Hi, I am trying to subclass a listview (my first subclassing experience) and I think I am missing something.
If I use the code as shown here:
Global OrigProc As Long
'--------------------------------------------------------------------------------
Function FORM1_WM_CREATE ( _
                         hWndForm As Dword, _      ' handle of Form
                         ByVal UserData As Long _  ' optional user defined Long value
                         ) As Long

  Local I As Word
  Local J As Word

  For I = 1 To 3
    FF_ListView_InsertColumn(HWND_FORM1_LISTVIEW1, I - 1, "Header Col" & Str$(I), %LVCFMT_CENTER,100 )
  Next I

  For I = 1 To 15
    FF_ListView_InsertItem (HWND_FORM1_LISTVIEW1, I - 1, 0, "row " & Str$(I) & "; col 1")
    For J = 2 To 3
      FF_ListView_InsertItem (HWND_FORM1_LISTVIEW1, I - 1, J - 1, "row " & Str$(I) & "; col" & Str$(J))
    Next J
  Next I

  Local NewProc&
  NewProc& = CodePtr(NewListViewProc)                                             
  OrigProc& = SetWindowLong(HWND_FORM1_LISTVIEW1, %GWL_WNDPROC, NewProc&) 

End Function


then the messages for my listview go to my procedure 'NewListViewProc' as I want, but the listview does not react on the vertical scrollbar any more, even if the only line in that procedure is CallWindowProc(OrigProc&, hWnd, wMsg, wParam, lParam)

If I move the three subclassing lines
  Local NewProc&
  NewProc& = CodePtr(NewListViewProc)                                             
  OrigProc& = SetWindowLong(HWND_FORM1_LISTVIEW1, %GWL_WNDPROC, NewProc&) 


to the create function of the listview control rather than in the create function of the form, the scrollbar works but I never reach my new function.
I guess I am missing something simple so I hope you guys are willing to help.

Wilko

Paul Squires

Hi Wilko,

In FireFly, you should not have to manually subclass a Listview. FireFly automatically subclasses the Listview for you when the control is created. You can handle all of your messages in the Listview's "CUSTOM" event handler. The exception to this would be if you need to manipulate the Listview's header control - that control is not automatically subclassed.
Paul Squires
PlanetSquires Software

Wilko Verweij

Hi Paul,
I would like to create a listview with alternating row colours. As far as I understood from the internet, I need to use subclassing for that. So any  help is appreciated.
Wilko

José Roca

You don't need to subclass it, but to make it ownerdraw and do the painting of the rows in the notification message.

Wilko Verweij

Thanks Jose, but doesn't that mean that you have to do all drawing yourself? I found this on the internet:

http://www.codeproject.com/Articles/20796/ListView-Alternating-Row-Colours-Using-Windows-API

and that approach allows automatic drawing and manual application of color. I was trying to use that approach.
Anyway, will try your suggestion too.
Thanks,
Wilko

José Roca

Here is a way to do it:


' ########################################################################################
' Microsoft Windows
' File: CW_LV_CustomDraw_HDPI.pbtpl
' Contents: Template - CWindow with a custom draw ListView (High DPI)
' Compilers: PBWIN 10.02+, PBCC 6.02+
' Headers: Windows API headers 2.04+
' Copyright (c) 2011 Jose 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.
' ########################################################################################

#COMPILE EXE
#DIM ALL

%UNICODE = 1
#INCLUDE ONCE "CWindow.inc"        ' // CWindow class
#INCLUDE ONCE "ListViewCtrl.inc"   ' // ListView control wrapper functions

%IDC_LISTVIEW = 1001

' ========================================================================================
' 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
   SetProcessDPIAware

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

   ' // Create the main window
   LOCAL hwnd AS DWORD
   hwnd = pWindow.CreateWindow(%NULL, "Custom Draw ListView (High DPI)", 0, 0, 0, 0, 0, 0, CODEPTR(WindowProc))
   ' // Change the class style to avoid flicker
   pWindow.ClassStyle = %CS_DBLCLKS
   ' // Set the client size
   pWindow.SetClientSize 565, 320
   ' // Center the window
   pWindow.CenterWindow

   ' // Add a ListView control
   LOCAL hListView AS DWORD
   hListView = pWindow.AddListView(hwnd, %IDC_LISTVIEW, "", 0, 0, 0, 0)

   ' // 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), pWindow.ScaleX(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 (you can replace it with your own)
   pWindow.DoEvents

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 pWindow AS IWindow           ' // Reference to the IWindow interface

   LOCAL pNmh  AS NMHDR PTR            ' // Pointer to a NMHDR structure
   LOCAL pLvNm AS NMLISTVIEW PTR       ' // Pointer to a NMLISTVIEW structure
   LOCAL pLvCd AS NMLVCUSTOMDRAW PTR   ' // Pointer to a NMLVCUSTOMDRAW structure

   SELECT CASE uMsg

      CASE %WM_CREATE
         ' // 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)
            ' // End the application by sending a %WM_CLOSE message
            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
         ' // Processs notify messages sent by the list view control
         pNmh = lParam
         SELECT CASE @pNmh.idFrom
            CASE %IDC_LISTVIEW
               pLvNm = lParam
               SELECT CASE @pLvNm.hdr.code
                  CASE %NM_CUSTOMDRAW
                     pLvCd = lParam
                     SELECT CASE @pLvCd.nmcd.dwDrawStage
                        CASE %CDDS_PREPAINT, %CDDS_ITEMPREPAINT
                           ' // Tell the list view to send the %CDDS_ITEMPREPAINT OR %CDDS_SUBITEM notification message
                           FUNCTION = %CDRF_NOTIFYSUBITEMDRAW
                           EXIT FUNCTION
                        CASE %CDDS_ITEMPREPAINT OR %CDDS_SUBITEM
                           IF @pLvCd.iSubItem = 0 THEN
                              ' // Paint the first column with a gray background
                              @pLvCd.clrTextBk = %LTGRAY
                              @pLvCd.clrText = %BLACK
                           ELSE
                              IF (@pLvCd.nmcd.dwItemSpec MOD 2) = 0 THEN
                                 ' // Paint the columns of odd rows with a white background
                                 @pLvCd.clrTextBk = %WHITE
                                 @pLvCd.clrText = %BLACK
                              ELSE
                                 ' // Paint the columns of even rows with a pale turquoise background
                                 @pLvCd.clrTextBk = %RGB_PaleTurquoise
                                 @pLvCd.clrText = %BLACK
                              END IF
                           END IF
                           ' // Tell the list view to draw itself
                           FUNCTION = %CDRF_DODEFAULT
                           EXIT FUNCTION
                     END SELECT
               END SELECT
          END SELECT

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

   END SELECT

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

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


Wilko Verweij


Robert Eaton

Great example Jose!
Thanks for sharing it