ListViewNoColumnResize.cpp

//----------------------------------------------------------------------------------------------------
// Created 2013-09-04 by Mikael Linusson (http://www.mikaellinusson.com/)
//----------------------------------------------------------------------------------------------------
// This is example source of how to disable resizing in a Win32 ListView control
// It disables both drag resize and double click on header resize
//----------------------------------------------------------------------------------------------------
// The accompanying tutorial can be found here:
// How To Disable Resizing in a ListView (Win32 / C++)
// http://www.mikaellinusson.com/tech/2013/09/04/how-to-disable-resizing-in-a-listview-win32-c/
//----------------------------------------------------------------------------------------------------
// How you are allowed to use this code:
//
// You are free to use this source, or parts of it, in your own code, whether commercial or not.
//
// You are NOT allowed to put this source code, or parts of it, up on your own web site and/or spread
// it like if you had written it. Instead, please, feel free to link to my tutorial (link above). :)
//----------------------------------------------------------------------------------------------------
// And remember:
//
// "Live - Learn - Laugh - Love" -Mikael Linusson :)
//
// Yes, I just quoted myself, sorry for that. :D
//----------------------------------------------------------------------------------------------------
// Have a Great Day! :)
//
// / Mikael Linusson
//----------------------------------------------------------------------------------------------------

#include <windows.h>

// Common Controls 6.x.x.x
#include <commctrl.h>
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

// Needed for the SetWindowTheme procedure
#include "Uxtheme.h"
#pragma comment(lib, "UxTheme.lib")

// Consts
const char* CLASS_NAME = "ListView No Column Resize - Mikael Linusson";
const int   LISTVIEW_CONTROL_ID = 100;

// Global vars, yihaaa ;)
HINSTANCE   g_hInstance;
HWND        g_hwnd;
HWND        g_hwndListView;
HWND        g_hwndListViewHeader;
WNDPROC     g_wndProcOriginalListView;
WNDPROC     g_wndProcOriginalListViewHeader;

// Function declarations
bool CreateWin(void);
void InitCC(void);
void ListViewAddColumns(HWND hwnd);
void ListViewAddItems(HWND hwnd);
void ListViewCreate(HWND hwndParent);
void ListViewSetStyle(HWND hwnd);
void ListViewSubClass(void);
void MainMessageLoop(void);
void RegClass(void);

