友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
C语言实例教程(PDF格式)-第34部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
pDC…》FrameRect(&lpDrawItemStruct…》rcItem; &br);
}
if (!(lpDrawItemStruct…》itemState & ODS_SELECTED) &&
…………………………………………………………Page 383……………………………………………………………
(lpDrawItemStruct…》itemAction & ODA_SELECT))
{
// 选中状态由选中变为非选中,清除其边框的加亮显示
CBrush br(cr);
pDC…》FrameRect(&lpDrawItemStruct…》rcItem; &br);
}
}
对于自绘制组合框来说,成员函数DrawItem是需要重载的一个很重要
的成员函数。该函数在自绘制组合框的可视部分发生改变时由框架调
用。在默认情况下,该成员函数不做任何操作。其参数
lpDrawItemStruct所指向的DRAWITEMSTRUCT结构包括了重绘制所需要
的各种信息,如所需重绘的项、其设备上下文以及所执行的重绘行为
等。在该成员函数终止前,应用程序应该恢复由该DRAWITEMSTRUCT结
构所提供的为该显示上下文所选定图形设备接口。
由表6。30可知在本示例程序中所使用的自绘组合框中的可选项是有序
的,而它们都是一些颜色值,框架如何知道当一个新的颜色值被添加
到组合框的列表框中时,它应该处于哪个颜色值之前,哪个颜色值之
后呢?这时通过调用成员函数pareItem成员函数来实现的。如果
在创建组合框时指定了LBS_SORT样式,则必须重载该成员函数以提供
足够的理由来帮助框架对新添加入组合框的列表框中的颜色项进行排
序。这里,我们首先根据颜色亮度的大小来对颜色进行排序,对于亮
度相同的颜色,我们依次以从蓝色到红色的优先级来判定其相对位
置。这个操作是以下面的代码来实现的:
int CClrboBox::pareItem(LPPAREITEMSTRUCT lppareItemStruct)
{
// TODO: 添加判断指定项的排序顺序的代码
// 当项 1 在项 2 之前时返回 …1
// 当项 1 和项 2 顺序相同时返回 0
// 当项 1 在项 2 之后时返回 1
// 获得项 1 和项 2 的颜色值
COLORREF cr1 = (COLORREF)lppareItemStruct…》itemData1;
…………………………………………………………Page 384……………………………………………………………
COLORREF cr2 = (COLORREF)lppareItemStruct…》itemData2;
if (cr1 == cr2)
{
// 项 1 和项 2 具有相同的颜色
return 0;
}
// 进行亮度比较; 亮度低的排列顺序在前
int intensity1 = GetRValue(cr1) + GetGValue(cr1) + GetBValue(cr1);
int intensity2 = GetRValue(cr2) + GetGValue(cr2) + GetBValue(cr2);
if (intensity1 《 intensity2)
return …1;
else if (intensity1 》 intensity2)
return 1;
// 如果亮度相同; 按颜色进行排序; (蓝色最前; 红色最后)
if (GetBValue(cr1) 》 GetBValue(cr2))
return …1;
else if (GetGValue(cr1) 》 GetGValue(cr2))
return …1;
else if (GetRValue(cr1) 》 GetRValue(cr2))
return …1;
else
return 1;
}
上面的代码同时也说明了pareItem成员函数的不同返回值所代表
的不同含义。这里要注意的是,由于同亮度的不同颜色给人的眼睛的
亮度感觉是不一样的(这好比人耳对声音的高频段和低频段的听觉灵
敏度要比对中频段的听觉灵敏度要小一样),上面的排序结果给人感
…………………………………………………………Page 385……………………………………………………………
觉并不象我们所想象的那样,是通过颜色的亮度来进行的。但我们没
有必要在这个问题上过分的纠缠而浪费时间。另外我们解释一下,为
什么要使用~cr & 0x00FFFFFF来代替~cr。很多人会认为直接将颜色
值按位取反就可以得到其对比色,但事实不是这样的。这是因为如果
32位颜色值的高位字节不为零的话,该颜色值将不被当作一个RGB颜
色值,而使用某个32位值与0x00FFFFFF按位与恰可以使其高位字节为
零,而其它位则不变。
到目前为止我们完成了自绘制组合框对应的类CClrboBox的设计,
下面我们来看如何在程序中将类CClrboBox的对象与对话框模板中
的现存组合框相关联,这是使用函数SubclassDlgItem来实现的。首
先在类CboDemoDlg中添加一个类型为CClrboBox的成员变量
m_clrbo,其访问限制在本工程中是不重要的,可以将它设置为
protected。然后,在类CboDemoDlg的OnInitDialog成员函数中
的// TODO注释之后添加下面的一行代码,这行代码将对象
m_clrbo与ID为IDC_CLRBO相关联,第一个参数为控件的父窗口
的指针。
m_clrbo。SubclassDlgItem(IDC_CLRBO; this);
这样就可以通过CWnd的消息映射机制和消息传递路径在类
CClrboBox中处理IDC_CLRBO中事件了。比如当组合框中的项需
要重绘时,在CClrboBox中定义的DrawItem成员函数将被调用,在
正确的绘制组合框中的内容。
4。 这里,我们在初始时没有为组合框添加任何选择项,用户可以单
击如图6。54的对话框中所示的 “添加颜色”按钮向组合框的列表框中
添加新颜色项。单击该按钮首先将弹出一个颜色选择对话框,用户如
果从颜色选择对话框中选择了一种具体的颜色,该颜色将被添加到组
合框的列表框中以供选择。基于这个要求,我们为按钮IDC_ADDCLR的
BN_CLICKED事件添加如下的命令处理成员函数OnAddClr:
void CboDemoDlg::OnAddClr()
{
CColorDialog dlg(0; 0; this);
int iRes=dlg。DoModal();
if (iRes==IDOK)
{
…………………………………………………………Page 386……………………………………………………………
COLORREF cr=dlg。GetColor();
m_clrbo。AddString( (LPCTSTR)cr );
}
else
{
}
}
虽然使用颜色对话框在本书的前面内容中没有讲述过,但即使是对初
学者而言,上面的代码也是非常之简单的,我们这里就不过多的作讲
解了。
下面来看如何为 “改变颜色”按钮 (IDC_CHGCLR)添加单击命令处理成
员函数。我们希望用户在单击该按钮时,改变静态文本控件
IDC_CLRSTATIC的颜色以反映用户所选择的颜色。如果改变静态文本
控件的颜色呢?我们这里使用了将 自绘制静态文本控件的办法。这并
不是最简单的方法。最简单的方法是处理对话框的WM_CTLCOLOR消
息,该消息在控件将要被重绘前发送给该控件的父窗口。这里我们舍
近而求远,主要是为了附带讲述一下自绘制静态文本控件的用法,它
和自绘制组合框的用法存在一些区别。但是,在资源编辑器中我们不
可以设置一个静态文本控件的自绘制样式,不过这并不意味将不可以
使用自绘制静态控件。方法并不复杂。首先,为静态文本控件添加自
绘制样式,将下面的代码添加到类CboDemoDlg的OnInitDialog成
员函数中的// TODO注释之后:
GetDlgItem(IDC_CLRSTATIC)…》ModifyStyle(0; SS_OWNERDRAW);
上面的代码将静态文本控件修改为具有SS_OWNERDRAW样式的自绘制静
态控件。下面我们来看如何在需要的时候重新绘制静态文本控件
IDC_CLRSTATIC,方法是在类CboDemoDlg中为消息WM_DRAWITEM添
加处理函数OnDrawItem,而使用ClassWizard很容易办到这一点。重
载版本的OnDrawItem成员函数的定义如下:
void CboDemoDlg::OnDrawItem(int nIDCtl; LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDialog::OnDrawItem(nIDCtl; lpDrawItemStruct);
if (nIDCtl==IDC_CLRSTATIC)
…………………………………………………………Page 387……………………………………………………………
{
CDC *pDC=CDC::FromHandle(lpDrawItemStruct…》hDC);
CBrush br(m_crClrStatic);
CRect rc=lpDrawItemStruct…》rcItem;
pDC…》FillRect(&rc; &br);
}
}
要使上面的代码正常工作,我们还需要在类CboDemoDlg中定义类
型为COLORREF的成员变量m_crClrStatic,该成员变量保存了静态文
本控件应该具有的颜色。我们注意到了在重载版本的OnDrawItem成员
变量调用了基类的OnDrawItem成员函数,这是必要的,不要忘记在我
们的对话框中还有一个自绘制组合框,基类的OnDrawItem成员函数调
用自绘制组合框所对应的CboBox的派生类的DrawItem成员函数来
绘制组合框中的各个项。
下面来看下压按钮IDC_CHGCLR的BN_CLICKED事件的处理函数
OnChgClr,其代码如下:
void CboDemoDlg::OnChgClr()
{
int nSel=m_clrbo。GetCurSel();
COLORREF cr=(COLORREF)m_clrbo。GetItemData(nSel);
if (cr!=…1)
{
DRAWITEMSTRUCT drawItemStruct;
drawItemStruct。CtlID=IDC_CLRSTATIC;
drawItemStruct。hwndItem=GetDlgItem(IDC_CLRSTATIC)…》GetSafeHwnd();
drawItemStruct。hDC=::GetDC(drawItemStruct。hwndItem);
GetDlgItem(IDC_CLRSTATIC)…》GetClientRect(&(drawItemStruct。rcItem));
m_crClrStatic=cr;
…………………………………………………………Page 388……………………………………………………………
OnDrawItem(IDC_CLRSTATIC; &drawItemStruct);
}
}
该成员函数将当前选中的颜色值 (如果不为CB_ERR的话)放入成员变量
m_crClrStatic中,然后构造一个DRAWITEMSTRUCT结构变量,并对它
进行必要的初始化,最后调用该结构对象调用OnDrawItem成员函数重
绘对话框。以后在需要重绘时,OnDrawItem成员函数会由框架自动调
用。
这时即可编辑并运行上面的示例程序了,其运行结果如图6。56所示。
单击 “添加颜色”向组合框的列表框中添加几种颜色选项,再来调试
程序的各项功能是否正常。还可以不同的窗口之前进行切换和相互覆
盖或移开,以观察自绘制组合框和自绘制静态文本控件是否正确的绘
制了自身。
图6。 56 示例程序boDemo的运行结果
相比较标准的组合框而言,自绘制组合框要复杂得多,我们得自己考
虑很多特殊的问题,但是如示例程序所示,它的确可以实现一些很有
趣的特性,因此在很多程序中得到广泛的使用。而掌握了自绘制控件
的使用,就可以使你所编写的应用程序界面更加的缤纷多彩,但是,
要注意一切事物的使用都有一个 “度”,不适宜的将应用程序的用户
界面做得过分的 “花哩呼哨”,很多时候只会适得其反。
第八节 滚动条控件
滚动条 (如图6。57所示)本身也可以作为一种控件,通常我们使用这种
控件来进行如定位之类的操作。滚动条控件分为水平滚动条和垂直滚
动条两种,它们对应于Controls工具箱中的图标分别为 和 。
图6。 57 滚动条控件
对于滚动条控件,可以在Properties对话框的Styles选项卡内设置的
…………………………………………………………Page 389……………………………………………………………
属性只有一种:即Align属性,该属性可以为三种值之一:None、
Top/Left和Bottom/Right。其中,Top/Left表示将滚动条控件的左上
边与由CreateWindowEx函数的参数定义的矩形的左上边对齐,而
Botton/Right则表示以右下边进行对齐。该属性的默认值为None,即
不进行任何对齐操作。
Windows标准滚动条的行为由MFC类CScrollBar封装。表中列出了在类
CScrollBar中定义的成员函数及其说明。
表6。 31 在类CScrollBar中定义的成员函数
成员函数 描述
CScrollBar 构造一个CScrollBar对象
Create 创建一个Windows滚动条,并将它与
CScrollBar对象相关联
GetScrollPos 获得滚动条的当前位置
SetScrollPos 设置滚动条的当前位置
GetScrollRange 获得给定滚动条的当前最大和最小位置
SetScrollRange 设置给定滚动条的当前最大和最小位置
ShowScrollBar 显示或隐藏滚动条
EnableScrollBar 允许或禁止滚动条上的一个或两个箭头
SetScrollInfo 设置关于滚动条的信息
GetScrollInfo 获得滚动条的信息
GetScrollLimit 获得滚动条的限制
当用户单击了滚动条时,父窗口将收到WM_HSCROLL或WM_VSCROLL消
息,在CWnd类的定义了处理该消息的成员函数为OnHScroll和
OnVScroll。成员函数OnHScroll的原型如下:
afx_msg void OnHScroll( UINT nSBCode; UINT nPos; CScrollBar* pScrollBar );
第一个参数nSBCode指定如下之一的滚动条代码,这些代码代表用户
所作的滚动请求:
SB_LEFT: 向左滚动较远距离
…………………………………………………………Page 390……………………………………………………………
SB_ENDSCROLL: 结束滚动
SB_LINELEFT: 向左滚动
SB_LINERIGHT: 向右滚动
SB_PAGELEFT: 向左滚动一页
SB_PAGERIGHT: 向右滚动一页
SB_RIGHT: 向右滚动较远距离
SB_THUMBPOSITION: 滚动到绝对位置。当前位置由
nPos参数指定
SB_THUMBTRACK: 拖动滚动条到指定的位置。当
前位置由nPos参数指定
通常,SB_THUMBTRACK滚动条代码由应用程序使用,以便在滚动条被
拖动时给以反馈。如果应用程序滚动了由滚动条控制的内容,它必须
使用SetScrollPos来重置滚动条的位置。
传递给函数OnHScroll的参数反映了当收到消息时由框架获得的值,
如果在重载版本的函数中调用了基类的实现,该实现将使用最初由消
息传递的参数,而不是向函数提供的参数。
消息WM_VSCROLL的处理函数OnVScroll与OnHScroll类似,我们这里就
不再重复讲述了。下面我们来看一个例子:
1。 创建一个名为ScrollDemo的基于对话框的MFC工程,按图设置对话
框的各控件。其中水平滚动条控件的ID为IDC_SCROLL,编辑框控件的
ID为IDC_CURPOS。
图6。 58 示例程序ScrollDemo的主对话框的设计
2。 使用ClassWizard为编辑框控件IDC_CURPOS映射类型为int的成员
变量m_iCurPos,并设置其最大值为100,最小值为…100。
3。 使用ClassWizard在类CScrollDemoDlg中为消息WM_HSCROLL添加处
理函数OnHScroll,其代码如下:
…………………………………………………………Page 391……………………………………………………………
void CScrollDemoDlg::OnHScroll(UINT nSBCode; UINT nPos; CScrollBar* pScrollBar)
{
// 获得原有的滚动条位置
int iPos=pScrollBar…》GetScrollPos();
// 根据不同的拖动方式设置新的滚动条位置
switch (nSBCode)
{
// 向右滚动一行
case SB_LINERIGHT:
iPos+=1;
break;
// 向左滚动一行
case SB_LINELEFT:
iPos…=1;
break;
// 向右滚动一页
case SB_PAGERIGHT:
iPos+=10;
break;
// 向左滚动一页
case SB_PAGELEFT:
iPos…=10;
break;
// 直接拖动滚动块
case SB_THUMBTRACK:
iPos=nPos;
…………………………………………………………Page 392……………………………………………………………
break;
default:
break;
}
// 滚动条的最大位置不超过 100; 最小位置不小于 …100
if (iPos100) iPos=100;
// 必须手动的更新滚动条的当前位置
pScrollBar…》SetScrollPos(iPos);
// 在编辑框中显示滚动条的当前位置
SetDlgItemInt(IDC_CURPOS; iPos);
CDialog::OnHScroll(nSBCode; nPos; pScrollBar);
}
上面的代码暗示了一点,这就是在被拖动时,滚动条不会 自动更新其
位置,我们必须自己在程序中做到这一点,即通过分析不同的滚动方
式来改变并设置新的滚动条位置,上面的代码演示了这一过程。
编译上面的程序代码,我们发现滚动条不能正常工作 !这是因为在默
认情况下,滚动条的滚动范围为从0到0。这时,我们根本不可能对滚
动条进行有意义的操作。因此,我们需要将下面的代码添加到
OnInitDialog成员函数:
CScrollBar *pScroll=(CScrollBar*)GetDlgItem(IDC_SCROLL);
pScroll…》SetScrollRange(…100; 100);
pScroll…》SetScrollPos(0);
SetDlgItemInt(IDC_CURPOS; 0);
上面的代码设定了滚动条的滚动范围和默认的滚动条位置,然后,将
当前滚动条位置显示在编辑控件IDC_CURPOS中。
4。 最后我们来实现一个功能,这就是我们希望当编辑控件中的文本
发生改变时,滚动条上的滑块的位置也相应的变化。要实现这一点,
…………………………………………………………Page 393……………………………………………………………
使用ClassWizard为控件IDC_CURPOS的通知消息EN_CHANGE添加消息处
理函数OnChangeCurPos。
void CScrollDemoDlg::OnChangeCurPos()
{
CString str;
GetDlgItemText(IDC_CURPOS; str);
str。TrimLeft();
str。TrimRight();
int iPos=0;
if (str!=〃…〃 && str!=〃〃)
{
if (!UpdateData())
{
return;
}
iPos=m_iCurPos;
}
CScrollBar *pScroll=(CScrollBar*)GetDlgItem(IDC_SCROLL);
pScroll…》SetScrollPos(iPos);
}
由于需要检验用户输入数据的有效性,上面的代码比较长。首先,如
果用户只输入一个负号 “? ”或刚将原有的数据删除,此时不应该报
错。这里我们可以将滚动条的位置设置为0。由于用户可能在所输入
的数据之前或之后插入一些空格,这种情况下我们也不应该报错,因
此,我们使用了一些额外的代码来避免了这种情况。最后,我们使用
了UpdateData函数来使用控件IDC_CURPOS的值更新成员变量
m_iCurPos,这样的目的是便于使用MFC提供的对话框数据检验机制。
但有个不好的地方是,如果用户输入的数据
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!