Prevent column resizing (2)

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.


This article was contributed by Charles Herman.

Zafir’s article Prevent column resizing describes a method to prevent resizing of columns. However the method does not prevent the cursor from changing its shape. Furthermore the columns can still be resized if the user double clicks on a column separator.

In this article I persent a method for overcoming these drawbacks. First, this method is presented for the case all columns are to be non-resizable, then it is assumed you want some, but not all, columns to be resizable.

BTW, Since my method does not prevent resizing (except when double clicking), but only prevents the resizing cursor from appearing, you still need to use the method described in “Prevent column resizing” to have it behave properly.

The method presented here requires the creation of a class, CMyHeader, based on the class CHeaderCtrl. The header from the list ctrl is connected to CMyHeader via the subclassing mechanism. CMyHeader will intercept the header messages and disable things we want disabled.

In the header file for CMyListCtrl define the member variable

           CMyHeader m_Header;

We do the subclassing in the OnCreate() function.

int CMyListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CListCtrl::OnCreate(lpCreateStruct) == -1)
		return -1;

	// Since I want to capture all messages passed to the header,
	// I am subclassing the header.  Note: 0 is the ID for the header
	m_Header.SubclassDlgItem( 0, this );

	return 0;
}

Next, using AppWizard create the class CMyHeader. Again using AppWizard, create handlers for the WM_SETCURSOR and WM_LBUTTONDBLCLK messages inside CMyHeader.cpp.

// MyHeader.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CMyHeader window
#ifndef __MYHEADER_H__
#define __MYHEADER_H__

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

// Attributes
public:

// Operations
public:

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

// Implementation
public:
	virtual ~CMyHeader();

	// Generated message map functions
protected:
	//{{AFX_MSG(CMyHeader)
	afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
	afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
	//}}AFX_MSG

	DECLARE_MESSAGE_MAP()
};
#endif
/////////////////////////////////////////////////////////////////////////////



// MyHeader.cpp : implementation file
//

#include "stdafx.h"
#include "MyHeader.h"

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

/////////////////////////////////////////////////////////////////////////////
// CMyHeader

CMyHeader::CMyHeader()
{
}

CMyHeader::~CMyHeader()
{
}


BEGIN_MESSAGE_MAP(CMyHeader, CHeaderCtrl)
	//{{AFX_MSG_MAP(CMyHeader)
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONDBLCLK()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMyHeader message handlers


////////////////////////////////////////////////////////////////////
// Disabling this message handler prevents the cursor from 
// changing when the cursor is over the column separator line
// in the header
//
BOOL CMyHeader::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	return TRUE;
//	return CHeaderCtrl::OnSetCursor(pWnd, nHitTest, message);
}


/////////////////////////////////////////////////////////////////
// Disabling this message handler prevents the column from resizing 
// when a double click is done on the column separator line.
//
void CMyHeader::OnLButtonDblClk(UINT nFlags, CPoint point)
{
//	CHeaderCtrl::OnLButtonDblClk(nFlags, point);
}

Next we assume there are some columns which we want to allow to be re-sized.

Inside the CMyHeader class definition (in the file MyHeader.h) enter the following declarations:

// Attributes
protected:
	BOOL IsDragAllowed( CPoint point );
	BOOL m_bAllowDrag;

Using AppWizard, create a handler for the WM_NCHITTEST messages inside CMyHeader.cpp.

// MyHeader.cpp : implementation file
//

#include "stdafx.h"
#include "MyHeader.h"

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

/////////////////////////////////////////////////////////////////////////////
// CMyHeader

CMyHeader::CMyHeader()
{
}

CMyHeader::~CMyHeader()
{
}


BEGIN_MESSAGE_MAP(CMyHeader, CHeaderCtrl)
	//{{AFX_MSG_MAP(CMyHeader)
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_NCHITTEST()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


////////////////////////////////////////////////////////////////////
// Disabling this message callback prevents the cursor from 
// changing when the cursor is over the column separator line
// in the header. We enable this callback only if the cursor lies
// on a separator for which resizing has been allowed.
//
BOOL CMyHeader::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	if (m_bAllowDrag)
		return CHeaderCtrl::OnSetCursor(pWnd, nHitTest, message);
	else
		return TRUE;

}



/////////////////////////////////////////////////////////////////
// Disabling this message callback prevents the feature that headers
// have of resizing a column when a double click is done on the column
// separator line.  We enable this callback only if the cursor lies
// on a separator for which resizing has been allowed.
//
void CMyHeader::OnLButtonDblClk(UINT nFlags, CPoint point)
{
	if (IsDragAllowed( point ))
		CHeaderCtrl::OnLButtonDblClk(nFlags, point);
}



///////////////////////////////////////////////////////////////
//	Everytime there is a cursor movement whithin the header, this
//	function is invoked.  It has the feature that it is invoked 
//	before OnSetCursor(),  which means we can decide in advance
//	whether we want the tracking cursor to appear.  (An aside:
//	OnMouseMove() will also track mouse movement, but it is
//	invoked after OnSetCursor())
//
UINT CMyHeader::OnNcHitTest(CPoint point)
{
	// This 'point' is in screen coordinates.  We need to
	// transform it to client coords before we test which
	// column it is in.
	POINT clientPoint = point;
	ScreenToClient( &clientPoint );

	m_bAllowDrag = IsDragAllowed( clientPoint );
	return CHeaderCtrl::OnNcHitTest(point);
}


The following function is where we select the columns for which we want to disable dragging.  It is assumed we have 5 columns, and we want to disable dragging for columns 0, 2, and 3.

/////////////////////////////////////////////////////////////
//	This is the function which determines which column 
//	the cursor is in, and we decide whether we want to
//	allow resizing of that column.
//
BOOL CMyHeader::IsDragAllowed( CPoint point )
{
	// We will extract information about the header 
	// using this structure
	HD_ITEM hi;
	hi.mask = HDI_WIDTH;	// We want the column width.

	// We keep a running sum of the horizontal location
	// of each column's divider.
	int dividerLocations = 0;

	// The amount of space around the dividor inside of which one 
	// can begin the dragging operation is equal to the width of 
	// the cursor, centered at the dividor.  So we need to trap 
	// the cursor a distance of half the cursor width to each 
	// side of the dividor.
	int dragWidth = GetSystemMetrics( SM_CXCURSOR );

	// Since we have no need to apply this test for columns for which 
	// we want to enable dragging, we do not need to go beyond the last 
	// column for which we want to disable dragging in our 'for loop'.
	BOOL allowDrag = TRUE;
	for (int i = 0; i < 4; ++i) {
		GetItem(i, &hi);

		// hi.cxy contains the width of the i'th column.
		dividerLocations += hi.cxy;

		// Here is where we place the indexes for the columns 
		// for which we want to disable dragging.
		if (i == 0 ||
		    i == 2 ||
		    i == 3)
		      if (point.x > dividerLocations - dragWidth/2 &&
			  point.x < dividerLocations + dragWidth/2)
				allowDrag = FALSE;
		}

	return allowDrag;
}

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read