Click here to Skip to main content
12,825,921 members (34,798 online)
Click here to Skip to main content
Add your own
alternative version


18 bookmarked
Posted 3 Sep 2013

Resize columns to avoid horizontal scroll

, 3 Sep 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
Avoid horizontal scroll and effectivelly use whole horizontal control width: resize all columns except one to their content and this one to all remaining space.


There are number of articles on CodeProject related to avoiding inappropriate horisontal scrolling in list controls ([1], [2]). This article provides one more way to do this. This way hovers situation when you have one column needed to resize to all available space and (optionally) other columns which have known small width and need to be sized to content. Implementation prevents horisontal scroll appearing or flickering and properly reacts on vertical scrolling appearing.

See at screenshot below, even if text appears partially hidden - in this case it is smaller problem than invisibility of last column in case of horisontal scroll use (especially if LVS_EX_LABELTIP list style used). Example source code contains few other appropriate usage ways.

Using the code

Add CListColumnAutoSize member to your window class implementation:

class CMainDlg: public CDialogImpl<CMainDlg> {

  CListColumnAutoSize columns_resize_;

Subclass your list control at window initialization, for example in WM_INITDIALOG handler:

BOOL CMainDlg::OnInitDialog(CWindow wndFocus, LPARAM lInitParam) {

  // Optionally set index of column to resize. By default it is first column
  return TRUE;

Well, in most cases that is all what need to do. Class resizes columns automatically. Automatic update can be turned off when it need. This is usefull for example when you add/remove/change big number of items at once, in this case auto updating can give huge overhead so better turn it off before batch operation and turn on after it. Functions which help to do this:

// Turn columns width automatic update on / off
void EnableAutoUpdate(bool enable);

// Returns true if automatic update currently is on
bool IsAutoUpdateEnabled() const;

// Manually update columns width if auto updating does not suit you or
// does not cover all cases when it should be performed
void UpdateColumnsWidth();

Source code containt one more class CListColumnAutoSizeEx which implements same columns resizing mechanism (i.e. resize to available space) but for several columns instead one. One usage difference here - at setting variable width column need also set percentage of available space which it should use. Example:

CListColumnAutoSizeEx list;
list.AddVariableWidthColumn(1, 0.4);
list.AddVariableWidthColumn(2, 0.6);
// Now column #1 resizes to 40% of free space, column #2 to 60%, column #0 and
// others - to content

Now we'll see how it works.


Implementation consist of two parts: preventing header resize by the user and actual columns resizing in response to list content change or list control resize.

Prevent header resize 

Header can be resized by several ways. First is press Ctrl and + key. This causes all list columns resize to their content (ignoring header text width). Prevents by filtering appropriate WM_KEYDOWN message:

void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
  // If CTRL + Add was pressed then message sets as handled and does not pass to control's
  // DefWindowProc() function
  SetMsgHandled(VK_ADD == nChar && 0 != ::GetKeyState(VK_CONTROL));

Note that implementation uses 'cracked' message map defined in WTL header <atlcrack.h>

Other ways to resize header is drag header's divider or double click on it (which causes resizing to content for column at left of clicked divider, also ignoring text width of column header). This prevented by filtering HDN_BEGINTRACK and HDN_DIVIDERDBLCLICK notifications from header control:

  // Header sends notifications to its parent which is our class

But there is a moment. Since Vista header control have style HDS_NOSIZING which does exactly what we need. Better to use native features when it is possible so notification filtering implemented by this way:

LRESULT OnHeaderBeginTrack(LPNMHDR pnmh) {
  // For Vista and above message stays unhandled, return value in this case ignored
  return TRUE; // prevent tracking
LRESULT OnHeaderDividerDblclick(LPNMHDR pnmh) {
  return 0; // prevent reaction (header resizing to content)

For Vista and above also need ensure that header have HDS_NOSIZING style. This done in PostInit() function which calls after windows subclassing or creation:

void PostInit() {
  if (WTL::RunTimeHelper::IsVista()) {
    GetHeader().ModifyStyle(0, HDS_NOSIZING);

The last thing need to do here is prevent cursor changing when it is over divider. For Vista and above this is already done by HDS_NOSIZING style. For XP need manually handle WM_SETCURSOR message sended to header control. To handle header control messages in list control implementation used CContainedWindow class, more info about which can be found this great article: [3]. So, to catch mouse messages sended to header control we do next things:

// At first add alternative message map with WM_SETCURSOR handler
ALT_MSG_MAP(T::kHeaderMsgMapId) // header control message map
// Next add CContainedWindow variable for header, we use its specialized version to be
// able to call header control functions without any type casts
ATL::CContainedWindowT<WTL::CHeaderCtrl> header_;
// CContainedWindow needs CMessageMap-based class where to pass messages (first arg) and
// map id in this message map (second arg)
CListColumnAutoSizeImplBase(): header_(this, T::kHeaderMsgMapId), ...
// Subclass header control in class initialization function
void PostInit() {
  if (WTL::RunTimeHelper::IsVista()) {
    GetHeader().ModifyStyle(0, HDS_NOSIZING);
  else {
// And finally process cursor message
BOOL OnHeaderSetCursor(ATL::CWindow wnd, UINT nHitTest, UINT message) {
  return TRUE; // prevent cursor change over dividers

Columns resizing

Columns width should be updated when control size changed and when list content changed. First done by processing WM_SIZE message:

LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam) {
  T* pT = static_cast<T*>(this);
  if (pT->IsAutoUpdate() && SIZE_MINIMIZED != wParam) {
    // Need update only columns with variable width
  return 0;

Updating column width on content change have 'lazy' implementation - update goes after any message which may change content. Class does not track actual change because this looks too error-prone, especially for highly customized list controls. So for any message which may change content (LVM_INSERTITEM, LVM_SETITEMTEXTA etc) there emits next function:

LRESULT OnItemChange(UINT uMsg, WPARAM wParam, LPARAM lParam) {
  // Apply this action
  LRESULT lr = DefWindowProc(uMsg, wParam, lParam);
  T* pT = static_cast<T*>(this);
  // If auto update turned on
  if (pT->IsAutoUpdate()) {
    // Update widths for all columns
  return lr;

For list with small numbers of items there is no reasons to do anything more specific and optimal. For lists with thousands elements maybe need something more specific. In this case there are two ways. First is set auto update off by calling EnableAutoUpdate(false) and manually update columns at appropriate time using UpdateColumnsWidth() function. Second is implement own class delivered from CListColumnAutoSizeImplBase and override UpdateColumnsWidth() function there.

Updating of columns with fixed width done using header control ability resize column to content, but with small hack:

void UpdateFixedWidthColumns() {
  // The easiest way to not screw it up is left resizing to the system. But in
  // case of LVSCW_AUTOSIZE_USEHEADER it resizes last column to all remaining
  // space. Workaround - made column not last by adding fake column to the end
  int count = GetHeader().GetItemCount();
  ATLVERIFY(count == InsertColumn(count, _T("")));
  T* pT = static_cast<T*>(this);
  // Loop for all columns except added
  for (int i = 0; i < count; i ++) {
    if (!pT->IsVariableWidthColumn(i)) {
      // Column here definitely not last so it will not resize content to remaining space
      SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);

Instead of custom width calculation this approach should definitely work in any cases. To use different algorithm can be implemented child class of CListColumnAutoSizeImplBase with overriden UpdateColumnsWidth() function.

And finally about updating variable width column:

void UpdateVariableWidthColumns() {
  // Get full available width
  RECT rect = {0};
  // Substract from it widhts of fixed columns
  T* pT = static_cast<T*>(this);
  int count = GetHeader().GetItemCount();
  for (int i = 0; i < count; i ++) {
    if (!pT->IsVariableWidthColumn(i)) {
      rect.right -= GetColumnWidth(i);
  // And apply remaining width to our variable width column
  SetColumnWidth(variable_width_column_, rect.right - rect.left);


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Nikolay Ananenko
Software Developer
Belarus Belarus
No Biography provided

You may also be interested in...

Comments and Discussions

QuestionWRONG! Pin
YDean01-May-16 23:19
memberYDean01-May-16 23:19 
SuggestionOn Demand Resize Pin
DaveAuld23-May-14 23:42
protectorDaveAuld23-May-14 23:42 
GeneralMy vote of 5 Pin
Mihai MOGA18-Apr-14 22:30
professionalMihai MOGA18-Apr-14 22:30 
SuggestionVery nice! Pin
Pablo Aliskevicius1-Dec-13 4:33
memberPablo Aliskevicius1-Dec-13 4:33 
GeneralRe: Very nice! Pin
Nikolay Ananenko1-Dec-13 6:15
memberNikolay Ananenko1-Dec-13 6:15 
QuestionNice idea! Pin
T800G9-Sep-13 6:45
memberT800G9-Sep-13 6:45 
GeneralMy vote of 5 Pin
Volynsky Alex4-Sep-13 3:04
memberVolynsky Alex4-Sep-13 3:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170308.1 | Last Updated 3 Sep 2013
Article Copyright 2013 by Nikolay Ananenko
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid