永发信息网

vc中listbox控件为什么不能横滚动 vc/mfc / 基础类

答案:2  悬赏:50  手机版
解决时间 2021-02-19 10:02
vc中listbox控件为什么不能横滚动 vc/mfc / 基础类
最佳答案
在MFC中,用列表框(CListBox)来显示多个字符串是一种很方便的方法。但缺
省的列表框水平滚动条不够智能——这里智能的含义是:在应该出现的时候出
现,不应该出现的时候消失,而且应能自动调节自己的大小。本文通过实例说
明了存在的问题和解决办法。

---- 一、问题演示

---- 首先用Visual Studio应用向导创建工程CustomCListBox。这是一个基于对
话框的应用,向导提供的所有可选参数均采用其缺省值。

---- 在资源编辑器中将对主话框字体设为宋体12,插入一个CListBox控制,设
其ID为IDC_LLISTTEST,大小为125 X 84。 请确认列表框的垂直滚动条、水平滚
动条有效,取消其排序风格。

---- 启动Class Wizard,选择Member Variables选项卡,为列表框加入对应的
成员变量m_lListTest,在Category中选择Control。

---- 接下来在Workspace窗格中选择ClassView,扩展CCustomCListBoxDlg类并
双击OnInitDialog(),在编辑窗格中找到注释行“TODO: Add extra initialization here”,
在该行下面加入以下内容:

m_lListTest.AddString(_T("One"));
m_lListTest.AddString(_T("Two"));
m_lListTest.AddString(_T("Three"));
m_lListTest.AddString(_T("Four"));
m_lListTest.AddString(_T("Five"));
m_lListTest.AddString(_T("Six"));
m_lListTest.AddString(_T("北国风光,千里冰封,万里雪飘。"));
m_lListTest.AddString(_T("Eight"));
m_lListTest.AddString(_T("Nine"));
m_lListTest.AddString(_T("Ten"));

---- 编译并运行这个工程,可以发现列表框能够正确显示全部内容。

---- 如果在上述m_lListText.AddString(_T"Ten"))后面加入一行:

m_lListTest.AddString(_T("Eleven"));

---- 重新编译并运行该工程,可以发现出现了一个垂直滚动条。垂直滚动条的出
现使得列表框水平方向有效显示宽度变小,第七行的内容被切割而不能完整显
示。但此时水平滚动条并没有自动出现,第七行被切割部分就无法看到了。

---- 如果我们删除最后加入的语句,把第七行汉字加长到超出列表框显示宽度
为止,也可以发现水平滚动条不会自动出现。被切割部分仍旧无法看到。

---- 由此可知,CListBox的水平滚动条并不象垂直滚动条那样“聪明”:垂直
滚动条总是能够在需要它的时候自动出现,并能够自动调节自身大小,而水平
滚动条不能。

---- 二、解决问题

---- 为提高代码的可重用性,可以创建CListBox的派生类,在派生类中实现
“智能”水平滚动条。需要考虑的主要问题包括:跟踪最大字符串宽度(应能适
应不同场合下的字体变化),必要时计算垂直滚动条宽度,自动显示和调节水平
滚动条的大小。

---- 选菜单 Insert/New Class,设新创建类的名字为CDJListBox,其基类为
CListBox,其它选项采用缺省值。单击OK,Visual Studio自动生成
DJListBox.cpp和DJListBox.h两个文件。

---- 接下来将主对话框的列表框改为CDJListBox类型,即在CLassView扩展
CCustomListBoxDlg类并双击m_lListTest成员,在编辑窗格,修改

CListBox m_lListTest;

---- 为:

CDJListBox m_lListTest;

---- 然后,在类声明代码之前,插入

#include "DJListBox.h"

---- 此时如果重新编译并运行,是无法看到任何实质性的改变的,因为我们并
没有修改CDJListBox。所有对于CDJListBox的调用都直接传递给基类CListBox了。

---- 跟踪字符串最大宽度可以通过覆盖CListBox::AddString()实现。打开DJListBox.h,
紧接类的析构函数加入如下声明:

int AddString( LPCTSTR lpszItem );

---- 并在实现文件DJListBox.cpp加入该函数框架:

int CDJListBox::AddString(LPCTSTR lpszItem)
{
//此处加入字符串宽度跟踪、水平滚动条显示等代码
}

---- 字符串宽度跟踪可以用整形成员变量m_nMaxWidth实现。在DjListBox.h的
protected声明区内,加入以下一行:

int m_nMaxWidth;

---- 在DJListBox.cpp文件,找到CDJListBox的建构函数,为这个最大宽度作
初始化:

m_nMaxWidth = 0;

---- 现在可以改动新加入的AddString()了。先应该调用基类AddString(),
并用nRet记录其返回值:

int nRet = CListBox::AddString(lpszItem);

---- 接下来调用GetScrollInfo()以获得垂直滚动条的相关信息。这些信息是
通过一个SCROLLINFO结构传递的,下面是对该结构初始化并调用GetScrollInfo()的代码:

SCROLLINFO scrollInfo;
memset(&scrollInfo, 0, sizeof(SCROLLINFO));
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_ALL;
GetScrollInfo(SB_VERT, &scrollInfo, SIF_ALL);

---- 在调试器内观察SCROLLINFO,可以发现要获得nMax和nPage的正确数值,
列表框至少应含有一个字符串。SCROLLINFO的成员nPage保存了列表框“每页”能够
显示的项目数,nMax是列表框内项目总数。当nMax大于或等于nPage,就出现了
垂直滚动条。我们需要知道垂直滚动条的宽度以正确计算列表框的有效显示宽度。
这里使用一个初始值为0的整数nScrollWidth表示,并在垂直滚动条显示时将
它赋值:

