I ran into a weird issue with custom drawing the
listview
control. I drew the grid lines in response to
CDDS_POSTPAINT
notification. All working okay so far. However, when scrolling vertically, the lines will be messed up and will not be drawn at the correct positions unless some invalidation occurs after the scroll. Such as minimizing/restoring the window.
I've spent a while trying to figure out a solution to this issue. Nothing worked without introducing flickering.
I would really appreciate some hand over here.
Note: Code uses Common controls 6.0 through a manifest.
#include <Windows.h>
#include <string>
#include <commctrl.h>
#pragma comment (lib,"Comctl32.lib")
#pragma comment (lib,"uxtheme.lib")
HWND g_hWndListView;
static HINSTANCE hInst;
LRESULT CALLBACK MainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
void InitListview(HWND hListview) {
ListView_SetExtendedListViewStyle(hListview, LVS_EX_FULLROWSELECT
| LVS_EX_DOUBLEBUFFER | LVS_EX_HEADERDRAGDROP);
ListView_SetBkColor(hListview, RGB(32, 32, 32));
ListView_SetTextBkColor(hListview, RGB(32, 32, 32));
ListView_SetTextColor(hListview, RGB(240, 240, 240));
SendMessage(hListview, WM_CHANGEUISTATE,
MAKELONG(UIS_SET, UISF_HIDEFOCUS), 0);
static HWND hwndHeader = (HWND)SendMessageW
(hListview, LVM_GETHEADER, 0, 0);
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_TEXT | LVCF_WIDTH;
lvColumn.cx = 200;
lvColumn.pszText = (LPWSTR)L"Header 1";
SendMessage(hListview, LVM_INSERTCOLUMN, 0, (LPARAM)&lvColumn);
lvColumn.pszText = (LPWSTR)L"Header 2";
SendMessage(hListview, LVM_INSERTCOLUMN, 1, (LPARAM)&lvColumn);
lvColumn.pszText = (LPWSTR)L"Header 3";
SendMessage(hListview, LVM_INSERTCOLUMN, 2, (LPARAM)&lvColumn);
LVITEM lvItem;
lvItem.mask = LVIF_STATE | LVIF_TEXT;
lvItem.state = LVIF_PARAM;
lvItem.stateMask = NULL;
for (int i = 0; i < 20; i++) {
lvItem.iSubItem = 0;
lvItem.iItem = i;
lvItem.pszText = (LPWSTR)L"Item";
SendMessage(hListview, LVM_INSERTITEM, 0, (LPARAM)&lvItem);
}
}
void CreateListview(HWND hWnd) {
HWND hListview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW,
L"", WS_TABSTOP | WS_CHILD | WS_VISIBLE |
LVS_REPORT, 0, 0, 655, 160, hWnd, NULL, NULL, NULL);
::g_hWndListView = hListview;
InitListview(hListview);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPr,
LPSTR args, int ncmdshow) {
WNDCLASS WindowClass = { 0 };
WindowClass.hbrBackground = CreateSolidBrush(0);
WindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WindowClass.hInstance = hInst;
WindowClass.lpszClassName = L"MainWindClass";
WindowClass.lpfnWndProc = MainProc;
if (!RegisterClassW(&WindowClass)) {
return -1;
}
int wCntr = GetSystemMetrics(SM_CXSCREEN) / 2;
int hCntr = GetSystemMetrics(SM_CYSCREEN) / 2;
HWND MainWindow = CreateWindowA("MainWindClass", "Main",
WS_OVERLAPPEDWINDOW | WS_VISIBLE, wCntr - (655 / 2),
hCntr - (200 / 2), 800, 400, NULL, NULL, NULL, NULL);
MSG MainMsg = { 0 };
while (GetMessage(&MainMsg, NULL, NULL, NULL)) {
TranslateMessage(&MainMsg);
DispatchMessage(&MainMsg);
}
return 0;
}
void ListView_DrawGridLines(HDC hDC,COLORREF GridColor)
{
HWND hwndHeader;
DWORD Counter = 0;
DWORD ColumnsCount;
DWORD ColumnWidth;
RECT rectHeader;
RECT rectLV;
RECT ItemRect = { 0 };
DWORD ItemHeight;
int scrollHPos;
COLORREF oldBkColor;
RECT LineRect{ 0 };
DWORD borderWidth;
DWORD LineIndex;
oldBkColor = SetBkColor(hDC, GridColor);
hwndHeader = (HWND)SendMessage(g_hWndListView, LVM_GETHEADER, 0, 0);
ColumnsCount = SendMessage(hwndHeader, HDM_GETITEMCOUNT, 0, 0);
GetClientRect(g_hWndListView, &rectLV);
GetClientRect(hwndHeader, &rectHeader);
scrollHPos = GetScrollPos(g_hWndListView, SB_HORZ) * -1;
if (scrollHPos >= rectLV.right)
return;
borderWidth = GetSystemMetrics(SM_CXBORDER);
while (Counter < ColumnsCount)
{
ColumnWidth = SendMessage(g_hWndListView,
LVM_GETCOLUMNWIDTH, Counter, 0);
scrollHPos = scrollHPos + ColumnWidth;
if (scrollHPos <= 0)
break;
LineRect.left = scrollHPos;
LineRect.top = rectHeader.bottom;
LineRect.right = scrollHPos + borderWidth;
LineRect.bottom = rectLV.bottom;
ExtTextOutW(hDC, 0, 0, ETO_OPAQUE,
&LineRect, NULL, NULL, NULL);
Counter++;
}
ItemRect.left = LVIR_BOUNDS;
SendMessage(g_hWndListView, LVM_GETITEMRECT,
0,(LPARAM)&ItemRect);
ItemHeight = ItemRect.bottom - ItemRect.top;
LineIndex = rectHeader.bottom - 1;
LineRect.bottom = LineIndex;
while (LineIndex < rectLV.bottom)
{
LineIndex = LineIndex + borderWidth;
LineRect.left = 0;
LineRect.top = LineIndex - 1;
LineRect.right = rectLV.right;
LineRect.bottom = LineIndex;
ExtTextOutW(hDC, 0, 0, ETO_OPAQUE,
&LineRect, NULL, NULL, NULL);
LineIndex = (LineRect.bottom - 1) + ItemHeight;
}
SetBkColor(hDC, oldBkColor);
}
int ListView_CustomDraw(LPNMLVCUSTOMDRAW lpnmlv)
{
switch (lpnmlv->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
return CDRF_NOTIFYPOSTPAINT;
break;
case CDDS_POSTPAINT:
ListView_DrawGridLines(lpnmlv->nmcd.hdc, RGB(89, 89, 89));
return CDRF_SKIPDEFAULT;
break;
default:
return CDRF_DODEFAULT;
}
}
LRESULT CALLBACK MainProc(HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
RECT rect;
switch (msg) {
case WM_CREATE: {
CreateListview(hWnd);
break;
}
case WM_NOTIFY:
{
LPNMHDR nmhdr = (LPNMHDR)lParam;
if (nmhdr->code == NM_CUSTOMDRAW &&
nmhdr->hwndFrom == g_hWndListView)
{
LPNMLVCUSTOMDRAW lpNMCustomDraw =
(LPNMLVCUSTOMDRAW)lParam;
return ListView_CustomDraw(lpNMCustomDraw);
}
break;
}
case WM_SIZE:
{
GetClientRect(hWnd, &rect);
SetWindowPos(g_hWndListView, NULL, 0, 0,
rect.right - 2, rect.bottom - 2,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
}
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
What I have tried:
I tried invalidating the
listview rect
in response to
LVN_ENDSCROLL
notification but that introduces flickering.
Tried using
MoveToEx
/
LineTo
APIs. Same issue. The horizontal lines disappear on scrolling vertically.