Autofit Header Control

The Header Common Control has a lot of functionality but can still be improved quite a bit. One thing I've always missed is to be able to automatically resize it's columns so that they occupy the entire width of the control. The following class inherits from CHeaderCtrl and adds exactly this feature to the Header Control. All you have to do is to insert the columns you want and then call the SetAutofit method. From there on all columns are resized proportionally to their existing size when the size of the control changes or when the user drags a column header divider.

There is still lot of room for improvements. If you find this class useful and make any modifications to it I would appreciate if you'd send me a note with the changes so that we can continue to improve this document. It's much better than posting an alternate version all together. I'll be glad to merge any such improvements into the basic idea.

For information on how to add this header control class to your listview for example, refer to the article "The Header Control" under the ListView category.

HeaderAutofit.h

#if !defined(AFX_HEADERAUTOFIT_H__B2BEAB04_6276_11D1_875B_00A0C9181E86__INCLUDED_)
#define AFX_HEADERAUTOFIT_H__B2BEAB04_6276_11D1_875B_00A0C9181E86__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// HeaderAutofit.h : header file
// ) 1997 Baldvin Hansson [baldvin@trafficsoftware.com]

/////////////////////////////////////////////////////////////////////////////
// CHeaderAutofit window

class CHeaderAutofit : public CHeaderCtrl
{
// Construction
public:
	CHeaderAutofit();

// Attributes
private:
	bool m_bAutofit;
public:

// Operations
private:
	void Autofit(int nOverrideItemData = -1, int nOverrideWidth = 0);
public:
	void SetAutofit(bool bAutofit = true) { m_bAutofit = bAutofit; Autofit(); }


// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CHeaderAutofit)
	//}}AFX_VIRTUAL

// Implementation
public:
	virtual ~CHeaderAutofit();

	// Generated message map functions
protected:
	//{{AFX_MSG(CHeaderAutofit)
	afx_msg void OnEndTrack(NMHDR * pNotifyStruct, LRESULT* result);
	afx_msg void OnSize(UINT nType, int cx, int cy);
	//}}AFX_MSG

	DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_HEADERAUTOFIT_H__B2BEAB04_6276_11D1_875B_00A0C9181E86__INCLUDED_)

HeaderAutofit.cpp
// HeaderAutofit.cpp : implementation file
//
// ) 1997 Baldvin Hansson [baldvin@trafficsoftware.com]

#include 
#include 
#include "HeaderAutofit.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CHeaderAutofit

CHeaderAutofit::CHeaderAutofit()
{
	m_bAutofit = false;
}

CHeaderAutofit::~CHeaderAutofit()
{
}


BEGIN_MESSAGE_MAP(CHeaderAutofit, CHeaderCtrl)
	//{{AFX_MSG_MAP(CHeaderAutofit)
	ON_NOTIFY_REFLECT(HDN_ENDTRACK, OnEndTrack)
	ON_WM_SIZE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CHeaderAutofit message handlers

void CHeaderAutofit::Autofit(int nOverrideItemData /*= -1*/, int nOverrideWidth /*= 0*/)
{
	int nItemCount = GetItemCount();
	int nTotalWidthOfColumns = 0;
	int nDifferenceInWidht;
	int nItem;
	HD_ITEM hi;
	CRect rClient;

	if (!m_bAutofit)
		return;

	SetRedraw(FALSE);

	GetClientRect(&rClient);
	if (-1 != nOverrideItemData)
		rClient.right -= nOverrideWidth;

	// Get total width of all columns
	for (nItem = 0; nItem < nItemCount; nItem++)
	{
		if (nItem == nOverrideItemData)	// Don't mess with the item being resized by the user
			continue;

		hi.mask = HDI_WIDTH;
		GetItem(nItem, &hi);

		nTotalWidthOfColumns += hi.cxy;
	}

	if (nTotalWidthOfColumns != rClient.Width())
	{
		nDifferenceInWidht = abs(nTotalWidthOfColumns-rClient.Width());	// We need to shrink/expand all columns!
		
		// Shrink/expand all columns proportionally based on their current size
		for (nItem = 0; nItem < nItemCount; nItem++)
		{
			if (nItem == nOverrideItemData)	// Skip the overrride column if there is one!
				continue;
			
			hi.mask = HDI_WIDTH;
			GetItem(nItem, &hi);

			hi.mask = HDI_WIDTH;
			hi.cxy = (hi.cxy * rClient.Width()) / nTotalWidthOfColumns;

			SetItem(nItem, &hi);
		}
	}

	SetRedraw(TRUE);
	Invalidate();
}

