动态调整列表控件列宽

在开发MFC应用程序时,经常需要处理列表控件,以展示数据。有时,希望列表控件的列宽能够随着窗口大小的变化而动态调整,以提高用户体验。本文将介绍如何实现这一功能。

在之前的一个论坛讨论中,有人提出了一个问题:他创建了一个列表控件,该控件能够在窗口大小变化时调整最后一列的宽度。但是,他遇到了一个问题:当列表控件的第一个项目不是第一个可见项目,并且用户调整窗口大小使得不再需要垂直滚动条时,列表控件顶部会出现一个空的条目。这看起来像是一个空白项,但实际上并没有项目。他提到Windows Media Player的一个版本也有同样的问题,但不记得具体是哪个版本了。起初不相信,直到亲自尝试并亲眼看到了这个bug。

开始编码

首先,需要创建一个基于对话框的应用程序。删除对话框上的所有内容,添加一个列表控件,将其设置为报告类型,并为其分配一个ID,称之为m_cList。不用担心它的位置和大小,将在代码中处理。别忘了在资源编辑器中将对话框的边框样式更改为可调整大小。同时,为了减少闪烁,为对话框设置WS_CLIPCHILDREN标志。

OnInitDialog函数

在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; }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485