Jump to content
Sign in to follow this  

Listview NM_CUSTOMDRAW and resizing - slow!

Recommended Posts


Basically I have a gui with a listview where items are highlighted in different colours (using NM_CUSTOMDRAW) and the listview columns are auto-sized when the gui is resized (using WM_SIZE).

The problem I have is that every time i resize the gui its super slow because the listview items are re-drawn using NM_CUSTOMDRAW every time the gui and subsequently the listview columns are resized.

Is there any way to turn of NM_CUSTOMDRAW after the initial draw? or is each cell required to be re-drawn when resizing columns/listview?

Here is a reproducer, click the Populate button to fill the listview. Notice how it doesnt colour in the whole row correctly until you resize and when you resize its very choppy/slow/flickery.

#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <Misc.au3>
#Include <Array.au3>
#Include <GuiListView.au3>

Global $Font1 = _WinAPI_CreateFont(14, 0, 0, 0, $FW_BOLD)
Global $Font2 = _WinAPI_CreateFont(14, 0, 0, 0, $FW_NORMAL, False, False, False, $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, _
                                   $CLIP_DEFAULT_PRECIS, $DEFAULT_QUALITY, 0, "MS Sans Serif")
#Region ### START $maingui ###
$maingui = GUICreate("Example", 860, 730, @DesktopWidth/2 - 860/2, @DesktopHeight/2 - 700/2, BitOR($WS_SIZEBOX, $WS_TABSTOP, $WS_MINIMIZEBOX, $WS_CLIPCHILDREN))
GUISetBkColor(0xFFFFFF, $maingui)
$maingui_lv = _GUICtrlListView_Create($maingui, "", 0, 0, 860, 480, BitOR($LVS_REPORT, $LVS_SHOWSELALWAYS))
_GUICtrlListView_AddColumn($maingui_lv, "Column 1", 100)
_GUICtrlListView_AddColumn($maingui_lv, "Column 2", 100)
_GUICtrlListView_AddColumn($maingui_lv, "Column 3", 100)
_GUICtrlListView_AddColumn($maingui_lv, "Column 4", 100)
_GUICtrlListView_AddColumn($maingui_lv, "Column 5", 100)
_GUICtrlListView_AddColumn($maingui_lv, "Column 6", 100)
_GUICtrlListView_AddColumn($maingui_lv, "Column 7", 100)
_GUICtrlListView_AddColumn($maingui_lv, "Column 8", 100)
$maingui_bn_populate = GUICtrlCreateButton("Populate", 10, 10, 100, 25)
GUIRegisterMsg($WM_SIZE, "WM_SIZE")
#EndRegion ### END $maingui ###

While 1
    $msg = GUIGetMsg(1)
    Switch $msg[1]
        Case $maingui
            Switch $msg[0]
                Case $GUI_EVENT_CLOSE
                Case $maingui_bn_populate

Func Populate()
    For $a = 1 To 10
        If $a = 5 or $a = 6 or $a = 10 Then
            _GUICtrlListView_AddItem($maingui_lv, "Test" & $a)
            $added = _GUICtrlListView_GetItemCount($maingui_lv) - 1
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "Wrong", 1)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 2)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 3)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 4)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 5)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 6)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 7)
        ElseIf $a = 2 or $a = 7 or $a = 8 Then
            _GUICtrlListView_AddItem($maingui_lv, "Test" & $a)
            $added = _GUICtrlListView_GetItemCount($maingui_lv) - 1
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "Right", 1)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 2)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 3)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 4)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 5)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 6)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "0", 7)
            _GUICtrlListView_AddItem($maingui_lv, "Test" & $a)
            $added = _GUICtrlListView_GetItemCount($maingui_lv) - 1
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "Right", 1)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "1", 2)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "1", 3)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "1", 4)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "1", 5)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "1", 6)
            _GUICtrlListView_AddSubItem($maingui_lv, $added, "1", 7)
    GUICtrlSetState($maingui_bn_populate, $GUI_DISABLE)