void CHeaderAutofit::OnEndTrack(NMHDR * pNotifyStruct, LRESULT* result)
{
	HD_NOTIFY* pHDN = (HD_NOTIFY*)pNotifyStruct;

	Autofit(pHDN->iItem, pHDN->pitem->cxy);
}

void CHeaderAutofit::OnSize(UINT nType, int cx, int cy) 
{
	CHeaderCtrl::OnSize(nType, cx, cy);

	Autofit();
}



Comments

  • Better Solution

    Posted by Legacy on 09/06/2001 12:00am

    Originally posted by: Kyle Sluder

    I have an even better method that requires no subclassing at all.  Override your view's OnActivateView virtual member function, and add the following code:
    
    

    void CMyView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
    {
    CListView::OnActivateView(bActivate, pActivateView, pDeactiveView);

    if(pActivateView == this)
    {
    GetListCtrl().SetRedraw(FALSE);

    GetListCtrl().SetColumnWidth(0, LVSCW_AUTOSIZE);
    GetListCtrl().SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER);

    GetListCtrl().SetColumnWidth(1, LVSCW_AUTOSIZE);
    GetListCtrl().SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER);

    // TODO: Do the same thing for every column,
    // replacing the first parameter with the column index.

    GetListCtrl().SetRedraw(TRUE);
    GetListCtrl().RedrawWindow();
    }
    }

    Of course, this only occurs when the view's window is activated, but you can encapsulate this code in a function and call it from where you need it. The SetRedraw() and RedrawWindow() calls are there to prevent the noticable resize from being drawn to the screen; in the rare case you are doing something like an animation in an item in the control, it will be paused for a half a second.

    Reply
  • Autofit Header Control

    Posted by Legacy on 03/21/2001 12:00am

    Originally posted by: Mark Elrod

    I also want to do something like this but both your implementation and mine have problems.

    1) resizing the parent window causes the listview to display it horizontal scrollbar in a flickering fashion.

    2) your version does not resize header in realtime but waits until the EndTrack. mine does it in real time but it runs in to scrollbar problem above.

    3) sometimes the proportions can get messed up and resizing causes one column to get progressively bigger and the other to get smaller. this can be avoided by not recalculating the proportions each resize but keeping them around in a member array/vector (choose your poison).

    from messing around with the for a couple hours i feel the only way to do this might be to rewrite the entire lsitview control like netscape did in their mail program. otherwise it is always going to have some problems.

    elrod

    Reply
  • Column Headers Occupy entire CListView width

    Posted by Legacy on 10/03/2000 12:00am

    Originally posted by: Jk

    If the idea is to Resize the Column Headers to fit the entire width of the CListView, the following code in the derived ListView class will do. We do not need to have a custom CMyHeaderCtrl derived from CHeaderCtrl
    
    

    void CMyListView::OnSize(UINT nType, int cx, int cy)
    {
    CMyListView::OnSize(nType, cx, cy);
    CListCtrl& lc = GetListCtrl();

    CHeaderCtrl *hc = lc.GetHeaderCtrl();

    int lastHeader = hc->GetItemCount() - 1;

    //calculate width occupied by other headers
    int width = 0;
    for(int i = 0; i < lastHeader; i++)
    {
    width += lc.GetColumnWidth(i);
    }

    int lastHeaderWidth = cx - width;
    //now, expand the last column to fit the entire List
    lc.SetColumnWidth(lastHeader, lastHeaderWidth);
    }

    regards
    Jk/..

    Reply
  • Simple ListCtrl Header AutoSize

    Posted by Legacy on 02/25/1999 12:00am

    Originally posted by: Larry Yates

    Hi!
    
    

    Using VC++ 5, I have had successful results using
    something like this:

    m_ListItem.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER );

    FYI,
    -Larry

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Hundreds of millions of users have adopted public cloud storage solutions to satisfy their Private Online File Sharing and Collaboration (OFS) needs. With new headlines on cloud privacy issues appearing almost daily, the need to explore private alternatives has never been stronger. Join ESG Senior Analyst Terri McClure and Connected Data in this on-demand webinar to take a look at the business drivers behind OFS adoption, how organizations can benefit from on-premise deployments, and emerging private OFS …

Most Popular Programming Stories

More for Developers

RSS Feeds