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
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.
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
You don't need to subclass it, but to make it ownerdraw and do the painting of the rows in the notification message.
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 (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
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
' ========================================================================================
Thanks so much!
Wilko
Great example Jose!
Thanks for sharing it