int nScrollWidth = 0;
if(GetCount() > 1 && ((int)scrollInfo.nMax
> = (int)scrollInfo.nPage))
{
nScrollWidth = GetSystemMetrics(SM_CXVSCROLL);
}

---- 接下来声明一个SIZE变量sSize,并实例化对话框的CClientDC:

SIZE sSize;
CClientDC myDC(this);

---- 对话框所采用的字体,有可能是缺省字体,也有可能是有目的的选择。在
对话框编辑器中右击对话框,并选择Properties可以查看当前值。虽然MyDC是
从列表框取得的,但列表框字体信息并未包含在MyDC中。也就是说,对话框创
建时所用字体并没有“选入”CClientDC。要从GetTextExtentPoint32()获得真
正的字符串大小,应该先调用GetFont()获得列表框的字体信息,然后将此字体
选入MyDC,代码为:

Cfont* pListBoxFont = GetFont();
if(pListBoxFont != NULL)
{
Cfont* pOldFont =
myDC.SelectObject(pListBoxFont);

---- 现在可以调用GetTextExtendPoint32()函数来获得字符串的宽度了。字符
串的宽度由sSize结构的cx成员返回,将该值和已有最大宽度相比较:

GetTextExtentPoint32(myDC.m_hDC,
lpszItem, strlen(lpszItem), &sSize);
m_nMaxWidth = max(m_nMaxWidth, (int)sSize.cx);

---- 剩下的重要工作之一,就是设置水平滚动条的大小了。这可以通过调用
SetHorizontalExtent()完成。如果传递给它的整形参数比列表框本身宽度小,
则水平滚动条被隐藏。

---- 这里有一个容易被忽略的地方。如果仔细观察CListBox,可以发现文本左
边有一栏小小的空白,它的大小为3 。这部分宽度应该加到文本宽度上。如果
希望在文本右边也同样空出一栏,则可以在文本宽度上再加3。

SetHorizontalExtent(m_nMaxWidth + 3);

---- 在结束之前,我们需要为MyDC选入原有字体。原有字体保存在pOldFont中:

myDC.SelectObject(pOldFont); }

return nRet;

---- 编译并执行新的代码,可以看到水平滚动条终于能够自动显示了。

---- 三、其它问题

---- 在实际应用中,凡是改变列表框内容的函数都可能影响水平滚动条的显示
要求,因而也必须加以定制。但其基本过程——计算文本宽度并按指定大小显示
滚动条等,和上述讨论过程是相似的。

---- CListBox类能够改变列表内容的方法除AddString()外,还有
DeleteString(),InsertString(),ResetContent()。其中InsertString()用
于在指定位置插入字符串,在本文讨论的主题内它和AddString()是一样的。

---- DeleteString()删除一个字符串,在派生类中其参考代码如下:

int CDJListBox::DeleteString(UINT nIndex)
{
RECT lRect;
GetWindowRect(&lRect);

int nRet = CListBox::DeleteString(nIndex);

int nBoxWidth = lRect.right - lRect.left;
m_nMaxWidth = nBoxWidth;

SIZE sSize;
CClientDC myDC(this);

int I;
char szEntry[257];

for (I = 0; I < GetCount(); I++)
{
GetText(I, szEntry);
GetTextExtentPoint32(myDC.m_hDC,
szEntry, strlen(szEntry), &sSize);
m_nMaxWidth = max(m_nMaxWidth, (int)sSize.cx);
}

if (m_nMaxWidth > nBoxWidth) // 显示水平滚动条
{
ShowScrollBar(SB_HORZ, TRUE);
SetHorizontalExtent(m_nMaxWidth);
}
else
{
ShowScrollBar(SB_HORZ, FALSE);
}
return nRet;
}

---- ResetContent()用于清除列表框的全部内容。在派生类中其参考代码如下:

void CDJListBox::ResetContent()
{
CListBox::ResetContent();

m_nMaxWidth = 0;
SetHorizontalExtent(0);
}
全部回答
参考:VC ListBox列表框实现自动水平滚动功能
我要举报
如以上问答信息为低俗、色情、不良、暴力、侵权、涉及违法等信息,可以点下面链接进行举报!
大家都在看
4.4平衡插头可以直接插播放器吗
下列疾病转归中,不正确的是A.胞阻--胎动不安
与区相关的成语,带区字的成语,区字成语大全
醴陵市审计局地址在哪,我要去那里办事
国内做仪表较著名的企业!!!!
初中生⑴3x+2x=1-6怎么解
你们的iPhone用啥软件听音乐?
从郑州东站到豫棉宾馆怎么走?
华盛口腔门诊部(宜人路店)怎么去啊,有知道地
在法国怎么往国外汇款最划算
【dp点】商场DP点指的是什么?
一个会看事的朋友说,在我很小的时候就有一只
哪里能小额贷款啊5千到1W 急需用钱
壮乡食府这个地址在什么地方,我要处理点事
甘的部首是什么偏旁
推荐资讯
当股价穿越上限压力线时,为卖出信号.啥意思
合约期是什么?
本来血压正常!血压突然降低是什么原因?
一道VB题 求解!!!!!!!
金算盘6F财务软件工作站怎么安装?有加密狗的
被鱼刺卡着,有什么办法可以弄出来
—Who’s ,Tom or Tim? —Tom is A. quieter
梦想世界2蛮王殿天赋是??
请问谁知道济南天桥区实验小学的具体地址呀?
瑞迈特呼吸机上的service required是什么意思
三个“大”叠起来 念什么啊?
49.98除以0.058保留两位小数这么列竖式
正方形一边上任一点到这个正方形两条对角线的
阴历怎么看 ?