// The main windows WndProc
LRESULT WndProcMain(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// The ListView subclassing WndProcs
LRESULT WndProcListView(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT WndProcListViewHeader(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

//----------------------------------------------------------------------------------------------------
// WinMain
//----------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
   g_hInstance = hInstance;
   InitCC();
   RegClass();
   if (CreateWin())
   {
      MainMessageLoop();
      UnregisterClass(CLASS_NAME, g_hInstance);
   }
   return 0;
}

//----------------------------------------------------------------------------------------------------
// CreateWin
//----------------------------------------------------------------------------------------------------
bool CreateWin(void)
{
   // In normal case you probably want to use WS_OVERLAPPEDWINDOW instead of WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
   // but as I didn't want the window to be resizeable I did it this way
   DWORD dwStyle = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
   DWORD dwStyleEx = NULL;
   g_hwnd = CreateWindowEx(dwStyleEx, CLASS_NAME, CLASS_NAME, dwStyle, 200, 100, 420, 320, NULL, NULL, g_hInstance, NULL);

   if (g_hwnd)
   {
      // Resize our window so that we get a client area with the specified size
      RECT rect;
      GetWindowRect(g_hwnd, &rect);
      AdjustWindowRectEx(&rect, dwStyle, GetMenu(g_hwnd) != NULL, dwStyleEx);
      SetWindowPos(g_hwnd, 0, -1, -1, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE);

      return true;
   }
   else
   {
      // Failed...
      MessageBox(NULL, "Couldn't create the main window!", "Failed!", MB_OK);
      return false;
   }
}

//----------------------------------------------------------------------------------------------------
// InitCC
//----------------------------------------------------------------------------------------------------
void InitCC(void)
{
   // Common controls is needed to create a ListView
   INITCOMMONCONTROLSEX icex;
   icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
   icex.dwICC = ICC_STANDARD_CLASSES;
   InitCommonControlsEx(&icex);
}

//----------------------------------------------------------------------------------------------------
// ListViewAddColumns
//----------------------------------------------------------------------------------------------------
void ListViewAddColumns(HWND hwnd)
{
   LVCOLUMN lvc;
   lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
   lvc.cx = 100;
   lvc.fmt = LVCFMT_LEFT;

   // You can use SendMessage with the LVM_INSERTCOLUMN message instead of ListView_InsertColumn, if you want to. :)
   // SendMessage(hwnd, LVM_INSERTCOLUMN, lvc.iSubItem, (LPARAM)&lvc);

   lvc.iSubItem = 0;
   lvc.pszText = "Column 1";
   ListView_InsertColumn(hwnd, lvc.iSubItem, &lvc);

   lvc.iSubItem = 1;
   lvc.pszText = "Column 2";
   ListView_InsertColumn(hwnd, lvc.iSubItem, &lvc);

   lvc.iSubItem = 2;
   lvc.pszText = "Column 3";
   ListView_InsertColumn(hwnd, lvc.iSubItem, &lvc);
} 

//----------------------------------------------------------------------------------------------------
// ListViewAddItems
//----------------------------------------------------------------------------------------------------
void ListViewAddItems(HWND hwnd)
{
   LVITEM lvItem;
   lvItem.mask = LVIF_TEXT;

   // You can use SendMessage with the LVM_INSERTITEM message instead of ListView_InsertItem, if you want to. :)
   // SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM)&lvItem);

   // First Row
   lvItem.iItem = 0;
   lvItem.pszText = "Row 1";
   lvItem.iSubItem = 0;
   ListView_InsertItem(hwnd, &lvItem);

   lvItem.pszText = "Sub Item 1";
   lvItem.iSubItem = 1;
   ListView_SetItem(hwnd, &lvItem);

   lvItem.pszText = "Sub Item 2";
   lvItem.iSubItem = 2;
   ListView_SetItem(hwnd, &lvItem);

   // And you can use SendMessage with the LVM_SETITEM message instead of ListView_SetItem, if you want to. :)
   // SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM)&lvItem);

   // Second Row
   lvItem.iItem = 1;
   lvItem.pszText = "Row 2";
   lvItem.iSubItem = 0;
   ListView_InsertItem(hwnd, &lvItem);

   lvItem.pszText = "Sub Item 1";
   lvItem.iSubItem = 1;
   ListView_SetItem(hwnd, &lvItem);

   lvItem.pszText = "Sub Item 2";
   lvItem.iSubItem = 2;
   ListView_SetItem(hwnd, &lvItem);
} 

//----------------------------------------------------------------------------------------------------
// ListViewCreate
//----------------------------------------------------------------------------------------------------
void ListViewCreate(HWND hwndParent)
{
   // InitCC must have been called before this function gets called

   // Create the ListView (report style, edit enabled and always show selection)
   DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | LVS_REPORT | LVS_EDITLABELS | LVS_SHOWSELALWAYS;
   DWORD dwStyleEx = WS_EX_CLIENTEDGE;
   g_hwndListView = CreateWindowEx(dwStyleEx, WC_LISTVIEW, "", dwStyle, 10, 10, 400, 300, hwndParent, (HMENU)LISTVIEW_CONTROL_ID, g_hInstance, NULL);

   // Set some optional ListView settings
   ListViewSetStyle(g_hwndListView);

   // Add columns and data to the ListView
   ListViewAddColumns(g_hwndListView);
   ListViewAddItems(g_hwndListView);

   // This is important - we must subclass to stop the resizing
   ListViewSubClass();
}

//----------------------------------------------------------------------------------------------------
// ListViewSetStyle
//----------------------------------------------------------------------------------------------------
void ListViewSetStyle(HWND hwnd)
{
   // Use full row select
   ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT);
   // If you want checkboxes or gridlines too, then use this row instead
   // ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES | LVS_EX_GRIDLINES);

   // This makes it look like the ListView in (Windows) Explorer - use it if you want that look
   // But don't forget to add the "Uxtheme" stuff from the top of this file too, as it's needed for this call.
   SetWindowTheme(hwnd, L"Explorer", NULL);
}

//----------------------------------------------------------------------------------------------------
// ListViewSubClass
//----------------------------------------------------------------------------------------------------
// Disable ListView Resize by subclassing both the ListView and the ListView Header
//----------------------------------------------------------------------------------------------------
void ListViewSubClass(void)
{
   // First subclass the ListView
   g_wndProcOriginalListView = (WNDPROC)SetWindowLong(g_hwndListView, GWLP_WNDPROC, (LONG_PTR)WndProcListView);

   // Then subclass the ListView Header (you can send LVM_GETHEADER message with SendMessage instad of ListView_GetHeader if you want to)
   g_hwndListViewHeader = ListView_GetHeader(g_hwndListView);
   g_wndProcOriginalListViewHeader = (WNDPROC)SetWindowLong(g_hwndListViewHeader, GWLP_WNDPROC, (LONG_PTR)WndProcListViewHeader);
}

//----------------------------------------------------------------------------------------------------
// MainMessageLoop
//----------------------------------------------------------------------------------------------------
void MainMessageLoop(void)
{
   MSG   msg;
   memset(&msg, NULL, sizeof(msg));
   while (msg.message != WM_QUIT)
   {
      while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
      {
         if (!IsDialogMessage(g_hwnd, &msg))
         {
            DispatchMessage(&msg);
         }
      }
      if (msg.message != WM_QUIT) WaitMessage();
   }
}

//----------------------------------------------------------------------------------------------------
// RegClass
//----------------------------------------------------------------------------------------------------
void RegClass(void)
{
   WNDCLASS wc;
   wc.style                = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc          = (WNDPROC)WndProcMain;
   wc.cbClsExtra           = 0;
   wc.cbWndExtra           = 0;
   wc.hInstance            = GetModuleHandle(NULL);
   wc.hIcon                = NULL;
   wc.hCursor              = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground        = GetSysColorBrush(COLOR_BTNFACE);
   wc.lpszMenuName         = NULL;
   wc.lpszClassName        = CLASS_NAME;
   RegisterClass(&wc);
}

//----------------------------------------------------------------------------------------------------
// WndProcListView
//----------------------------------------------------------------------------------------------------
// ListView Subclass WndProc
//----------------------------------------------------------------------------------------------------
LRESULT WndProcListView(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
   switch (msg)
   {
      // WM_NOTIFY
      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
   return CallWindowProc(g_wndProcOriginalListView, hWnd, msg, wParam, lParam);
}

//----------------------------------------------------------------------------------------------------
// WndProcListViewHeader
//----------------------------------------------------------------------------------------------------
// ListView Header Subclass WndProc
//----------------------------------------------------------------------------------------------------
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 ListView Headers original WndProc
   return CallWindowProc(g_wndProcOriginalListViewHeader, hWnd, msg, wParam, lParam);
}

//----------------------------------------------------------------------------------------------------
// WndProcMain
//----------------------------------------------------------------------------------------------------
// This is the main windows WndProc, I kept it as clean as possible. :)
//----------------------------------------------------------------------------------------------------
LRESULT WndProcMain(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
   switch(msg)
   {
      // WM_CLOSE
      case WM_CLOSE:
         DestroyWindow(g_hwnd);
         return 0;

      // WM_CREATE
      case WM_CREATE:
         ListViewCreate(hWnd);
         break;

      // WM_DESTROY
      case WM_DESTROY:
         PostQuitMessage(0);
         return 0;
   }

   return DefWindowProc(hWnd, msg, wParam, lParam);
}