Last time, we had figured out how to prevent the version 5 ListView Win32 common control from resizing columns, but we noticed that the cursor still changes to a resize cursor that doesn’t work. How can we avoid misleading the user?
I had a few ideas but decided that the easiest way would be to subclass the header control and override its WM_ message with one that just sets the arrow cursor.
LRESULT CALLBACK AlwaysArrowSubclassProc(
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
[[maybe_unused]] UINT_PTR uIdSubclass,
[[maybe_unused]] DWORD_PTR dwRefData)
{
switch (uMsg) {
case WM_SETCURSOR:
SetCursor(LoadCursor(nullptr, IDC_ARROW));
return 1;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
case WM_CREATE: // or WM_INITDIALOG if this is a dialog procedure
⟦ ... ⟧
SetWindowSubclass(ListView_GetHeader(hwndLV),
AlwaysArrowSubclassProc, 1, 0);
⟦ ... ⟧
return 0;
case WM_DESTROY:
RemoveWindowSubclass(ListView_GetHeader(hwndLV),
AlwaysArrowSubclassProc, 1);
⟦ ... ⟧
return 0;
Alternatively, we could have the subclass procedure be self-destroying.
LRESULT CALLBACK AlwaysArrowSubclassProc(
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, [[maybe_unused]] DWORD_PTR dwRefData)
{
switch (uMsg) {
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, AlwaysArrowSubclassProc, uIdSubclass);
break;
case WM_SETCURSOR:
SetCursor(LoadCursor(nullptr, IDC_ARROW));
return 1;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
case WM_CREATE: // or WM_INITDIALOG if this is a dialog procedure
⟦ ... ⟧
SetWindowSubclass(ListView_GetHeader(hwndLV),
AlwaysArrowSubclassProc, 1, 0);
⟦ ... ⟧
return 0;
case WM_DESTROY:
// RemoveWindowSubclass(ListView_GetHeader(hwndLV),
// AlwaysArrowSubclassProc, 1);
⟦ ... ⟧
return 0;
We could generalize this subclass procedure so it always sets the cursor to one specified in its dwRefData.
LRESULT CALLBACK FixedCursorSubclassProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, [[maybe_unused]] DWORD_PTR dwRefData) { switch (uMsg) { case WM_NCDESTROY: RemoveWindowSubclass(hWnd, FixedCursorSubclassProc, uIdSubclass); break; case WM_SETCURSOR: SetCursor((HCURSOR)dwRefData); return 1; } return DefSubclassProc(hWnd, uMsg, wParam, lParam); } case WM_CREATE: // or WM_INITDIALOG if this is a dialog procedure ⟦ ... ⟧ SetWindowSubclass(ListView_GetHeader(hwndLV), FixedCursorSubclassProc, 1, (DWORD_PTR)LoadCursor(nullptr, IDC_ARROW)); ⟦ ... ⟧ return 0;
And then I realized that I don’t need to set the cursor at all. The default window procedure sets the cursor to the class cursor. We just have to call it.
LRESULT CALLBACK ClassCursorSubclassProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, [[maybe_unused]] DWORD_PTR dwRefData) { switch (uMsg) { case WM_NCDESTROY: RemoveWindowSubclass(hWnd, ClassCursorSubclassProc, uIdSubclass); break; case WM_SETCURSOR: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return DefSubclassProc(hWnd, uMsg, wParam, lParam); } case WM_CREATE: // or WM_INITDIALOG if this is a dialog procedure ⟦ ... ⟧ SetWindowSubclass(ListView_GetHeader(hwndLV), ClassCursorSubclassProc, 1, 0); ⟦ ... ⟧ return 0;
Recall that all of this work is just to work around the lack of support for the HDS_ style in the version 5 common controls library. If you are using version 6 (and really, you should be), then just set the HDS_ style onto the ListView’s header control, and you’re all done.
0 comments
Be the first to start the discussion.