Explorer‑style hover for SysListView32 that keeps the hot row in sync while the list scrolls under a stationary pointer (mouse wheel or precision touchpad). No overlays. Single shared manager timer. Multi‑ListView ready.
Ever hovered a ListView and lost the highlight the moment you scroll?
Ever clicked twice because the “hot row” slipped under your mouse?
CreateLVHover fixes that tiny, daily frustration. It keeps the hot row in sync while the list moves beneath a stationary pointer — mouse wheel or precision touchpad. No overlays, no hacks, no flicker. One shared manager timer. Works across multiple ListViews.
If this helps you, consider sponsoring or buying me a coffee — it keeps the lights on and the code clean. ☕
#Requires AutoHotkey v2.0
#Include <CreateLVHover>
gui := Gui()
lv := gui.Add("ListView", "-Hdr r15 w320", ["Name"]) ; any standard ListView
for v in ["Alpha","Beta","Gamma"]
lv.Add("", v)
gui.Show()
hover := CreateLVHover(lv, {
underline: true, ; underline on hover (Explorer‑style)
oneClick: true, ; one‑click activate + TRACKSELECT
hoverMs: 1000, ; time before TRACKSELECT (ms)
timerMs: 30, ; poll interval for "scroll under pointer"
pxJiggle: 1, ; minimal pointer nudge to trigger real WM_MOUSEMOVE
scrollWindowMs: 150 ; grace period after scroll where jiggle is allowed
})Place CreateLVHover.ahk in one of the standard library locations so #Include <CreateLVHover> can find it:
- Local library (next to your script)
.<your script folder>\Lib\CreateLVHover.ahk
- User library (per‑user)
%A_MyDocuments%\AutoHotkey\Lib\CreateLVHover.ahk
- Standard library (AutoHotkey install folder)
...\AutoHotkey\Lib\CreateLVHover.ahk(theLibfolder next toAutoHotkey.exe)
Using angle brackets with
#Include <CreateLVHover>tells AutoHotkey to search the library folders above.Alternative: Include by explicit path if you prefer:
#Include "C:\Path\To\CreateLVHover.ahk"
Constructor
CreateLVHover(target, options := {})target— the ListView to enhance. Accepts:- a ListView control object (recommended),
- a raw HWND (Integer), or
- a WinTitle/ahk_id string.
The constructor strictly validates that the resolved window is a SysListView32. If it isn’t, a friendly message box explains the issue and the script exits. There is no child auto‑discovery to avoid picking the wrong ListView when multiple exist in the same window.
options— all fields optional:
| Option | Type | Default | Description |
|---|---|---|---|
underline |
Boolean | true |
Show underline on hot items (LVS_EX_UNDERLINEHOT). |
oneClick |
Boolean | true |
One‑click activation + LVS_EX_TRACKSELECT. If false, behaves like double‑click activation (no auto‑select). |
hoverMs |
Integer | 1000 |
Hover time before TRACKSELECT triggers (only relevant if oneClick:true). |
timerMs |
Integer | 30 |
Polling interval used to detect “scroll under pointer.” |
pxJiggle |
Integer | 1 |
Minimal pointer movement (px) to trigger a real WM_MOUSEMOVE. |
scrollWindowMs |
Integer | 150 |
Grace period after a scroll message during which jiggle is allowed. |
scrollWindowMsPointer |
Integer | 220 |
slightly longer grace period for precision touchpad / gesture events. |
Instance methods
UpdateNow()— Force a single poll of the active instance.IsActive()— Returnstruewhile this instance is the active one under the pointer.
Status helper
CreateLVHover.GetStatusArray()→ returns an array of objects with per‑instance info:
[
{ hwnd: 0x123456, active: true/false, visibleEnabled: true/false, lastIdx: N },
...
]Create one instance per ListView:
h1 := CreateLVHover(lv1),h2 := CreateLVHover(lv2), etc.
- Explorer styling — The class applies
SetWindowTheme("Explorer")and extended styles (e.g.,LVS_EX_UNDERLINEHOT,LVS_EX_TRACKSELECT,LVS_EX_ONECLICKACTIVATE) so the ListView behaves like Windows Explorer. - Single manager timer — A class‑level timer figures out which ListView the pointer is currently over and calls
_Poll()only on that instance. - Scroll hooks (class‑level) — On scroll/gesture messages, the class resets the hover timer (
LVM_SETHOVERTIME(hoverMs)+_RequestHover()), opens a short grace window, and performs a 1‑px “micro‑jiggle” (unless at top/bottom) to provoke a realWM_MOUSEMOVEwithout visible cursor stepping. - Hit‑testing —
_Poll()usesLVM_SUBITEMHITTESTto find the row under the pointer and updates the hot state. When nothing changes (same row, same mouse position, grace window elapsed), it returns early. - No overlays — The highlight is native, coming from the OS theme; the class does not draw custom rectangles.
- Hover colors come from the OS theme. To customize hover colors per row/cell, use
owner‑drawor an overlay. - Works with all standard ListView views (report, list, small/large icons) that use the common control class
SysListView32. - The class avoids jiggle at the top/bottom boundary of the list (no jiggle if a scroll wouldn’t move content).
- Designed to be lightweight: one shared timer, fast routing via an internal HWND→instance map, minimal allocations per tick.
-
Library not found
- Ensure the file name is exactly
CreateLVHover.ahkand is placed in one of the library folders listed under Installation.
- Ensure the file name is exactly
-
Invalid target
- The constructor validates that the target resolves to a
SysListView32. If not, a friendly diagnostic message is shown and the script stops.
- The constructor validates that the target resolves to a
-
No underline / no hover
- Make sure
underline: trueandoneClick: true(which enables TRACKSELECT). You can tweakhoverMsto your liking.
- Make sure
-
Hover doesn’t update during scrolling
- Increase
scrollWindowMs(e.g., 200–300) or reducetimerMs(e.g., 20). - Ensure the pointer is actually over the ListView while scrolling — the grace window only opens then.
- Increase
-
Multiple ListViews
- Create one instance per ListView:
h1 := CreateLVHover(lv1),h2 := CreateLVHover(lv2), etc. The class manages a single shared timer internally.
- Create one instance per ListView:
Project‑local library (recommended for per‑project isolation):
MyApp\
├─ MyApp.ahk
└─ Lib\
└─ CreateLVHover.ahk
Then in MyApp.ahk:
#Include <CreateLVHover>User library (available to all scripts for your user account):
%A_MyDocuments%\AutoHotkey\Lib\CreateLVHover.ahk
Standard library (available to all scripts on the machine):
<AutoHotkey install folder>\Lib\CreateLVHover.ahk
Direct path include (alternative to library folders):
#Include "C:\Path\To\CreateLVHover.ahk"This project is licensed under the terms described in the accompanying LICENSE file.
- Tested on Windows 11 with AutoHotkey v2.
- Uses only public Windows APIs and standard AHK directives.
- Thanks to the Windows common controls documentation and community examples for the underlying message constants and techniques.
