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

具有自动 HSCROLL 维护的 CListBox

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (8投票s)

2001年3月28日

3分钟阅读

viewsIcon

161717

downloadIcon

1745

消除向列表框添加水平滚动条的麻烦。

新闻组中大约每周出现一次的问题是“我检查了我的 ListBox 中的水平滚动样式,但我没有看到任何滚动条。我该怎么办?”

水平滚动理解起来很困难,文档记录也很差。为了使水平滚动工作,您必须调用 SetHorizontalExtent 方法并设置 ListBox 中条目使用的水平空间的总宽度。如果水平范围大于列表框的客户区,则会出现水平滚动条,前提是您已选择水平滚动样式。您需要同时设置这两个选项才能获得效果。

不幸的是,这在父窗口中很难做到。它需要在添加字符串的每个位置进行计算。这不是很好的面向对象的方法。因此,我所做的是创建一个从 CListBox 派生的新类,该类自动包含此功能。

我这样做的方式是维护一个值,该值是到目前为止设置的最大宽度。每当添加新字符串时,我都会更新宽度。删除字符串时,我也会更新宽度。我通过重写 ResetContentInsertItemAddItemDeleteString 方法来实现这一点。

请注意,这仅适用于非所有者绘制的列表框。对于所有者绘制的列表框,这有点容易,因为您可以在 DrawItem 处理程序中维护它。

您可以下载示例代码,但这里有一些摘录。该示例代码包括一个完整的项目,演示了滚动功能。 

请注意,要将此类包含在您的项目中,您需要在您的项目中包含源文件,然后删除 .clw 文件并重新调用 ClassWizard 以使其看到新类(Microsoft 过去允许直接导入类,但此功能似乎已在最新版本的 Visual Studio 中被删除)。然后,您可以直接在 ClassWizard 中使用 CHListBox 类创建控件变量。如果您不重新构建 .clw 文件,您将需要手动编辑您的头文件。如果您不知道如何执行此操作,请查看我的关于避免 GetDlgItem 的文章。

构造函数

我们需要在构造函数中初始化 width 变量

CHListBox::CHListBox()
   {
    width = 0;
   }

AddString 和 InsertString

AddStringInsertString 处理程序中,我们调用一个公共函数来更新宽度

int CHListBox::AddString(LPCTSTR s)
   {
    int result = CListBox::AddString(s);
    if(result < 0)
       return result;
    updateWidth(s);
    return result;
   }
int CHListBox::InsertString(int i, LPCTSTR s)
   {
    int result = CListBox::InsertString(i, s);
    if(result < 0)
       return result;
    updateWidth(s);
    return result;
   }

updateWidth 函数定义为

void CHListBox::updateWidth(LPCTSTR s)
    {
     CClientDC dc(this);

     CFont * f = CListBox::GetFont();
     dc.SelectObject(f);

     CSize sz = dc.GetTextExtent(s, _tcslen(s));
     sz.cx += 3 * ::GetSystemMetrics(SM_CXBORDER);
     if(sz.cx > width)
	 { /* extend */
	  width = sz.cx;
	  CListBox::SetHorizontalExtent(width);
	 } /* extend */
    }

我们添加 3*SM_CXBORDER 因子的原因是我们需要留出一点额外的空间来考虑用于绘制字符的(未记录且无法访问的)边距。 这个权宜之计似乎能给出最好的结果。 为了获得正确的计算,我们必须将控件中设置的字体选择到 DC 中。

ResetContent

ResetContent 方法很简单

void CHListBox::ResetContent()
    {
     CListBox::ResetContent();
     width = 0;
    }

DeleteString

DeleteString 操作很昂贵,因为我们不知道是否删除了最宽的字符串。 因此,我们必须重新评估所有字符串。 由于如果我们一直调用 updateWidth 可能会有点昂贵,因此 DC 创建和字体选择的功能已移回 DeleteString。 这可以通过一些 inline 函数来加速,但代码不是很复杂,所以似乎没有理由不复制它。

int CHListBox::DeleteString(int n)
    {
     int result = CListBox::DeleteString(n);
     if(result < 0)
	 return result;
     CClientDC dc(this);

     CFont * f = CListBox::GetFont();
     dc.SelectObject(f);

     width = 0;
     for(int i = 0; i < CListBox::GetCount(); i++)
	 { /* scan strings */
	  CString s;
	  CListBox::GetText(i, s);
	  CSize sz = dc.GetTextExtent(s);
          sz.cx += 3 * ::GetSystemMetrics(SM_CXBORDER);
	  if(sz.cx > width)
	      width = sz.cx;
	 } /* scan strings */
     CListBox::SetHorizontalExtent(width);
     return result;
    }

这些文章中表达的观点是作者的观点,不代表,也不被微软认可。

如有关于此网站的问题或意见,请发送邮件至newcomer@flounder.com
版权所有 © 1999CompanyLongName保留所有权利。
www.flounder.com/mvp_tips.htm
© . All rights reserved.