自动调整列表控件标题的大小






4.87/5 (16投票s)
自动调整列表控件标题的最后一列。但有一个问题……
引言
您是否曾经想过如何使列表控件自动调整最后一列的大小,以适应整个列表控件,而无需水平滚动条。您是否曾希望在调整列表控件大小时做到这一点。
您是否真的完成了它,但最终得到了类似这样的结果?
有希望,请继续阅读。
背景
前段时间,我正在论坛上回答一个问题,提问者创建了一个列表控件,该控件在调整视图大小时会调整最后一列的大小。但他遇到了一个问题。当列表控件中的第一个项目不是第一个可见项目,并且用户调整视图大小直到不再需要垂直滚动条时,列表控件最终会在顶部出现一个空条目。当然那里没有项目,但它看起来像一个空白项目。他说 Windows Media Player 的一个版本也有同样的问题,但我记不起版本号了。无论如何,直到我自己尝试并亲眼看到了这个错误,我才相信他。
让我们开始编写一些调整大小的代码,在此过程中找到问题并修复它。
使用代码
我们需要做的第一件事是创建一个基于对话框的应用程序。删除对话框中的所有内容。添加一个列表控件,使其为报告类型,为其提供一个 ID,并为其附加一个变量,我们将其称为m_cList
。不要太担心它的大小或位置;我们将在代码中处理它。别忘了在资源编辑器中将对话框的边框样式更改为“可调整大小”。另外,为了减少闪烁,请为对话框设置WS_CLIPCHILDREN
标志。
完成上述步骤后,我们就可以开始编码了。让我们向列表控件添加一些列和项目。我们将在主对话框的OnInitDialog
中执行此操作。
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
}
现在,我们还要为WM_SIZE
消息添加一个处理程序,以便在调整对话框大小时调整列表控件的大小以适应对话框。这应该看起来像这样
void CListControlIssueDlg::OnSize(UINT nType, int cx, int cy)
{
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);
}
}
现在,当您运行该程序时,您应该会看到一个列表控件,其中包含 10 个项目、3 列,每列宽 100 像素,它们会随着您调整对话框的大小而调整大小。到目前为止很简单。
现在让我们添加代码来自动更改列的大小。为此,我们需要创建一个从CListCtrl
继承的类,我们将其称为CMyListCtrl
。完成后,让我们捕获 List 控件的WM_SIZE
消息。同时添加一个名为AutoAdjustColumns
的私有方法,如下所示
(我必须感谢 Chris Radke 提供这段代码。)
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
。
//Per Franc Morales' suggestion
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;
}
现在运行该程序,并以与之前相同的方式调整对话框的大小。现在应该没问题了。
玩得开心!