Last time, we saw how to prevent the user from changing the widths of ListView columns, but the technique required version 6 of the common controls. What if you’re stuck in the dark ages and have to use version 5?
You can deny the ability to change the width of a header item by listening for HDN_ and returning 1 to deny the change if there is a change to the width.
case WM_NOTIFY:
{
auto hdr = (NMHDR*)lParam;
if (hdr->code == HDN_ITEMCHANGING) {
auto header = (NMHEADER*)lParam;
if (header->pitem->mask & HDI_WIDTH) {
return 1;
}
}
}
return 0;
The above code assumes that it is running in a window procedure. If it’s running in a dialog procedure, then you need to set the dialog message result.
case WM_NOTIFY:
{
auto hdr = (NMHDR*)lParam;
if (hdr->code == HDN_ITEMCHANGING) {
auto header = (NMHEADER*)lParam;
if (header->pitem->mask & HDI_WIDTH) {
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 1);
return TRUE;
}
}
}
return FALSE;
Note that if somebody tries to change both the width and the text, this will reject the entire change. There is, unfortunately, no way to selectively reject the change: Modifications to header->pitem->mask are ignored.¹
However, all is not lost. Even though changes to the mask are ignored, changes to the pitem->cxy are still honored, so we can just set the width back to whatever the width is right now.
case WM_NOTIFY:
{
auto hdr = (NMHDR*)lParam;
if (hdr->code == HDN_ITEMCHANGING) {
auto header = (NMHEADER*)lParam;
if (header->pitem->mask & HDI_WIDTH) {
HDITEM item;
item.mask = HDI_WIDTH;
if (Header_GetItem(nm->hdr.hwndFrom, nm->iItem, &item)) {
{
header->pitem->cxy = item.cxy;
}
}
}
}
return 0;
One thing we haven’t fixed, though, is that the mouse cursor changes to a resize cursor when it is on the border between two column headers, even though resizing has been disabled. We’ll try to fix that next time.
¹ This is arguably a bug in the version 5 header control, but there’s no point trying to fix it now. There may be code that relies on the fact that changes to the mask have no effect, and besides, this is the old and busted version 5 control.²
² The version 6 control has the same bug, but again, there’s no point trying to fix it now because it will almost certainly break someone. The version 6 common controls are 25 years old, and it’s probably safe to assume that every possible change will probably break someone.³
³ Once, I helped fixed a memory leak in the common controls, but we had to back it out because it broke a major application. We couldn’t figure out why it broke the program, so we couldn’t put together a shim. We just had to restore the leak. My guess is that the developers of the program had discovered the leak on their own and was somehow working around it, and our fix broke their workaround.
Maybe a stupid question, but how come there wasn’t any common control v7? Did the controls really peek in 2000s or was is better to just create separate libraries for new controls to avoid compatibility issues and other hassle like manifests?
Raw Win32(for lack of a better term) stopped being the main platform that new functionality was developed on.
New controls/functionality/etc. made after this point were either made in COM(so there's no need to mess around with common control libraries and manifests), or were made as a part of .NET, whether that was WinForms, WPF, etc.
And they do actually add new functionality to the common controls library, they just don't version it anymore. (Well, they do, but as part of the OS.)
For example, being able to add a UAC badge to a Win32 button(as well as other controls) was added in Windows...
s/helped fixed/helped fix/
> We couldn’t figure out why it broke the program, so we couldn’t put together a shim. We just had to restore the leak.
Oh fun. The leak should be documented publicly so everybody else can know how to avoid it. With VDI servers, memory leaks in GUI applications are important again.