具有自动 HSCROLL 维护的 CListBox






4.50/5 (8投票s)
2001年3月28日
3分钟阅读

161717

1745
消除向列表框添加水平滚动条的麻烦。
新闻组中大约每周出现一次的问题是“我检查了我的 ListBox 中的水平滚动样式,但我没有看到任何滚动条。我该怎么办?”
水平滚动理解起来很困难,文档记录也很差。为了使水平滚动工作,您必须调用 SetHorizontalExtent
方法并设置 ListBox 中条目使用的水平空间的总宽度。如果水平范围大于列表框的客户区,则会出现水平滚动条,前提是您已选择水平滚动样式。您需要同时设置这两个选项才能获得效果。
不幸的是,这在父窗口中很难做到。它需要在添加字符串的每个位置进行计算。这不是很好的面向对象的方法。因此,我所做的是创建一个从 CListBox
派生的新类,该类自动包含此功能。
我这样做的方式是维护一个值,该值是到目前为止设置的最大宽度。每当添加新字符串时,我都会更新宽度。删除字符串时,我也会更新宽度。我通过重写 ResetContent
、InsertItem
、AddItem
和 DeleteString
方法来实现这一点。
请注意,这仅适用于非所有者绘制的列表框。对于所有者绘制的列表框,这有点容易,因为您可以在 DrawItem
处理程序中维护它。
您可以下载示例代码,但这里有一些摘录。该示例代码包括一个完整的项目,演示了滚动功能。
请注意,要将此类包含在您的项目中,您需要在您的项目中包含源文件,然后删除 .clw 文件并重新调用 ClassWizard 以使其看到新类(Microsoft 过去允许直接导入类,但此功能似乎已在最新版本的 Visual Studio 中被删除)。然后,您可以直接在 ClassWizard 中使用 CHListBox
类创建控件变量。如果您不重新构建 .clw 文件,您将需要手动编辑您的头文件。如果您不知道如何执行此操作,请查看我的关于避免 GetDlgItem 的文章。
构造函数
我们需要在构造函数中初始化 width 变量
CHListBox::CHListBox()
{
width = 0;
}
AddString 和 InsertString
在 AddString
和 InsertString
处理程序中,我们调用一个公共函数来更新宽度
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; }
这些文章中表达的观点是作者的观点,不代表,也不被微软认可。