在开发MFC应用程序时,经常需要处理列表控件,以展示数据。有时,希望列表控件的列宽能够随着窗口大小的变化而动态调整,以提高用户体验。本文将介绍如何实现这一功能。
在之前的一个论坛讨论中,有人提出了一个问题:他创建了一个列表控件,该控件能够在窗口大小变化时调整最后一列的宽度。但是,他遇到了一个问题:当列表控件的第一个项目不是第一个可见项目,并且用户调整窗口大小使得不再需要垂直滚动条时,列表控件顶部会出现一个空的条目。这看起来像是一个空白项,但实际上并没有项目。他提到Windows Media Player的一个版本也有同样的问题,但不记得具体是哪个版本了。起初不相信,直到亲自尝试并亲眼看到了这个bug。
首先,需要创建一个基于对话框的应用程序。删除对话框上的所有内容,添加一个列表控件,将其设置为报告类型,并为其分配一个ID,称之为m_cList。不用担心它的位置和大小,将在代码中处理。别忘了在资源编辑器中将对话框的边框样式更改为可调整大小。同时,为了减少闪烁,为对话框设置WS_CLIPCHILDREN标志。
在OnInitDialog函数中,将添加一些列和项目到列表控件中。以下是C++代码示例:
BOOL CListControlIssueDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE);
SetIcon(m_hIcon, FALSE);
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);
}
}
}
CRect Rect;
GetClientRect(&Rect);
m_cList.MoveWindow(5, 5, Rect.Width() - 10, Rect.Height() - 10);
return TRUE;
}
现在,让添加一个处理WM_SIZE消息的处理器,以便在对话框被调整大小时调整列表控件的大小。代码如下:
void CListControlIssueDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
if (IsWindow(m_cList.m_hWnd))
{
m_cList.MoveWindow(5, 5, cx - 10, cy - 10);
}
}
运行程序后,将看到一个包含10个项目、3列宽为100像素的列表控件,它会随着对话框的调整而调整大小。到目前为止都很简单。
现在,让添加代码以自动改变列的宽度。为此,需要创建一个继承自CListCtrl的类,称之为CMyListCtrl。然后,需要捕获列表控件的WM_SIZE消息,并添加一个名为AutoAdjustColumns的私有方法:
void CMyListCtrl::AutoSizeColumn()
{
SetColumnWidth(GetHeaderCtrl()->GetItemCount() - 1, LVSCW_AUTOSIZE_USEHEADER);
}
上面的代码非常简单。它只是遍历所有列(除了最后一列),并从控件的宽度中减去它们的宽度。然后将最后一列的宽度设置为剩余的值。(注意:为了不让文章变得太复杂,这段代码没有处理最小列宽的情况。但这种情况应该被处理)。
为了在列表控件调整大小时调整列宽,自然会想要在列表控件的OnSize处理器中调用AutoSizeColumn。以下是代码示例:
void CMyListCtrl::OnSize(UINT nType, int cx, int cy)
{
CListCtrl::OnSize(nType, cx, cy);
AutoSizeColumn();
}
Franc Morales建议,在用户拖动列标题后,列应该自动调整大小。为了实现这一点,需要处理HDN_ENDTRACK消息并调用AutoSizeColumn。以下是代码示例:
void CMyListCtrl::OnHdnEndtrack(NMHDR *pNMHDR, LRESULT *pResult)
{
AutoSizeColumn();
*pResult = 1;
}
别忘了将m_cList的类型从CListCtrl更改为CMyListCtrl。然后运行程序,滚动列表控件到底部,然后调整对话框的大小,直到不再需要垂直滚动条。可能会看到以下情况:
在看来,修复这个问题的方法非常有趣。如果直接从OnSize调用AutoSizeColumn,就会得到这样的结果,但如果在OnSize返回后调用它,一切就正常了。为了实现这一点,将改变OnSize的实现,使用PostMessage向自己发送消息,消息处理器将调用AutoSizeColumn。以下是代码示例:
#define WM_RESIZEME WM_APP+1
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;
}