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);
}