How To Disable Resizing in a ListView (Win32 / C++)

2013-09-04 (Wednesday) by Mikael Linusson

I thought that disabling the column resize on a Win32 ListView would be very easy (like set a property or send a message), I was wrong. And as I didn't find any good example of a satisfactory and completely working solution, I made my own. My hope now is that this tutorial will save some time and frustration for someone else out there. :)

Tutorial + Example Source in C/C++

This is a tutorial (with example source) of how to disable resizing of the columns in a ListView. The example source code is written in regular Win32 C/C++ (Visual Studio 2008) and I've tried to keep it as simple as possible. It should be easy to convert to other languages, for example Delphi, if that's what you are using. Because the solution should still be the same.

I assume that you have some prior knowledge of both Win32 programming and C/C++ programming as I only focus on the subject at hand. I've also avoided classes just to keep things simple.

What You'll Learn in This Tutorial

It depends on how much you already knows. ;)

The Important Things:
What's this tutorial is all about.

  1. How to stop the users from being able to resize the columns in a Win32 ListView (we'll stop both drag resize and double click in header "to fit" resize).
  2. How to hide the resize mouse cursor, i.e. how to always show the normal cursor when hovering over the header.
  3. How to subclass a ListView
  4. How to subclass an automatically created ListView Header

The Bonus Things:
If you study the source code some more you can also learn about these things.

  1. How to create a ListView.
  2. How to add columns to a ListView.
  3. How to add rows (items and subitems) to a ListView.
  4. How to get the "Explorer" look on your ListView - see the SetWindowTheme() call in the ListViewSetStyle function.

As I've already said, I've included a fully functional Win32 (VS 2008) example, including a pre-compiled Win32 binary. I can't promise anything but the source should be easy to convert to Delphi or other similar languages.

How To Stop The User From Resizing a ListView - The Short Version

Here's the things you need to do to stop the user from resizing a ListView:

  1. Subclass the ListView and catch the WM_NOTIFY message. Get the NMHDR from the lParam, and if the NMHDR.code is HDN_BEGINTRACKW or HDN_BEGINTRACKA then return TRUE. This will stop the drag resizing.

  2. Subclass the ListView Header and catch the WM_LBUTTONDBLCLK message and return 0 (zero) on it. This will stop the double click resizing.

  3. Then in the same ListView Header subclass/WndProc, you need to catch the WM_SETCURSOR and return TRUE. This will remove the "resize mouse cursor" when hovering over the header.

That's it! Easy when you know what to do. :D

How To Stop The User From Resizing a ListView - The Long Version

Here I'll go through the important things step by step.

Before you do the following steps you need to create your main window and your ListView.

Subclass The ListView

This is how you subclass a ListView.

First you need to declare a pointer that is going to point at your ListViews original WndProc.

WNDPROC     g_wndProcOriginalListView;

Then you change the ListViews WndProc pointer to your own function (this is what SubClassing is all about when we are talking about Win32 Controls). The SetWindowLong function will return the pointer to the original WndProc, so we save that in the variable we just declared.

hwndListView is the HWND to your ListView.
WndProcListView is a function that we are going to create soon. :)

g_wndProcOriginalListView = (WNDPROC)SetWindowLong(hwndListView, GWLP_WNDPROC, (LONG_PTR)WndProcListView);

Create Your ListViews New WndProc

Now you need to add your new WndProc function (called WndProcListView in this example) for the subclassed ListView.

Then you use your new WndProc function to catch the HDN_BEGINTRACKW and HDN_BEGINTRACKA messages via the WM_NOTIFY message.

Example source:

LRESULT WndProcListView(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
   switch (msg)
   {
      case WM_NOTIFY:
      {
         // Catch the resize messages for the ListView Header and abort the resizing
         NMHDR *nmhdr = (NMHDR*)lParam;
         if (HDN_BEGINTRACKW == nmhdr->code || HDN_BEGINTRACKA == nmhdr->code)
         {
            return TRUE;
         }
      }
      break;
   }

   // Send all other messages to the ListViews original WndProc - THIS IS IMPORTANT
   return CallWindowProc(g_wndProcOriginalListView, hWnd, msg, wParam, lParam);
}

Now you've made it impossible to drag resize the columns in the ListView. You can still double click to fit resize + that the mouse cursor still changes to a resize cursor when it hovers over the header. So let's fix that too. :)

Subclass The ListView Header

First, you need to declare a pointer that is going to point at your ListViews Headers original WndProc.

WNDPROC     g_wndProcOriginalListViewHeader;

Second, get the ListView Headers HWND from the ListViews HWND.

HWND hwndListViewHeader = ListView_GetHeader(hwndListView);

Third, now subclass the ListViews Headers and save the original WndProc in the variable we declared above.

g_wndProcOriginalListViewHeader = (WNDPROC)SetWindowLong(hwndListViewHeader, GWLP_WNDPROC, (LONG_PTR)WndProcListViewHeader);

Create Your ListViews Headers WndProc

Now you need to add your new WndProc function (called WndProcListViewHeader in this example) for the subclassed ListView Header.

In this WndProc we are going to handle both the WM_SETCURSOR and WM_LBUTTONDBLCLK message. This will take care of both the fit resize and the mouse cursor.

Example source:

LRESULT WndProcListViewHeader(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
   switch(msg)
   {
      // Stop the header from changing to the "change column width mouse cursor"
      // Without this it will look like the user can resize, but he won't be able to do it
      case WM_SETCURSOR:
      {
         return TRUE;
      }

      // Stop the user from resizing by double clicking on the header
      case WM_LBUTTONDBLCLK:
      {
         return 0;
      }
   }

   // Send all other messages to the original WndProc - THIS IS IMPORTANT
   return CallWindowProc(g_wndProcOriginalListViewHeader, hWnd, msg, wParam, lParam);
}

And that's it, now you have effectively put an end to the users possibilities to change the column width in your ListView. :D

View the full source code here: ListViewNoColumnResize.cpp
Download the full source code and projects files here: ListViewNoColumnResize.zip