Skip to content

Instantly share code, notes, and snippets.

@AHK-just-me
Last active July 29, 2019 10:15
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AHK-just-me/4427236 to your computer and use it in GitHub Desktop.
Save AHK-just-me/4427236 to your computer and use it in GitHub Desktop.
Class_LV_Colors{} - Set background and/or text colours for individual cells or rows in an AHK GUI ListView control.
; ======================================================================================================================
; Namespace: LV_Colors
; AHK version: AHK 1.1.09.02
; Function: Helper object and functions for ListView row and cell coloring
; Language: English
; Tested on: Win XPSP3, Win VistaSP2 (U32) / Win 7 (U64)
; Version: 0.1.00.00/2012-10-27/just me
; 0.2.00.00/2013-01-12/just me - bugfixes and minor changes
; 0.3.00.00/2013-06-15/just me - added "Critical, 100" to avoid drawing issues
; ======================================================================================================================
; CLASS LV_Colors
;
; The class provides seven public methods to register / unregister coloring for ListView controls, to set individual
; colors for rows and/or cells, to prevent/allow sorting and rezising dynamically, and to register / unregister the
; included message handler function for WM_NOTIFY -> NM_CUSTOMDRAW messages.
;
; If you want to use the included message handler you must call LV_Colors.OnMessage() once.
; Otherwise you should integrate the code within LV_Colors_WM_NOTIFY into your own notification handler.
; Without notification handling coloring won't work.
; ======================================================================================================================
Class LV_Colors {
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; PRIVATE PROPERTIES ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Static MessageHandler := "LV_Colors_WM_NOTIFY"
Static WM_NOTIFY := 0x4E
Static SubclassProc := RegisterCallback("LV_Colors_SubclassProc")
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; META FUNCTIONS ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
__New(P*) {
Return False ; There is no reason to instantiate this class!
}
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; PRIVATE METHODS +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
On_NM_CUSTOMDRAW(H, L) {
Static CDDS_PREPAINT := 0x00000001
Static CDDS_ITEMPREPAINT := 0x00010001
Static CDDS_SUBITEMPREPAINT := 0x00030001
Static CDRF_DODEFAULT := 0x00000000
Static CDRF_NEWFONT := 0x00000002
Static CDRF_NOTIFYITEMDRAW := 0x00000020
Static CDRF_NOTIFYSUBITEMDRAW := 0x00000020
Static CLRDEFAULT := 0xFF000000
; Size off NMHDR structure
Static NMHDRSize := (2 * A_PtrSize) + 4 + (A_PtrSize - 4)
; Offset of dwItemSpec (NMCUSTOMDRAW)
Static ItemSpecP := NMHDRSize + (5 * 4) + A_PtrSize + (A_PtrSize - 4)
; Size of NMCUSTOMDRAW structure
Static NCDSize := NMHDRSize + (6 * 4) + (3 * A_PtrSize) + (2 * (A_PtrSize - 4))
; Offset of clrText (NMLVCUSTOMDRAW)
Static ClrTxP := NCDSize
; Offset of clrTextBk (NMLVCUSTOMDRAW)
Static ClrTxBkP := ClrTxP + 4
; Offset of iSubItem (NMLVCUSTOMDRAW)
Static SubItemP := ClrTxBkP + 4
; Offset of clrFace (NMLVCUSTOMDRAW)
Static ClrBkP := SubItemP + 8
DrawStage := NumGet(L + NMHDRSize, 0, "UInt")
, Row := NumGet(L + ItemSpecP, 0, "UPtr") + 1
, Col := NumGet(L + SubItemP, 0, "Int") + 1
; SubItemPrepaint ------------------------------------------------------------------------------------------------
If (DrawStage = CDDS_SUBITEMPREPAINT) {
NumPut(This[H].CurTX, L + ClrTxP, 0, "UInt"), NumPut(This[H].CurTB, L + ClrTxBkP, 0, "UInt")
, NumPut(This[H].CurBK, L + ClrBkP, 0, "UInt")
ClrTx := This[H].Cells[Row][Col].T, ClrBk := This[H].Cells[Row][Col].B
If (ClrTx <> "")
NumPut(ClrTX, L + ClrTxP, 0, "UInt")
If (ClrBk <> "")
NumPut(ClrBk, L + ClrTxBkP, 0, "UInt"), NumPut(ClrBk, L + ClrBkP, 0, "UInt")
If (Col > This[H].Cells[Row].MaxIndex()) && !This[H].HasKey(Row)
Return CDRF_DODEFAULT
Return CDRF_NOTIFYSUBITEMDRAW
}
; ItemPrepaint ---------------------------------------------------------------------------------------------------
If (DrawStage = CDDS_ITEMPREPAINT) {
This[H].CurTX := This[H].TX, This[H].CurTB := This[H].TB, This[H].CurBK := This[H].BK
ClrTx := ClrBk := ""
If This[H].Rows.HasKey(Row)
ClrTx := This[H].Rows[Row].T, ClrBk := This[H].Rows[Row].B
If (ClrTx <> "")
NumPut(ClrTx, L + ClrTxP, 0, "UInt"), This[H].CurTX := ClrTx
If (ClrBk <> "")
NumPut(ClrBk, L + ClrTxBkP, 0, "UInt") , NumPut(ClrBk, L + ClrBkP, 0, "UInt")
, This[H].CurTB := ClrBk, This[H].CurBk := ClrBk
If This[H].Cells.HasKey(Row)
Return CDRF_NOTIFYSUBITEMDRAW
Return CDRF_DODEFAULT
}
; Prepaint -------------------------------------------------------------------------------------------------------
If (DrawStage = CDDS_PREPAINT) {
Return CDRF_NOTIFYITEMDRAW
}
; Others ---------------------------------------------------------------------------------------------------------
Return CDRF_DODEFAULT
}
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; PUBLIC METHODS ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; ===================================================================================================================
; Attach() Register ListView control for coloring
; Parameters: HWND - ListView's HWND.
; Optional ------------------------------------------------------------------------------------------
; NoSort - Prevent sorting by click on a header item.
; Values: True / False
; Default: True
; NoSizing - Prevent resizing of columns.
; Values: True / False
; Default: True
; Return Values: True on success, otherwise false.
; ===================================================================================================================
Attach(HWND, NoSort = True, NoSizing = True) {
Static LVM_GETBKCOLOR := 0x1000
Static LVM_GETHEADER := 0x101F
Static LVM_GETTEXTBKCOLOR := 0x1025
Static LVM_GETTEXTCOLOR := 0x1023
Static LVM_SETEXTENDEDLISTVIEWSTYLE := 0x1036
Static LVS_EX_DOUBLEBUFFER := 0x00010000
If !DllCall("User32.dll\IsWindow", "Ptr", HWND, "UInt")
Return False
If This.HasKey(HWND)
Return False
; Set LVS_EX_DOUBLEBUFFER style to avoid drawing issues, if it isn't set as yet.
SendMessage, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER, , ahk_id %HWND%
If (ErrorLevel = "FAIL")
Return False
; Get the default colors
SendMessage, LVM_GETBKCOLOR, 0, 0, , ahk_id %HWND%
BkClr := ErrorLevel
SendMessage, LVM_GETTEXTBKCOLOR, 0, 0, , ahk_id %HWND%
TBClr := ErrorLevel
SendMessage, LVM_GETTEXTCOLOR, 0, 0, , ahk_id %HWND%
TxClr := ErrorLevel
; Get the header control
SendMessage, LVM_GETHEADER, 0, 0, , ahk_id %HWND%
Header := ErrorLevel
; Store the values in a new object
This[HWND] := {BK: BkClr, TB: TBClr, TX: TxClr, Header: Header}
If (NoSort)
This.NoSort(HWND)
If (NoSizing)
This.NoSizing(HWND)
Return True
}
; ===================================================================================================================
; Detach() Unregister ListView control
; Parameters: HWND - ListView's HWND
; Return Value: Always True
; ===================================================================================================================
Detach(HWND) {
; Remove the subclass, if any
If (This[HWND].SC)
DllCall("Comctl32.dll\RemoveWindowSubclass", "Ptr", HWND, "Ptr", This.SubclassProc, "Ptr", HWND)
This.Remove(HWND, "")
WinSet, Redraw, , ahk_id %HWND%
Return True
}
; ===================================================================================================================
; Row() Set background and/or text color for the specified row
; Parameters: HWND - ListView's HWND
; Row - Row number
; Optional ------------------------------------------------------------------------------------------
; BkColor - Background color as RGB color integer (e.g. 0xFF0000 = red)
; Default: Empty -> default background color
; TxColor - Text color as RGB color integer (e.g. 0xFF0000 = red)
; Default: Empty -> default text color
; Return Value: True on success, otherwise false.
; ===================================================================================================================
Row(HWND, Row, BkColor = "", TxColor = "") {
If !This.HasKey(HWND)
Return False
If (BkColor = "") && (TxColor = "") {
This[HWND].Rows.Remove(Row, "")
Return True
}
BkBGR := TxBGR := ""
If BkColor Is Integer
BkBGR := ((BkColor & 0xFF0000) >> 16) | (BkColor & 0x00FF00) | ((BkColor & 0x0000FF) << 16)
If TxColor Is Integer
TxBGR := ((TxColor & 0xFF0000) >> 16) | (TxColor & 0x00FF00) | ((TxColor & 0x0000FF) << 16)
If (BkBGR = "") && (TxBGR = "")
Return False
If !This[HWND].HasKey("Rows")
This[HWND].Rows := {}
If !This[HWND].Rows.HasKey(Row)
This[HWND].Rows[Row] := {}
If (BkBGR <> "")
This[HWND].Rows[Row].Insert("B", BkBGR)
If (TxBGR <> "")
This[HWND].Rows[Row].Insert("T", TxBGR)
Return True
}
; ===================================================================================================================
; Cell() Set background and/or text color for the specified cell
; Parameters: HWND - ListView's HWND
; Row - Row number
; Col - Column number
; Optional ------------------------------------------------------------------------------------------
; BkColor - Background color as RGB color integer (e.g. 0xFF0000 = red)
; Default: Empty -> default background color
; TxColor - Text color as RGB color integer (e.g. 0xFF0000 = red)
; Default: Empty -> default text color
; Return Value: True on success, otherwise false.
; ===================================================================================================================
Cell(HWND, Row, Col, BkColor = "", TxColor = "") {
If !This.HasKey(HWND)
Return False
If (BkColor = "") && (TxColor = "") {
This[HWND].Cells.Remove(Row, "")
Return True
}
BkBGR := TxBGR := ""
If BkColor Is Integer
BkBGR := ((BkColor & 0xFF0000) >> 16) | (BkColor & 0x00FF00) | ((BkColor & 0x0000FF) << 16)
If TxColor Is Integer
TxBGR := ((TxColor & 0xFF0000) >> 16) | (TxColor & 0x00FF00) | ((TxColor & 0x0000FF) << 16)
If (BkBGR = "") && (TxBGR = "")
Return False
If !This[HWND].HasKey("Cells")
This[HWND].Cells := {}
If !This[HWND].Cells.HasKey(Row)
This[HWND].Cells[Row] := {}
This[HWND].Cells[Row, Col] := {}
If (BkBGR <> "")
This[HWND].Cells[Row, Col].Insert("B", BkBGR)
If (TxBGR <> "")
This[HWND].Cells[Row, Col].Insert("T", TxBGR)
Return True
}
; ===================================================================================================================
; NoSort() Prevent / allow sorting by click on a header item dynamically.
; Parameters: HWND - ListView's HWND
; Optional ------------------------------------------------------------------------------------------
; DoIt - True / False
; Default: True
; Return Value: True on success, otherwise false.
; ===================================================================================================================
NoSort(HWND, DoIt = True) {
Static HDM_GETITEMCOUNT := 0x1200
If !This.HasKey(HWND)
Return False
If (DoIt)
This[HWND].NS := True
Else
This[HWND].Remove("NS")
Return True
}
; ===================================================================================================================
; NoSizing() Prevent / allow resizing of columns dynamically.
; Parameters: HWND - ListView's HWND
; Optional ------------------------------------------------------------------------------------------
; DoIt - True / False
; Default: True
; Return Value: True on success, otherwise false.
; ===================================================================================================================
NoSizing(HWND, DoIt = True) {
Static OSVersion := DllCall("Kernel32.dll\GetVersion", "UChar")
Static HDS_NOSIZING := 0x0800
If !This.HasKey(HWND)
Return False
HHEADER := This[HWND].Header
If (DoIt) {
If (OSVersion < 6) {
If !(This[HWND].SC) {
DllCall("Comctl32.dll\SetWindowSubclass", "Ptr", HWND, "Ptr", This.SubclassProc, "Ptr", HWND, "Ptr", 0)
This[HWND].SC := True
} Else {
Return True
}
} Else {
Control, Style, +%HDS_NOSIZING%, , ahk_id %HHEADER%
}
} Else {
If (OSVersion < 6) {
If (This[HWND].SC) {
DllCall("Comctl32.dll\RemoveWindowSubclass", "Ptr", HWND, "Ptr", This.SubclassProc, "Ptr", HWND)
This[HWND].Remove("SC")
} Else {
Return True
}
} Else {
Control, Style, -%HDS_NOSIZING%, , ahk_id %HHEADER%
}
}
Return True
}
; ===================================================================================================================
; OnMessage() Register / unregister LV_Colors message handler for WM_NOTIFY -> NM_CUSTOMDRAW messages
; Parameters: DoIt - True / False
; Default: True
; Return Value: Always True
; ===================================================================================================================
OnMessage(DoIt = True) {
If (DoIt)
OnMessage(This.WM_NOTIFY, This.MessageHandler)
Else If (This.MessageHandler = OnMessage(This.WM_NOTIFY))
OnMessage(This.WM_NOTIFY, "")
Return True
}
}
; ======================================================================================================================
; PRIVATE FUNCTION LV_Colors_WM_NOTIFY() - message handler for WM_NOTIFY -> NM_CUSTOMDRAW notifications
; ======================================================================================================================
LV_Colors_WM_NOTIFY(W, L) {
Static NM_CUSTOMDRAW := -12
Static LVN_COLUMNCLICK := -108
Critical, 100
If LV_Colors.HasKey(H := NumGet(L + 0, 0, "UPtr")) {
M := NumGet(L + (A_PtrSize * 2), 0, "Int")
; NM_CUSTOMDRAW --------------------------------------------------------------------------------------------------
If (M = NM_CUSTOMDRAW)
Return LV_Colors.On_NM_CUSTOMDRAW(H, L)
; LVN_COLUMNCLICK ------------------------------------------------------------------------------------------------
If (LV_Colors[H].NS && (M = LVN_COLUMNCLICK))
Return 0
}
}
; ======================================================================================================================
; PRIVATE FUNCTION LV_Colors_SubclassProc() - subclass for WM_NOTIFY -> HDN_BEGINTRACK notifications (Win XP)
; ======================================================================================================================
LV_Colors_SubclassProc(H, M, W, L, S, R) {
Static HDN_BEGINTRACKA := -306
Static HDN_BEGINTRACKW := -326
Static WM_NOTIFY := 0x4E
Critical, 100
If (M = WM_NOTIFY) {
; HDN_BEGINTRACK -------------------------------------------------------------------------------------------------
C := NumGet(L + (A_PtrSize * 2), 0, "Int")
If (C = HDN_BEGINTRACKA) || (C = HDN_BEGINTRACKW) {
Return True
}
}
Return DllCall("Comctl32.dll\DefSubclassProc", "Ptr", H, "UInt", M, "Ptr", W, "Ptr", L, "UInt")
}
; ======================================================================================================================
#NoEnv
SetBatchLines, -1
#Include Class_LV_Colors.ahk
Gui, Margin, 20, 20
Gui, Add, ListView, w450 r15 Grid -ReadOnly vVLV hwndHLV
, Column 1|Column 2|Column 3|Column 4|Column 5
Loop, 256
LV_Add("", "Value " . A_Index, "Value " . A_Index, "Value " . A_Index, "Value " . A_Index, "Value " . A_Index)
Loop, % LV_GetCount("Column")
LV_ModifyCol(A_Index, "AutoHdr")
Gui, Add, Button, wp gSubColors vBtnColors, Colors on!
Gui, Show, , ListView & Colors
LV_Colors.OnMessage()
Return
GuiClose:
GuiEscape:
ExitApp
SubColors:
GuiControlGet, BtnColors
GuiControl, -Redraw, %HLV%
If (BtnColors = "Colors on!") {
If !LV_Colors.Attach(HLV) {
GuiControl, +Redraw, %HLV%
Return
}
Sleep, 10
Loop, 256 {
If (A_Index & 1) {
LV_Colors.Row(HLV, A_Index, 0xFF0000, 0xFFFF00)
LV_Colors.Cell(HLV, A_Index, 1, 0x00FF00, 0x000080)
LV_Colors.Cell(HLV, A_Index, 3, 0x00FF00, 0x000080)
LV_Colors.Cell(HLV, A_Index, 5, 0x00FF00, 0x000080)
} Else {
LV_Colors.Row(HLV, A_Index, 0x000080, 0x00FF00)
}
GuiControl, , BtnColors, Colors off!
}
} Else {
LV_Colors.Detach(HLV)
GuiControl, , BtnColors, Colors on!
}
GuiControl, +Redraw, %HLV%
Return
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment