• Welcome to PlanetSquires Forums.
 

CWindow: Custom Draw ListView example

Started by José Roca, August 31, 2015, 12:01:00 AM

Previous topic - Next topic

José Roca

Caveats: Instead of RGB we have to use BGR, with the colors reversed. Some messages, like NM_CUSTOMDRAW, are defined as negative numbers (I miss PB's sufixes such ???), so I have needed to use CASE CULNG(NM_CUSTOMDRAW) instead of CASE NM_CUSTOMDRAW (*).

Adding the columns and items of the listview without wrappers is a pain.


' ########################################################################################
' Microsoft Windows
' File: CW_LV_CustomDraw_HDPI.fbtpl
' Contents: Template - CWindow with a custom draw ListView (High DPI)
' Compiler: Free Basic
' Copyright (c) 2015 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.
' ########################################################################################

#define unicode
#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/AfxWin.inc"

#define IDC_LISTVIEW 1001

USING Afx.CWindowClass

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, COMMAND(), SW_NORMAL)

' ========================================================================================
' Window procedure
' ================================================================e========================
FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

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

   FUNCTION = 0

   SELECT CASE AS CONST uMsg

      CASE WM_CREATE
         EXIT FUNCTION

      CASE WM_COMMAND
         SELECT CASE LOWORD(wParam)
            ' // If the user press the ESC key...
            CASE IDCANCEL
               ' // ...send a message to close the application
               IF HIWORD(wParam) = BN_CLICKED THEN
                  SendMessageW 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
            DIM hListView AS HWND, pWindow AS CWindow PTR
            pWindow = CAST(CWindow PTR, GetPropW(hwnd, "CWINDOWPTR"))
            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 = CAST(NMHDR PTR, lParam)
         SELECT CASE pNmh->idFrom
            CASE IDC_LISTVIEW
               pLvNm = CAST(NMLISTVIEW PTR, lParam)
               SELECT CASE pLvNm->hdr.code
                  CASE CULNG(NM_CUSTOMDRAW)   ' CULNG needed because NM_CUSTOMDRAW is negative
                     pLvCd = CAST(NMLVCUSTOMDRAW PTR, 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 = BGR(&HD3, &HD3, &HD3)
                              pLvCd->clrText = BGR(&H00, &H00, &H00)
                           ELSE
                              IF (pLvCd->nmcd.dwItemSpec MOD 2) = 0 THEN
                                 ' // Paint the columns of odd rows with a white background
                                 pLvCd->clrTextBk = BGR(&HFF, &HFF, &HFF)
                                 pLvCd->clrText = BGR(&H00, &H00, &H00)
                              ELSE
                                 ' // Paint the columns of even rows with a pale turquoise background
                                 pLvCd->clrTextBk = BGR(&HAF, &HEE, &HEE)
                                 pLvCd->clrText = BGR(&H00, &H00, &H00)
                              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
          ' // End the application
         PostQuitMessage(0)
         EXIT FUNCTION

   END SELECT

   ' // Pass unprocessed messages to DefWindowProc
   FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)

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

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

   DIM pWindow AS CWindow
   pWindow.Create(NULL, "Custom Draw ListView (High DPI)", @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", pWindow.hWindow, 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 i AS LONG, lvc AS LVCOLUMNW, wszText AS WSTRING * 260
   lvc.mask = LVCF_FMT OR LVCF_WIDTH OR LVCF_TEXT OR LVCF_SUBITEM
   FOR i = 0 TO 4
      wszText = "Column " & STR(i)
      lvc.pszText = @wszText
      lvc.cx = pWindow.ScaleX(110)
      lvc.iSubItem = i
      SendMessageW(hListView, LVM_INSERTCOLUMNW, i, CAST(LPARAM, @lvc))
   NEXT

   ' // Populate the ListView with some data
   DIM x AS LONG
   DIM lvi AS LVITEMW
   lvi.mask = LVIF_TEXT
   FOR i = 0 to 29
      lvi.iItem = i
      lvi.iSubItem = 0
      wszText = "Column 0 Row" + STR(i)
      lvi.pszText = @wszText
      ListView_InsertItem(hListView, @lvi)
      FOR x = 1 TO 4
         lvi.iSubItem = x
         wszText = "Column " & STR(x) & " Row" + STR(i)
         lvi.pszText = @wszText
         SendMessageW hListView, LVM_SETITEMTEXTW, i, CAST(LPARAM, @lvi)
      NEXT
   NEXT

   ' // Select the fist item
   ListView_SetItemState(hListView, 0, LVIS_FOCUSED OR LVIS_SELECTED, &H000F)

   ' // Set the focus in the ListView
   SetFocus hListView

   FUNCTION = pWindow.DoEvents(nCmdShow)

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


(*) There are several negative notification messages that need to be converted to ULONG (DWORD).


#define NM_OUTOFMEMORY (NM_FIRST - 1)
#define NM_CLICK (NM_FIRST - 2)
#define NM_DBLCLK (NM_FIRST - 3)
#define NM_RETURN (NM_FIRST - 4)
#define NM_RCLICK (NM_FIRST - 5)
#define NM_RDBLCLK (NM_FIRST - 6)
#define NM_SETFOCUS (NM_FIRST - 7)
#define NM_KILLFOCUS (NM_FIRST - 8)
#define NM_CUSTOMDRAW (NM_FIRST - 12)
#define NM_HOVER (NM_FIRST - 13)
#define NM_NCHITTEST (NM_FIRST - 14)
#define NM_KEYDOWN (NM_FIRST - 15)
#define NM_RELEASEDCAPTURE (NM_FIRST - 16)
#define NM_SETCURSOR (NM_FIRST - 17)
#define NM_CHAR (NM_FIRST - 18)
#define NM_TOOLTIPSCREATED (NM_FIRST - 19)
#define NM_LDOWN (NM_FIRST - 20)
#define NM_RDOWN (NM_FIRST - 21)
#define NM_THEMECHANGED (NM_FIRST - 22)

#if _WIN32_WINNT = &h0602
#define NM_FONTCHANGED (NM_FIRST - 23)
#define NM_CUSTOMTEXT (NM_FIRST - 24)
#define NM_TVSTATEIMAGECHANGING (NM_FIRST - 24)
#endif

Paul Squires

I am on my way out for a few hours but your post triggered a memory I had of a post I made on the FB forums a few months. I wonder if your negative equates issue is the same as this one: http://www.freebasic.net/forum/viewtopic.php?f=6&t=23212#p204479  I will lookk at it more when I get home from.... Universal Studios Florida theme park!!! :) :) :)
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

Yes, the same issue. The negative numbers need to be converted to ULONG.

Paul Squires

Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer