Jump to content

Listview NM_CUSTOMDRAW and resizing - slow!


mpower
 Share

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-once
#NoTrayIcon
#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_SetExtendedListViewStyle($maingui_lv, BitOR($LVS_EX_GRIDLINES, $LVS_EX_FULLROWSELECT, $LVS_EX_SUBITEMIMAGES, $LVS_EX_CHECKBOXES, $LVS_EX_INFOTIP, $LVS_EX_DOUBLEBUFFER))
_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_NOTIFY, "WM_NOTIFY")
GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")
GUIRegisterMsg($WM_SIZE, "WM_SIZE")
GUIRegisterMsg($WM_GETMINMAXINFO, "_WM_GETMINMAXINFO")
GUISetState()
#EndRegion ### END $maingui ###

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

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)
        Else
            _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)
        EndIf
    Next
    GUICtrlSetState($maingui_bn_populate, $GUI_DISABLE)
EndFunc

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
    _GUICtrlListView_BeginUpdate($maingui_lv)
    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)
    _GUICtrlListView_EndUpdate($maingui_lv)
    Return $GUI_RUNDEFMSG
EndFunc

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
    EndIf
EndFunc   ;==>WM_GETMINMAXINFO

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))
                            EndIf
                            Return $CDRF_NEWFONT
                    EndSwitch
                Case $LVN_ENDSCROLL
                    _WinAPI_InvalidateRect($maingui_lv)
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

Func RGB2BGR($iColor)
    Return BitAND(BitShift(String(Binary($iColor)), 8), 0xFFFFFF)
EndFunc   ;==>RGB2BGR()
Edited by mpower
Link to comment
Share on other sites

mpower, If you add

_GUICtrlListView_BeginUpdate($maingui_lv)
_GUICtrlListView_EndUpdate($maingui_lv)
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:

Case $GUI_EVENT_RESTORE, $GUI_EVENT_MAXIMIZE, $GUI_EVENT_RESIZED
  ...
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.

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...