65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (16投票s)

2007年4月12日

CPOL

4分钟阅读

viewsIcon

87709

downloadIcon

1378

自动调整列表控件标题的最后一列。但有一个问题……

Screenshot - ListCtrlAutoSize.jpg

引言

您是否曾经想过如何使列表控件自动调整最后一列的大小,以适应整个列表控件,而无需水平滚动条。您是否曾希望在调整列表控件大小时做到这一点。

您是否真的完成了它,但最终得到了类似这样的结果?

Screenshot - ListCtrlAutoSizeProblem.jpg

有希望,请继续阅读。

背景

前段时间,我正在论坛上回答一个问题,提问者创建了一个列表控件,该控件在调整视图大小时会调整最后一列的大小。但他遇到了一个问题。当列表控件中的第一个项目不是第一个可见项目,并且用户调整视图大小直到不再需要垂直滚动条时,列表控件最终会在顶部出现一个空条目。当然那里没有项目,但它看起来像一个空白项目。他说 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。然后运行该程序,将列表控件滚动到底部,然后调整对话框的大小,直到不再需要垂直滚动条。您可能会看到

Screenshot - ListCtrlAutoSizeProblem.jpg

嗯,在我看来,解决这个问题的方法非常有趣。如果直接从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;
}

现在运行该程序,并以与之前相同的方式调整对话框的大小。现在应该没问题了。

玩得开心!

© . All rights reserved.