Autosize ListCtrl Header
Introduction
Did you ever wonder how to make a list control automatically resize the last column to fit the entire list control without a horizontal scrollbar. Did you ever want to make it do that as you resize your list control.
Did you actually get it done but ended up with something like this?
There is hope, keep on reading.
Background
A while back, I was answering a question on a forum, the person asking the question had created a list control that would resize the last column as he resized his view. But he ran into a problem. When the first item in the list control was not the first visible item and the user resized the view to where a vertical scrollbar was no longer needed, the list control would end up with an empty entry at the top. Of course there was no item there, but it looked like a blank item. He said that a version of Windows Media player had the same problem, but I can't remember the version number. Anyway I didn't believe him until I tried it for myself, and saw the bug first hand.
Let's start writing some code for the resizing, find the problem along the way and fix it.
Using the code
The first thing we need to do is to create a dialog base application. Delete everything on the dialog box. Add a list control to it, make it report type, give it an ID, and attach a variable to it, let's call it m_cList
. Don't worry too much about its size or position; we will take care of that in the code. Don't forget to change the dialog's border style to Resizing while you are in the resource editor. Also in order to reduce flickering set the WS_CLIPCHILDREN
flag for the dialog box.
Once the above steps are done, we can start coding. Let's add some columns and items to the list control. We are going to do that in OnInitDialog
of the main dialog box.
BOOL CListControlIssueDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
m_cList.InsertColumn(0, _T("Column1"),LVCFMT_LEFT,100);
m_cList.InsertColumn(1, _T("Column2"),LVCFMT_LEFT,100);
m_cList.InsertColumn(2, _T("Column3"),LVCFMT_LEFT,100);
for ( int i = 0 ; i < 10 ; i++ )
{
for ( int j = 0 ; j < 3 ; j++ )
{
CString s;
s.Format(_T("Item %d (%d)") , i, j);
if ( j == 0 )
{
m_cList.InsertItem(i, s);
}
else
{
m_cList.SetItemText(i, j, s);
}
}
}
//also lets move it to cover most of the dialog
CRect Rect;
GetClientRect(&Rect);
m_cList.MoveWindow( 5, 5, Rect.Width()-10, Rect.Height()-10);
return TRUE; // return TRUE unless you set the focus to a control
}
Now let's also add a handler for the WM_SIZE
message so that we can resize the list control to fit the dialog as the dialog is being resized. This should look something like this:
{
CDialog::OnSize(nType, cx, cy);
//make sure m_cList has already been
//attached to the list control
if ( IsWindow( m_cList.m_hWnd ) )
{
m_cList.MoveWindow( 5, 5, cx-10, cy-10);
}
}
Now when you run the program you should see a list control with 10 items, 3 columns at 100 pixels wide, which gets resized as you resize the dialog. Easy so far.
Now let's add the code to automatically change the size of the column. For that we need to create a class that inherits from CListCtrl
, let call it CMyListCtrl
. After that's done let go ahead and catch the WM_SIZE
message for the List control. Also add a private method named AutoAdjustColumns
which looks like this:
(I have to thank Chris Radke for this code.)
{
SetColumnWidth(GetHeaderCtrl()->GetItemCount()-1, LVSCW_AUTOSIZE_USEHEADER);
}
The code above is pretty straight forward. It simply loops through all the columns (except the last one) and subtract their width from the width of the control. And sets the last column's width to the remaining value. (Note: In order to keep the article from getting too complicated this code does not handle minimum column size. However this situation should be handled).
Now in order to resize the columns as the list control is being resized, you would naturally want to call AutoSizeColumn
from the OnSize
handler of the list control. Go ahead and do that.
{
CListCtrl::OnSize(nType, cx, cy);
AutoSizeColumn();
}
Franc Morales suggested that the columns should autoresize after the user drags the column headers. In order to do that we need to handle the HDN_ENDTRACK
message and call AutoSizeColumn
.
void CMyListCtrl::OnHdnEndtrack(NMHDR *pNMHDR, LRESULT *pResult)
{
AutoSizeColumn();
*pResult = 1;
}
Don't forget to change your m_cList
type from CListCtrl
to CMyListCtrl
. Then run the program, scroll the list control to the bottom, and then resize the dialog to a point where a vertical scrollbar is no longer needed. You will probably see this:
Well the fix for that in my opinion is pretty interesting. If the call to AutoSizeColumn
is made directly from OnSize
then this is the result, but if you were to call it after OnSize
had returned everything works fine. So to do this we are going to change OnSize
's implementation to use PostMessage
to post a message to itself and the message handler for that message is going to call AutoSizeColumn
. So let's change things to make them look like this:
BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
ON_WM_SIZE()
ON_MESSAGE(WM_RESIZEME,OnResizeMe)
END_MESSAGE_MAP()
void CMyListCtrl::OnSize(UINT nType, int cx, int cy)
{
CListCtrl::OnSize(nType, cx, cy);
PostMessage(WM_RESIZEME);
}
LRESULT CMyListCtrl::OnResizeMe(WPARAM,LPARAM)
{
AutoSizeColumn();
return 1;
}
Now run the program and resize the dialog in the same way you did before. Things should be fine now.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)