Func WM_SIZE($hWnd, $Msg, $wParam, $lParam) ;resize columns with gui resize
    Local $iHeight, $iWidth
    $iWidth = BitAND($lParam, 0xFFFF) ; _WinAPI_LoWord
    $iHeight = BitShift($lParam, 16) ; _WinAPI_HiWord
    GuiSetState(@SW_LOCK, $maingui)
    WinMove($maingui_lv, "", 0, 0, $iWidth, $iHeight - 70)
    $jWidth = $iWidth - 140
    _GUICtrlListView_SetColumnWidth($maingui_lv, 0, $jWidth*0.15)
    _GUICtrlListView_SetColumnWidth($maingui_lv, 1, $jWidth*0.20)
    _GUICtrlListView_SetColumnWidth($maingui_lv, 2, $jWidth*0.05)
    _GUICtrlListView_SetColumnWidth($maingui_lv, 3, $jWidth*0.125)
    _GUICtrlListView_SetColumnWidth($maingui_lv, 4, $jWidth*0.125)
    _GUICtrlListView_SetColumnWidth($maingui_lv, 5, $jWidth*0.125)
    _GUICtrlListView_SetColumnWidth($maingui_lv, 6, $jWidth*0.125)
    _GUICtrlListView_SetColumnWidth($maingui_lv, 7, -2)
    GUICtrlSetPos($maingui_bn_populate, $jWidth, $iHeight - 50)
    GuiSetState(@SW_UNLOCK, $maingui)

Func _WM_GETMINMAXINFO($hwnd, $Msg, $wParam, $lParam) ;lock gui min size
    If $hWnd = $maingui Then
        $tagMaxinfo = DllStructCreate("int;int;int;int;int;int;int;int;int;int", $lParam)
        DllStructSetData($tagMaxinfo, 7,  617.6) ; W (min size)
        DllStructSetData($tagMaxinfo, 8,  744) ; H (min size)
        Return 0

Func WM_NOTIFY($hWnd, $Msg, $wParam, $lParam)
    Local $tNMHDR, $hWndFrom, $iCode

    $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    $hWndFrom = DllStructGetData($tNMHDR, "hWndFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")

    Switch $hWndFrom
        Case $maingui_lv
            Switch $iCode
                Case $NM_CUSTOMDRAW
                    Local $tCustDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam)
                    Local $iDrawStage = DllStructGetData($tCustDraw, "dwDrawStage")
                    Local $iSubItem = DllStructGetData($tCustDraw, "iSubItem")
                    Local $iItem = DllStructGetData($tCustDraw, "dwItemSpec")
                    Local $iColor, $hDC
                      Switch $iDrawStage
                        Case $CDDS_PREPAINT
                            Return $CDRF_NOTIFYITEMDRAW ;request custom drawing of items
                        Case $CDDS_ITEMPREPAINT
                            Return $CDRF_NOTIFYSUBITEMDRAW ;request drawing each cell separately
                        Case Else
                            $hDC = DllStructGetData($tCustDraw, "hdc")
                            If _GUICtrlListView_GetItemText($maingui_lv, $iItem, 1) = "Wrong" Then
                                $iColor = RGB2BGR(0xFF0000)
                                _WinAPI_SelectObject($hDC, $Font1)
                                DllStructSetData($tCustDraw, "clrText", $iColor)
                            ElseIf _GUICtrlListView_GetItemText($maingui_lv, $iItem, 3) = "0" OR _GUICtrlListView_GetItemText($maingui_lv, $iItem, 4) = "0" OR _GUICtrlListView_GetItemText($maingui_lv, $iItem, 5) = "0" OR _GUICtrlListView_GetItemText($maingui_lv, $iItem, 7) = "0" Then
                                $iColor = RGB2BGR(0x000000)
                                _WinAPI_SelectObject($hDC, $Font2)
                                DllStructSetData($tCustDraw, "clrText", $iColor)
                                DllStructSetData($tCustDraw, "clrTextBk", RGB2BGR(0xFFFF80))
                            Return $CDRF_NEWFONT
                Case $LVN_ENDSCROLL
EndFunc   ;==>WM_NOTIFY

Func RGB2BGR($iColor)
    Return BitAND(BitShift(String(Binary($iColor)), 8), 0xFFFFFF)
EndFunc   ;==>RGB2BGR()
Edited by mpower

Share this post

Link to post
Share on other sites

mpower, If you add

statements around your populate loop the rows will be drawn properly.

If you don't use these statements a row will be drawn cell by cell. And for the first few cells in each row there is no information to draw the cells with a yellow background. The first test to draw a row with yellow background is in column 4:

ElseIf _GUICtrlListView_GetItemText($maingui_lv, $iItem, 3) = "0" ...
If you fill out the entire LV before you draw it, all information is available.

You can create the LV with the built-in command GUICtrlCreateListView. Then you can use GUICtrlSetResizing to resize the LV. Move most of the code from WM_SIZE function to the message loop:

Then you don't need the WM_SIZE function and the resize will be much better.

You should consider to fill out the LV with GUICtrlCreateListViewItem. This is much faster than using the UDF.

In the $NM_CUSTOMDRAW code you should not use slow UDF functions. Use an array to provide information for custom drawing. See post 7 here.

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this