我们在做程序设计时界面与功能,那个更加吸引用户的兴趣呢?这是一个很难回答的问题。拥有美丽的外观,软件就成功了一半。界面由控件、工具栏、菜单、窗体等元素组成,对他们进行美化就能得到一个美丽的界面。
目前界面编程技术包括MFC、win32 SDK 、CJLibrary、WTL以及一些界面开发包。文本介绍MFC界面编程技术。
一、控件自绘
控件的生成包括静态控件和动态控件的生成。动态控件是在应用程序运行过程中临时产生的。所以在进行动态控件的自绘时,方法比自绘静态控件复杂些。应该考虑控件的大小、宽高等。
自绘控件类型
静态控件
动态控件
绘制步骤
1、控件具有自绘属性。
2、响应OnDrawItem函数。
1、控件具有自绘属性。
2、响应OnMeasureItem函数。
3、响应OnDrawItem函数。
注:控件的自画需要响应四个消息:WM_MEASUREITEM, WM_DRAWITEM, WM_COMPAREITEM, 和WM_DELETEITEM.
combo box ,list box 销毁时响应OnDeleteItem
combo ,list box 排序时响应OnCompareItem
button, combo box, list box, or menu 创建时响应OnMeasureItem
button, combo box, list box, or menu 改变时响应OnDrawItem
OnDrawItem函数说明,函数定义为:
afx_msg void OnDrawItem(int nIDCtl,LPDRAWITEMSTRUCT lpDrawItemStruct);
参数说明:
nIDCtl:发送WM_DRAWITEM消息控件的ID值,如果该值为零,表明该消息由菜单控件发出的。
LpDrawItemStruct:指向一个DRAWITEMSTRUCT结构的指针, DRAWITEMSTRUCT 为需要自绘的控件或者菜单项提供了必要的信息。在需要绘制的控件或者菜单项对应的WM_DRAWITEM消息函数中得到一个指向该结构的指针。 DRAWITEMSTRUCT结构的定义如下:
typedef struct tagDRAWITEMSTRUCT {
UINT CtlType;
UINT CtlID;
UINT itemID;
UINT itemAction;
UINT itemState;
HWND hwndItem;
HDC hDC;
RECT rcItem;
ULONG_PTR itemData;
} DRAWITEMSTRUCT;
结构成员:
CtlType :指定了控件的类型,其取值如下表所示。
取值 描述
ODT_BUTTON 按钮控件
ODT_COMBOBOX 组合框控件
ODT_LISTBOX 列表框控件
ODT_LISTVIEW 列表视图控件
ODT_MENU 菜单项
ODT_STATIC 静态文本控件
ODT_TAB Tab控件
CtlID: 指定了自绘控件的ID值,而对于菜单项则不需要使用该成员
itemID :表示菜单项ID,也可以表示列表框或者组合框中某项的索引值。对于一个空的列表框或组合框,该成员的值为–1。这时应用程序只绘制焦点矩形(该矩形的坐标由rcItem 成员给出)虽然此时控件中没有需要显示的项,但是绘制焦点矩形还是很有必要的,因为这样做能够提示用户该控件是否具有输入焦点。当然也可以设置itemAction 成员为合适值,使得无需绘制焦点。
itemAction :指定绘制行为,其取值可以为下表中所示值的一个或者多个的联合。
取值 描述
ODA_DRAWENTIRE 当整个控件都需要被绘制时,设置该值
ODA_FOCUS 如果控件需要在获得或失去焦点时被绘制,则设置该值。此时应该检查itemState成员,以确定控件是否具有输入焦点。
ODA_SELECT 如果控件需要在选中状态改变时被绘制,则设置该值。此时应该检查itemState 成员,以确定控件是否处于选中状态。
itemState :指定了当前绘制操作完成后,所绘项的可见状态。例如,如果菜单项应该被灰色显示,则可以指定ODS_GRAYED状态标志。其取值可以为下表中所示值的一个或者多个的联合。
取值 描述
ODS_CHECKED 如果菜单项将被选中,则可设置该值。该值只对菜单项有用。
ODS_COMBOBOXEDIT 在自绘组合框控件中只绘制选择区域。
ODS_DEFAULT 默认值。
ODS_DISABLED 如果控件将被禁止,则设置该值。
ODS_FOCUS 如果控件需要输入焦点,则设置该值。
ODS_GRAYED 如果控件需要被灰色显示,则设置该值。该值只在绘制菜单时使用。
ODS_HOTLIGHT Windows 98/Me, Windows 2000/XP: 如果鼠标指针位于控件之上,则设置该值,这时控件会显示高亮颜色。
ODS_INACTIVE Windows 98/Me, Windows 2000/XP: 表示没有激活的菜单项。
ODS_NOACCEL Windows 2000/XP: 控件是否有快速键盘。
ODS_NOFOCUSRECT Windows 2000/XP: 不绘制捕获焦点的效果。
ODS_SELECTED 选中的菜单项。
hwndItem :指定了组合框、列表框和按钮等自绘控件的窗口句柄;如果自绘的对象时菜单项,则表示包含该菜单项的菜单句柄。
hDC :指定了绘制操作所使用的设备环境。
rcItem :指定了将被绘制的矩形区域。这个矩形区域就是上面hDC的作用范围。系统会自动裁剪组合框、列表框或按钮等控件的自绘制区域以外的部分。也就是说rcItem中的坐标点(0,0)指的就是控件的左上角。但是系统不裁剪菜单项,所以在绘制菜单项的时候,必须先通过一定的换算得到该菜单项的位置,以保证绘制操作在我们希望的区域中进行。
itemData :
对于菜单项,该成员的取值可以是由CMenu::AppendMenu、CMenu::InsertMenu或者CMenu::ModifyMenu等函数传递给菜单的值。
对于列表框或这组合框,该成员的值可以为由ComboBox::AddString、CComboBox::InsertString、CListBox::AddString或者CListBox::InsertString等传递给控件的值。
如果ctlType 的取值是ODT_BUTTON或者ODT_STATIC, itemData的取值为0
OnMeasureItem函数说明,函数定位:
afx_msg void OnMeasureItem(int nIDCtl,LPMEASUREITEMSTRUCT lpMeasureItemStruct);
参数说明:
nIDCtl:发送WM_MEASUREITEM消息控件的ID值,如果该值为零,表明该消息是由菜单控件发出的。
LpMeasureItemStruct:指向一个MEASUREITEMSTRUCT结构的指针,它的数据结构定义如下:
typedef struct tagMEASUREITEMSTRUCT {
UINT CtlType;
UINT CtlID;
UINT itemID;
UINT itemWidth;
UINT itemHeight;
DWORD itemData;
} MEASUREITEMSTRUCT;
CtlType:指定控件的类型.这个成员可以是下列的一个值:
取值 描述
ODT_BUTTON 自绘按钮
ODT_COMBOBOX 自绘组合框
ODT_LISTBOX 自绘列表框
ODT_LISTVIEW 自绘列表视图控件
ODT_MENU 自绘菜单
CtlID:指定组合框(combo box), 列表框(list box), 或 控钮(button)的标识符.这个成员不能在菜单中使用
ItemID:指定菜单项的标识符或组合框(combo box), 列表框(list box)的位置索引。列表框(list box)风格已经有LBS_OWNERDRAWVARIABLE时这个值才被指定。组合框(combo box)风格已经有CBS_OWNERDRAWVARIABLE风格时这个值才被指定。
ItemWidth:指定宽,单位象素,一个菜单项目.在从消息返回之前,自绘菜单项的所有者必需填充这个成员。
ItemHeight:指定高,单位象素,列表框(list box)一个个别的项或一个菜单.在从消息返回之前自绘组合框,列表框或菜单项必需填写这个参数。
ItemData:指定与应用程序定义的菜单项相关联的32位值.做为控件,这个参数指定值是最后指定给列表框(list box)或组合框(combo box)的LB_SETITEMDATA或CB_SETITEMDATA消息中的值.如果列表框(list box)或组合框(combo box)已经使用LB_HASSTRINGS或CB_HASSTRINGS风格这个最初值是零.否则,这个值最初的值是传给列表框(list box)或组合框(combo box)下列消息中lparam参数的一个值:
CB_ADDSTRING
CB_INSERTSTRING
LB_ADDSTRING
LB_INSERTSTRING
WM_MEASUREITEM与WM_DRAWITEM区别:在WM_MEASUREITEM消息影射函数中设置当前要画的Item的大小尺寸;创建控件。在WM_DRAWITEM消息影射函数中根据Item的大小尺寸来画该Item(图标/位图/字符串等)。
二、常用控件使用方法
1、按钮类
CButtonST目前见过的最强大,功能最全的CButton派生类。具体使用方法参考:http://www.vckbase.com/document/viewdoc/?id=517
2、菜单
自绘菜单的实现:
http://www.vckbase.com/document/viewdoc/?id=1200
3、工具条使用方法
http://www.vckbase.com/document/viewdoc/?id=629
http://www.vckbase.com/document/listdoc.asp?mclsid=3&sclsid=305
4、CToolTipCtrl使用方法
ToolTip是Win32中一个通用控件,用于提示信息的显示,MFC中为其生成了一个类CToolTipCtrl,总的说来其使用方法是较简单的,下面讲一下它的一般用法和高级用法。
一般用法步骤:
添加CToolTipCtrl成员变量 m_tt。
在父窗口中调用EnableToolTips(TRUE);
在窗口的OnCreate(或者其他适当的位置)中向ToolTip中添加需要显示Tip的子窗口,并同时指定相应的显示字串CToolTipCtrl::AddTool(pWnd,"string to display")。
重载父窗口的 BOOL PreTranslateMessage(MSG* pMsg) ,在函数中调用 m_tt.RelayEvent(pMsg)。
下面假设在窗口CWndYour中使用CToolTipCtrl
在类定义中添加变量说明:
class CWndYour:xxx
{
CToolTipCtrl m_tt;
}
在OnCreate中添加需要显示Tip的子窗口
CWndYour::OnCreate(....)
{
EnableToolTips(TRUE);
m_tt.Create(this);
m_tt.Activate(TRUE);
CWnd* pW=GetDlgItem(IDC_CHECK1);//得到窗口指针
m_tooltip.AddTool(pW,"Check1");//添加
........
}
在BOOL PreTranslateMessage(MSG* pMsg)中添加代码
BOOL CWndYour::PreTranslateMessage(MSG* pMsg)
{
{
m_tt.RelayEvent(pMsg);
}
return CParentClass::PreTranslateMessage(pMsg);
}
这样当鼠标移动到相应的子窗口上时会显示出相应的ToolTip。
动态改变ToolTip的显示内容的方法及步骤:
上面所讲的1、2、4步骤。
在增加ToolTip时不指定显示的字串,而是使用LPSTR_TEXTCALLBACK。
在窗口中增加消息映射 ON_NOTIFY_EX( TTN_NEEDTEXT, 0, SetTipText )。
在窗口中增加一个函数用于动态提供显示内容,其原型为 BOOL SetTipText( UINT id, NMHDR * pTTTStruct, LRESULT * pResult ),下面的代码可以根据传入的参数判定应该显示的内容。
BOOL CWndYour::SetTipText( UINT id, NMHDR * pTTTStruct, LRESULT * pResult )
{
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pTTTStruct;
UINT nID =pTTTStruct->idFrom; //得到相应窗口ID,有可能是HWND
if (pTTT->uFlags & TTF_IDISHWND) //表明nID是否为HWND
{
nID = ::GetDlgCtrlID((HWND)nID);//从HWND得到ID值,当然你也可以通过HWND值来判断
switch(nID)
case(IDC_YOUR_CONTROL1)
strcpy(pTTT->lpszText,your_string1);//设置
return TRUE;
break;
case(IDC_YOUR_CONTROL2)
//设置相应的显示字串
return TRUE;
break;
}
return(FALSE);
}
5、状态栏
状态栏是基于 Windows 通用控件 msctls_statusbar32,这个通用控件并不提供任何方法来添加子窗口。在 Windows 中,在某些控件或是窗口中添加子窗口并不是将它们作为这些控件的子窗口,而是作为这些控件的兄弟窗口。在现在这种情况下,你有两个选择:一是建立一个“超级状态栏“,它包含一个普通状态栏 以及其它控件子窗口(就像 Windows 结合列表框和编辑框而合成的组合框一样);第二、你也可以直接将按钮或是其它控件直接加在主框架上,就像是状态栏,工具栏 或视图的兄弟窗口一样。
至于决定使用那种方法取决于你的设计有多复杂以及你的规划。如果你想加很多的控件, 和/或在其它的窗口或应用程序中重用组合的状态栏/按钮/编辑控制的话,那么最好建立一个复合控件。如果仅仅是想 在某个窗口中添加单个按钮,那么最好是将它添加到主框架。无论你使用哪种方法,你都需要写一点代码来定位你的控件,使 之与其它相邻的控件在合适的位置上。
1)首先了解单文档应用程序中自带的状态栏结构:
在Mainfrm.cpp中有两处:
static UINT indicators[] =
{
ID_SEPARATOR, // 状态行指示器
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
该数组指明每个指示器的ID,可以理解为状态栏中的一个格子,格子的大小由字符串的长度决定。其中每个ID在string table 中都有定义,定义中的的“标题”就是将要显示的内容,如“AFX_IDS_IDLEMESSAGE 57345 就绪”等。
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("未能创建状态栏\n");
return -1; // 未能创建
}
以上语句是在OnCreate()函数中创建状态栏,其中用SetIndicators函数将indicators数组中的内容设为每个指示器的ID。
2)简单操作--添加时间
添加SetTimer()和OnTimer函数,在Ontimer中添加
CTime time = CTime::GetCurrentTime();
CString str;
str=time.Format("%H:%M:%S");
m_wndStatusBar.SetPaneText(0,str); //显示时钟
即可显示时钟。如果添加自定义的ID则要在string table中添加新的图IDS_MYTIME 00:00:00 并将其添加到indicators数组中,将m_wndStatusBar.SetPaneText(0,str); //显示时钟 改为:
m_wndStatusBar.SetPaneText(m_wndStatusBar.CommandToIndex(IDS_MYTIME),str);
6、CEdit使用方法
http://www.vckbase.com/document/viewdoc/?id=1025
http://www.vckbase.com/document/listdoc.asp?mclsid=3&sclsid=311
7、CRichEditCtrl使用方法
http://blog.csdn.net/byxdaz/archive/2010/03/18/5393658.aspx
http://www.vckbase.com/document/viewdoc/?id=328
8、ComboBox
Auto-completion ComboBox:
http://www.codeguru.com/cpp/controls/combobox/article.php/c1807/
9、属性页CPropertySheet使用方法
http://www.vckbase.com/document/viewdoc/?id=427
http://www.vckbase.com/document/listdoc.asp?mclsid=3&sclsid=317
10、静态控件使用方法
http://www.vckbase.com/document/listdoc.asp?mclsid=3&sclsid=319
11、树型控件使用方法
树形控件在MFC中以两种形式来封装,即树形控件(CtreeCtrl)和树形视图(CtreeView),来满足用户的不同需求,对于一般要求的用户如在对话框中应用,使用树形控件比较方便,而对于具有较高要求的用户,在使用树形视图时还具有视图窗口的各种方便特性,可以更好的满足文档/视图结构的要求。在窗口中使用树形视图时,树形视图会占满两个窗口的客户区域并自动随窗口的框架结构的调整而调整,并能处理菜单、工具条中的命令消息。在树形视图中利用CtreeCtrl & treeCtrl = GetTreeCtrl()得到树形控件。
树形控件一些典型使用过程如下:
1)、树形控件的创建,如果树形控件是添加在对话框资源中的,树形控件的创建是自动的。如果是动态生成的树形控件,则需要使用Create函数进行创建。
树形控件的外观取决于在创建时对其风格的设置。通常,树形控件的风格有以下几种:
TVS_EDITLABELS 允许用户进行节点文本的编辑
TVS_HASBUTTONS 节点左侧添加一个按钮
TVS_HASLINES 父节点与子节点之间出现连线
TVS_LINESATROOT 子节点与根节点之间出现连线
TVS_NOTOOLTIPS 节点无动态提示
TVS_SINGLEEXPAND 节点的选中(未选中)与展开(合拢)同步
2)、树形控件节点的添加。调用InsertItem函数能够将节点插入到树形控件中。InsertItem函数成功调用后,返回的节点将有可能成为下次使用InsertItem的一个参数(如父节点)。节点插入工作往往是在对话框OnInitDialog函数中进行,或者在文档/视图中OnInitUpdate函数使用。
3)、节点数据的使用。树形控件节点的数据可以是文本的,也可以是图像。节点中使用图像是和树形控件的图像列表相对应。在树形控件使用图像列表使用SetImageList函数来完成。
4)、进行树形控件的消息响应,当用户在树形控件中对节点的选择发生变化时,树形控件会发出相应的通知消息。如果需要对这些消息进行响应,则需要在程序中添加ON_NOTIFY_REFLECT宏。
树形控件产生的通知消息
树形控件
通知消息
TVN_BEGINDRAG
开始拖拽操作
TVN_BEGINLABELEDIT
开始编辑节点文本
TVN_BEGINRDRAG
开始拖拽操作(使用右键)
TVN_DELETEITEM
删除指定节点
TVN_ENDLABELEDIT
结束编辑节点
TVN_GETDISPINFO
请求显示节点所需的消息
TVN_GETINFOTIP
需要得到节点数据提示
TVN_ITEMEXPANDED
节点被展开或合拢
TVN_ITEMEXPANDING
节点即将被展开或合拢
TVN_KEYDOWN
按键操作
TVN_SELCHANGED
用户选择的节点发生变化
TVN_SELCHANGING
用户选择的节点即将发生变化
TVN_SETDISPINFO
更新节点数据
TVN_SINGLEEXPAND
节点被展开或合拢(使用鼠标单击)
注:树控制的数据结构 在使用树控制时需要了解两个个非常重要的数据结构TV_ITEM和TV_INSERTSTRUCT,前一个数据结构是用来表示树控制的树项信息,后一个数据结构是用来定义将树项增加到数据控制中所需要的数据内容。另外,还需要NM_TREEVIEW、TV_DISPINFO和TV_HITTESTINFO三个数据结构,这几个数据结构的定义方法如下:
①基本数据项结构
typedef struct _TV_ITEM{
UINT mask; //结构成员有效性屏蔽位
HTREEITEM hItem; //数据项控制句柄
UINT state; //数据项状态
UINT stateMask; //状态有效性屏蔽位
LPSTR pszText; //数据项名称字符串
int cchTextMax; //数据项名称的最大长度
int iImage; //数据项图标索引号
int iSelectedImage;//选中数据项图标索引号
int cChildren; //子项标识
LPARAM lParam; //程序定义的32位数据
} TV_ITEM, FAR *LPTV_ITEM;
②插入树项结构
typedef struct _TV_INSER TSTRUCT {
HTREEITEM hParent; //父项控制句柄
HTREEITEM hInsertAfter; //插入树项的位置
TV_ITEM item; //数据项的结构
} TV_INSERTSTRUCT, FAR *LPTV_INSERTSTRUCT;
其中插入的位置如果是TVI_FIRST 或TVI_LAST ,则分别插入到树控制的最前面或最后面,如果是TVI_SORT ,则插入的树项自动插入到合适的位置。
③树控制通知消息结构
typedef struct _NM_TREEVIEW {
NMHDR hdr; //通知消息句柄
UINT action; //通知消息标志
TV_ITEM itemOld; //原来的数据结构
TV_ITEM itemNew; //新的数据结构
POINT ptDrag; //拖动指针
} NM_TREEVIEW;
④取得或设置数据结构
typedef struct _TV_DISPINFO {
tvdi NMHDR hdr; //通知消息控制句柄
TV_ITEM item; //数据项结构
} TV_DISPINFO;
⑤指针测试数据结构
typedef struct _TVHITTESTINFO {
tvhtst POINT pt; //客户区域屏幕坐标指针
UINT flags; //存放测试结果的变量
HTREEITEM hItem; //测试的数据项结构
} TV_HITTESTINFO, FAR *LPTV_HITTESTINFO;
其中flags测试结果可以是如下值: TVHT_ABOVE 在客户区域上面 TVHT_BELOW 在客户区域下面 TVHT_NOWHERE 在客户区域中并在最后一项下面 TVHT_ONITEM 在与树项关联的位图或标签内 TVHT_ONITEMBUTTON 在与树项关联的按钮上 TVHT_ONITEMICON 在与树项关联的位图上 TVHT_ONITEMINDENT 在与树项关联的联线上 TVHT_ONITEMLABEL 在与树项关联的标签上 TVHT_ONITEMRIGHT 在树项的右侧区域中 TVHT_ONITEMSTATEICON
在用户定义的状态图标上 TVHT_TOLEFT 在客户区域的左侧 TVHT_TORIGHT 在客户区域的右侧。
在用户定义的状态图标上 TVHT_TOLEFT 在客户区域的左侧 TVHT_TORIGHT 在客户区域的右侧。
http://cqyangyong.spaces.live.com/blog/cns!66BB503A2FB4C3F5!346.entry
http://www.vckbase.com/document/viewdoc/?id=1848
http://www.vckbase.com/document/listdoc.asp?mclsid=3&sclsid=321
12、列表控件使用方法
列表控制和视(List Control&View)主要用来以各种方式显示一组数据记录供用户进行各种操作,Windows98/95中资源管理器中的“查看”标签下的“大图标|小图标|列表|详细资源”就是一个非常好的典型应用。列表中的记录可以包括多个数据项,也可以包括表示数据内容的大小图标,用来表示数据记录的各种属性。
列表控制提供了对Windows列表功能操作的基本方法,而使用列表视的视函数可以对列表视进行各种操作,通过调用视成员GetListCtrl获取嵌在列表视内列表控制的引用(GetListCtrl& ctrlList = GetListCtrl()),就可以和列表控制一样进行各种操作。操作一个列表控制和视的基本方法为:创建列表控制;创建列表控制所需要的图像列表;向列表控制添加表列和表项;对列表进行各种控制,主要包括查找、排序、删除、显示方式、排列方式以及各种消息处理功能等;最后撤消列表控制。
对于一个列表控制,其最典型最常用的显示控制方式为:大图标方式(LVS_ICON)、小图标方式(LVS_SMALLICON)、列表显示方式(LVS_LIST)和详细资料(即报告LVS_REPORT)显示方式。这可以通过设置其显示方式属性来实现。要控制列表所在窗口的风格,可通过功能函数GetWindowLong和SetWindowLong来实现,要控制列表图标的对齐方式,可通过设置列表窗口的风格LVS_ALIGNTOP或LVS_ALIGNLEFT来实现,
1)、列表控制的建立方法
CListCtrl&listCtrl 定义列表对象的结构
Create 建立列表控制并绑定对象
列表控制CListCtrl::Create的调用格式如下:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
其中参数dwStyle用来确定列表控制的风格;rect用来确定列表控制的大小和位置;pParentWnd用来确定列表控制的父窗口,通常是一个对话框;nID用来确定列表控制的标识。其中列表控制的风格可以是下列值的组合:
LVS_ALIGNLEFT 用来确定表项的大小图标以左对齐方式显示;
LVS_ALIGNTOP 用来确定表项的大小图标以顶对齐方式显示;
LVS_AUTOARRANGE 用来确定表项的大小图标以自动排列方式显示;
LVS_EDITLABELS 设置表项文本可以编辑,父窗口必须设有LVN_ENDLABELEDIT风格;
LVS_ICON 用来确定大图标的显示方式;
LVS_LIST 用来确定列表方式显示;
LVS_NOCOLUMNHEADER 用来确定在详细资料方式时不显示列表头;
LVS_NOLABELWRAP 用来确定以单行方式显示图标的文本项;
LVS_NOSCROLL 用来屏蔽滚动条;
LVS_NOSORTHEADER 用来确定列表头不能用作按钮功能;
LVS_OWNERDRAWFIXED 在详细列表方式时允许自绘窗口;
LVS_REPORT 用来确定以详细资料即报告方式显示;
LVS_SHAREIMAGELISTS用来确定共享图像列表方式;
LVS_SHOWSELALWAYS 用来确定一直显示被选中表项方式;
LVS_SINGLESEL 用来确定在某一时刻只能有一项被选中;
LVS_SMALLICON 用来确定小图标显示方式;
LVS_SORTASCENDING 用来确定表项排序时是基于表项文本的升序方式;
LVS_SORTDESCENDING 用来确定表项排序时是基于表项文本的降序方式;
2)、列表控制的属性类
列表控制的属性类包括取得列表控制的背景色GetBkColor、设置列表控制的背景色SetBkColor、取得列表控制的图像列表GetImageList、设置列表控制的图像列表SetImageList、取得列表项数目GetItemCount、取得列表控制的属性GetItem、取得与表项相关的数据GetItemData、设置表项的属性SetItem、设置与表项相关的数值SetItemData、取得相关联的下一个表项GetNextItem、设置列表控制的文本颜色SetTextColor、取得列表控制的文本背景颜色GetTextBkColor、设置表项的最大数目SetItemCount和取得被选中表项的数目GetSelectedCount等。
3)、列表控制的操作方法
列表控制的操作方法包括插入一个新的表项InsertItem、删除一个表项DeleteItem、排序表项SortItems、测试列表的位置HitTest、重绘表项RedrawItems、插入一个表列InsertColumn、删除一个表列DeleteColumn、编辑一个表项文本EditLabel和重绘一个表项DrawItem等。
注:列表控制的数据结构
列表控制中包含两个非常重要的数据结构LV_ITEM和LV_COLUMN。LV_ITEM用于定义列表控制的一个表项,LV_COLUMN用于定义列表控制的一个表列,其定义格式分别为:
typedef struct _LV_ITEM {
UINT mask; //结构成员屏蔽位
int iItem; //表项索引号
int iSubItem; //子表项索引号
UINT state; //表项状态
UINT stateMask; //状态有效性屏蔽位
LPTSTR pszText; //表项名文本
int cchTextMax; //表项名最大长度
int iImage; // 表项图标的索引号
LPARAM lParam; // 与表项相关的32位数
} LV_ITEM;
typedef struct _LV_COLUMN {
UINT mask; //结构成员有效性屏蔽位
int fmt; //表列对齐方式
int cx; //表列的象素宽度
LPTSTR pszText; //表列的表头名
int cchTextMax; //表列名的文本长度
int iSubItem; //与表列关联的子表项索引号
} LV_COLUMN;
其中fmt可以取如下值:
LVCFMT_CENTER 表列居中对齐
LVCFMT_LEFT 表列左对齐
http://blog.csdn.net/ctbinzi/archive/2009/09/03/4510858.aspx
http://blog.csdn.net/sghgcn/archive/2009/03/05/3958219.aspx
http://www.vckbase.com/document/viewdoc/?id=1604
http://www.vckbase.com/document/listdoc.asp?mclsid=3&sclsid=323
13、进度条使用方法
http://www.yesky.com/33/1710533.shtml
http://www.vckbase.com/document/listdoc.asp?mclsid=3&sclsid=325
14、CScrollBar
http://xinny.bokee.com/1131543.html
http://download.csdn.net/source/1802602
15、数据表格控件DBGrid,FlexGrid使用方法
http://www.vckbase.com/document/listdoc.asp?mclsid=3&sclsid=327
16、cgridctrl详细说明
http://blog.csdn.net/byxdaz/archive/2008/06/19/2563142.aspx
17、CAnimateCtrl 动画控件
http://blog.csdn.net/huahuamoon/archive/2008/02/14/2095060.aspx
18、对话框
http://www.vckbase.com/document/listdoc.asp?mclsid=5&sclsid=501
19、单文档界面
http://www.vckbase.com/document/listdoc.asp?mclsid=5&sclsid=503
20、多文档界面
http://www.vckbase.com/document/listdoc.asp?mclsid=5&sclsid=505
21、视图分割与停靠
http://www.vckbase.com/document/listdoc.asp?mclsid=5&sclsid=507
22、高级用户界面
http://www.vckbase.com/document/listdoc.asp?mclsid=5&sclsid=510
23、VC模仿超炫QQ界面的实现
http://www.vckbase.com/document/viewdoc/?id=1841
24、CdialogBar
http://www.cppblog.com/tx7do/archive/2008/06/03/51926.html
25、CImageList控件
图像列表控制(CImageList)是相同大小图像的一个集合,每个集合中均以0为图像的索引序号基数,图像列表通常由大图标或位图构成,其中包含透明位图模式。可以利用Windows32位应用程序接口函数API来绘制、建立和删除图像,并能实现增加、删除、替换和拖动图像等操作。图像列表控制提供了控制图像列表的基本方法,这些方法在WINDOWS95及以后版本才能实现。
(一)图像控制的对象结构
1)、图像控制的数据成员
m_hImageList 连接图像对象的控制句柄
2)、图像控制的建立方法
CimageList&imageList建立图像控制对象结构
Create 初始化图像列表并绑定对象
图像控制的建立方法如下:
BOOL Create( int cx, int cy, UINT nFlags, int nInitial, int nGrow );
BOOL Create( UINT nBitmapID, int cx, int nGrow, COLORREF crMask );
BOOL Create( LPCTSTR lpszBitmapID, int cx, int nGrow, COLORREF crMask );
BOOL Create( CImageList& imagelist1, int nImage1, CImageList& imagelist2
,int nImage2,int dx, int dy );
其中各项参数的含义为:cx定义图像的宽度,单位为象素;cy定义图象的高度,单位为象素;nFlags确定建立图像列表的类型,可以是以下值的组合:ILC_COLOR、ILC_COLOR4、ILC_COLOR8、ILC_COLOR16、ILC_COLOR24、ILC_COLOR32、ILC_COLORDDB和ILC_MASK;nInitial用来确定图像列表包含的图像数量;nGrow用来确定图像列表可控制的图像数量。
NbitmapID 用来确定图像列表联系的位图标志值;crMask表示颜色屏蔽位;
LpszBitmapID 用来确定包含位图资源的标识串;
imagelist1 指向图像列表控制对象的一个指针;nImage1图像列表1中包含的图像数 量;imagelist2指向图像列表控制对象的一个指针;nImage2图像列表2中包含的图像数量;dx表示以象素为单位的图像宽度;dy表示以象素为单位的图像高度。
同样,图像控制的建立也包括两个步骤,首先建立图像列表结构,然后建立图像列表控制。
3)、图像控制的属性类
图像控制的属性类包括返回m_hImageList.控制句柄GetSafeHandle、取得图像列表中的图像数量GetImageCount、设置图像列表的背景颜色SetBkColor、取得图像列表的背景颜色SetBkColor和取得图像的有关信息SetBkColor。
4)、图像控制的操作方法
图像控制的操作方法包括将一个图像列表绑定到一个对象上Attach、将对象上的图像列表解除绑定并返回句柄Detach、删除一个图像列表DeleteImageList、将一个图像增加到图像列表中Add和将一个图像从图像列表中删除Remove等。
(二)图像控制的应用技巧
对于图像控制,同样不能单独使用,必须与列表控制、树控制和标签控制相互结合应用,下面分别介绍其具体应用技巧。
1)、图像控制在列表控制中的应用技巧
①设置图像控制CListCtrl::SetImageList的调用格式如下:
CImageList* SetImageList( CImageList* pImageList, int nImageList );
其返回值是指向前一个图像列表控制的一个指针,如果不存在前一个图像列表则为NULL;其中参数pImageList是指向图像列表的标识,nImageList是图像列表的类型,可以是如下值:
LVSIL_NORMAL 用大图标方式进行图像列表;
LVSIL_SMALL 用小图标方式进行图像列表;
LVSIL_STATE 以图像状态进行图像列表;
②取得图像控制CListCtrl::GetImageList的调用格式如下:
CImageList* GetImageList( int nImageList ) const;
其返回值为指向图像列表控制的指针,其中nImageList用来确定取得返回值的图像列表的 值,其取值与设置图像列表函数相同。
③图像控制在列表控制中的应用示例
CImageList Cil1,Cil2; //定义大小图标像列表
CVCLISTApp *pApp=(CVCLISTApp *)AfxGetApp();//取得列表控制程序
Cil1.Create(32,32,TRUE,2,2); //建立32位图像控制
Cil1.Add(pApp->LoadIcon(IDI_GJ));//增加选中状态图像
Cil1.Add(pApp->LoadIcon(IDI_XS));//增加非选中状态图像
Cil2.Create(16,16,TRUE,2,2); //建立16位图像控制
Cil2.Add(pApp->LoadIcon(IDI_GJ));//增加选中状态图像
Cil2.Add(pApp->LoadIcon(IDI_XS));//增加非选中状态图像
m_ListCtrl.SetImageList(&Cil1,LVSIL_NORMAL);//设置大图标控制
m_ListCtrl.SetImageList(&Cil2,LVSIL_SMALL);//设置小图标控制
2)、图像控制在树控制中的应用技巧
①设置图像控制CTreeCtrl::SetImageList的调用格式如下:
CImageList* SetImageList( CImageList * pImageList, int nImageListType );
其返回值为指向前前一个图像列表的指针,否则为NULL;参数pImageList为指向图像列表的标识,如果pImageList为NULL则所有的图像都将从树控制中被清除;nImageListType为图像列表设置的类型,可以是如下值之一:
TVSIL_NORMAL 设置正常图像列表,其中包括选中和非选中两种图标;
TVSIL_STATE 设置图像列表状态,指用户自定义状态;
②取得图像控制CTreeCtrl::GetImageList的调用格式如下:
CImageList* GetImageList( UINT nImage );
如果调用成功则返回图像列表控制指针,否则为NULL;nImage为取得返回值的图像列表类型,其取值和取得图像列表控制完全相同。
③图像控制在树控制中的应用示例
CImageList Cil1,Cil2;//定义大小图标像列表
CVCTREEApp *pApp=(CVCTREEApp *)AfxGetApp();//获取应用程序指针
Cil1.Create(16,16,ILC_COLOR,2,2);//建立图像控制
Cil1.Add(pApp->LoadIcon(IDI_PM));//增加选中状态图像
Cil1.Add(pApp->LoadIcon(IDI_CJ));//增加非选中状态图像
m_TreeCtrl.SetImageList(&Cil1,TVSIL_NORMAL);//设置图像控制列表
然后在树控制的结构定义中进行如下设置:
TCItem.item.iImage=0; //设置未选中图像索引号
TCItem.item.iSelectedImage=1;//设置选中时图像引号
3)、图像控制在标签控制中的应用技巧
①设置图像控制CTabCtrl::SetImageList的调用格式
CImageList * SetImageList( CImageList * pImageList );
其返回值为指向前一个图像列表的指针,如果不存在前一个图像列表则为NULL;pImageList为标识TAB控制的图像列表指针。
②取得图像控制CTabCtrl::GetImageList的调用格式
HIMAGELIST GetImageList() const;
其返回值为指向TAB控制的图像列表指针,如果调用不成功则为NULL。
26、CheaderCtrl表头控件
表头控件是一个有组织的排列成一行的标签序列,表头控件(CheaderCtrl)通常应用在窗口中的文本或数据的列表之上。一般为数据列的标题,可以包括多个部分(图标、文本、位图),用户可以拖动每个部分并可以控制每列的宽度。表头控件类提供了普通表头控件的基本方法,它一般与标签控件(CtabCtrl)和列表控件(ClistCtrl)配套使用。
1)、表头控件的建立
BOOL Create(DWORD dwStyle,const RECT & rect,CWnd *pParentWnd,UINT nID);
参数说明:
dwStyle,表示表头控件风格,它可以是下列值之一:
HDS_BUTTONS 表头控件外观类似按钮
HDS_HORZ 表头控件为水平排列
HDS_VERT 表头控件为垂直排列
HDS_HIDDEN 表头控件为隐藏模式
HDS_FILTERBAR 允许将过滤条作为表头控件的一部分,过滤条可以定制有选择地显示内容
HDS_FULLDRAG 在用户重新设定列宽时同样显示列内容
rect,表示表头控件的大小和位置
pParentWnd,表示表头控件的父窗口
nID, 表示表头控件的ID值
2)、表头控制的属性
表头控制的属性包括取得表头控制中项目的数量GetItemCount、取得表头控制中某一项目的内容GetItem和设置表头控制中某一项目的内容SetItem。
3)、表头控制的操作方法
表头控制的操作方法包括向表头控制中插入一个新项目InsertItem、从表头控制中删除一个项目DeleteItem和绘制表头中给定的项目DrawItem等。
注:表头控制的数据结构
在使用表头控制时,首先必须建立一个数据结构HD_ITEM,其结构定义如下:
typedef struct _HD_ITEM
{ UINT mask; //结构成员有效控制位
int cxy; //表头项目的宽度
LPSTR pszText; //表头项目内容
HBITMAP hbm; //表头项目的位置句柄
int cchTextMax; //表头内容字符串长度
int fmt; //表头项目的格式
LPARAM lParam; //应用程序定义的32位数据
} HD_ITEM;
屏蔽控制位说明了数据结构成员中包含的有效数据,可以是下面标志的组合:
HDI_BITMAP hbm成员有效
HDI_FORMAT fmt 成员有效
HDI_LPARAM lParam成员有效
HDI_TEXT pszText 和cchTextMax 成员有效
HDI_WIDTH cxy 成员有效并确定项目宽度值
格式标志位fmt可以是以下标志的组合:
HDF_CENTER 表头项目居中
HDF_LEFT 表头项目左对齐
HDF_RIGHT 表头项目右对齐
HDF_BITMAP 表头显示一个位图
HDF_OWNERDRAW 由主窗口自绘表头项目
HDF_STRING 表头项目为一个字符串
27、CFileDialog
CFileDialog文件选择对话框的使用
首先构造一个对象并提供相应的参数,构造函数原型如下:
CFileDialog::CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL );参数意义如下:
bOpenFileDialog 为TRUE则显示打开对话框,为FALSE则显示保存对话文件对话框。
lpszDefExt 指定默认的文件扩展名。
lpszFileName 指定默认的文件名。
dwFlags 指明一些特定风格。
lpszFilter 是最重要的一个参数,它指明可供选择的文件类型和相应的扩展名。参数格式如:
"Chart Files (*.xlc)|*.xlc|Worksheet Files (*.xls)|*.xls|Data Files (*.xlc;*.xls)|*.xlc; *.xls|All Files (*.*)|*.*||";文件类型说明和扩展名间用 | 分隔,同种类型文件的扩展名间可以用 ; 分割,每种文件类型间用 | 分隔,末尾用 || 指明。
pParentWnd 为父窗口指针。
创建文件对话框可以使用DoModal(),在返回后可以利用下面的函数得到用户选择:
CString CFileDialog::GetPathName( ) 得到完整的文件名,包括目录名和扩展名如:c:\test\test1.txt
CString CFileDialog::GetFileName( ) 得到完整的文件名,包括扩展名如:test1.txt
CString CFileDialog::GetExtName( ) 得到完整的文件扩展名,如:txt
CString CFileDialog::GetFileTitle ( ) 得到完整的文件名,不包括目录名和扩展名如:test1
POSITION CFileDialog::GetStartPosition( ) 对于选择了多个文件的情况得到第一个文件位置。
CString CFileDialog::GetNextPathName( POSITION& pos ) 对于选择了多个文件的情况得到下一个文件位置,并同时返回当前文件名。但必须已经调用过POSITION CFileDialog::GetStartPosition( )来得到最初的POSITION变量。
CColorDialog颜色选择对话框的使用
首先通过CColorDialog::CColorDialog( COLORREF clrInit = 0, DWORD dwFlags = 0, CWnd* pParentWnd = NULL )构造一个对象,其中clrInit为初始颜色。通过调用DoModal()创建对话框,在返回后调用COLORREF CColorDialog::GetColor( )得到用户选择的颜色值。
CFontDialog字体选择对话框的使用
首先构造一个对象并提供相应的参数,构造函数原型如下:
CFontDialog::CFontDialog( LPLOGFONT lplfInitial = NULL, DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS, CDC* pdcPrinter = NULL, CWnd* pParentWnd = NULL );构造一个对象,其中参数lplfInitial指向一个LOGFONG结构.如果该参数设置为NULL表示不设置初始字体。pdcPrinter指向一个代表打印机设备环境的DC对象,若设置该参数则选择的字体就为打印机所用。pParentWnd用于指定父窗口。通过调用DoModal()创建对话框,在返回后通过调用以下函数来得到用户选择:
void CFontDialog::GetCurrentFont( LPLOGFONT lplf );用来获得所选字体的属性。该函数有一个参数,该参数是指向LOGFONT结构的指针,函数将所选字体的各种属性写入这个LOGFONT结构中。
CString CFontDialog::GetFaceName( ) 得到所选字体名字。
int CFontDialog::GetSize( ) 得到所选字体的尺寸(以10个象素为单位)。
COLORREF CFontDialog::GetColor( ) 得到所选字体的颜色。
BOOL CFontDialog::IsStrikeOut( )
BOOL CFontDialog::IsUnderline( )
BOOL CFontDialog::IsBold( )
BOOL CFontDialog::IsItalic( )
得到所选字体的其他属性,是否有删除线,是否有下划线,是否为粗体,是否为斜体。
三、特效图像处理技术
1、 GDI在Windows中定义为Graphics Device Interface,即图形设备接口,是Windows API(Application Programming Interface)的一个重要组成部分。它是Windows图形显示程序与实际物理设备之间的桥梁,GDI使得用户无需关心具体设备的细节,而只需在一个虚拟的环境(即逻辑设备)中进行操作。
GDI、GDI+编程操作:
http://dev.yesky.com/255/2190255.shtml
2、DirectX
DirectX是一种图形应用程序接口(API),简单的说它是一个辅助软件,一个提高系统性能的加速软件,由微软创建开发的,微软将定义它为“硬件设备无关性”。Direct是直接的意思,X是很多东西,加在一起就是一组具有共性的东西,从内部原理探讨,也简单说来DirectX 就是一系列的 DLL (动态连接库),通过这些 DLL,开发者可以在无视于设备差异的情况下访问底层的硬件,DirectX 封装了一些 COM(Component Object Model)对象,这些 COM 对象为访问系统硬件提供了一个主要的接口。
DirectX并不是一个单纯的图形API,它是由微软公司开发的用途广泛的API,它包含有Direct Graphics(Direct 3D+Direct Draw)、Direct Input、Direct Play、Direct Sound、Direct Show、Direct Setup、Direct Media Objects等多个组件,它提供了一整套的多媒体接口方案。只是其在3D图形方面的优秀表现,让它的其它方面显得暗淡无光。DirectX开发之初是为了弥补Windows 3.1系统对图形、声音处理能力的不足,而今已发展成为对整个多媒体系统的各个方面都有决定性影响的接口。
下载地址:
Direct9.0c http://www.fs2you.com/files/9c976905-0cb9-11dd-9c0b-00142218fc6e/
DirectX 9.0C http://down1.tech.sina.com.cn/download/downContent/2004-03-16/363.shtml
参考书籍:《Visual C++ 6.0高级编程技术——DirectX篇》
3、OpenGL
OpenGL是近几年发展起来的一个性能卓越的三维图形标准,它是在SGI等多家世界闻名的计算机公司的倡导下,以SGI的GL三维图形库为基础制定的一个通用共享的开放式三维图形标准。目前,包括Microsoft、SGI、IBM、DEC、SUN、HP等大公司都采用了OpenGL做为三维图形标准,许多软件厂商也纷纷以OpenGL为基础开发出自己的产品,其中比较著名的产品包括动画制作软件Soft Image和3D Studio MAX、仿真软件Open Inventor、VR软件World Tool Kit、CAM软件ProEngineer、GIS软ARC/INFO等等。值得一提的是,随着Microsoft公司在Windows
NT和最新的Windows 95中提供了OpenGL标准及OpenGL三维图形加速卡的推出,OpenGL将在微机中有广泛地应用,同时也为广大用户提供了在微机上使用以前只能在高性能图形工作站上运行的各种软件的机会。
NT和最新的Windows 95中提供了OpenGL标准及OpenGL三维图形加速卡的推出,OpenGL将在微机中有广泛地应用,同时也为广大用户提供了在微机上使用以前只能在高性能图形工作站上运行的各种软件的机会。
OpenGL编程指南(第四版)(PDF)+源码 下载地址:
http://download.csdn.net/source/286881
四、Windows Shell编程
在Windows环境下,不论是使用Visual C++还是Delphi或是其他一些软件开发工具开发的应用程序,尽管存在着差别,但有一点是相同的:都是运行于Windows操作系统之下的。在程序开发过程中也经常要在自己的应用程序中加入一些Windows系统本身就有的功能,比如文件的拷贝、删除、查找以及运行程序等等。而这些功能在Windows操作系统下都是具备的,显然如果能直接从系统中调用这些功能将不仅仅减少程序的大小和开发人员的工作量,而且由于是直接通过操作系统来完成这些功能,将会大大减小这部分程序出现异常错误的概率。Windows系统虽说也存在不少错误,但常用功能的错误还是比较少的,而且通过补丁程序可以更低限度减少系统错误,因此程序员可以将调试检错的注意力放在应用程序的其他地方,对于调用系统功能这部分代码则可以不必投入太大的精力去调试,因为这部分调试的工作在操作系统发布的时候就已经由微软做好了。实际上我们可以通过这些COM接口来直接对Windows外壳进行编程。
具体shell编程可以参考《Windows Shell扩展编程完全指南》书籍,书中提供了shell编程技巧以及大量的实例。下载地址:http://download.csdn.net/source/182553
注意事项:
1、对于控件的背景色(位图),Windows是通过发送WM_ERASEEKGND消息给控件,让控件自己决定所使用的背景。对于控件的色彩,Windows是通过发送WM_CTLCOLOR消息给控件,同样也是通过控件自己决定所使用的色彩。
2、默认情况下,窗口是不响应WM_MOUSELEAVE和WM_MOUSEHOVER消息的,所以要使用_TrackMouseEvent函数来激活这两个消息。调用这个函数后,当鼠标在指定窗口上停留超过一定时间或离开窗口后,该函数会Post这两个消息到指定窗口。
使用方法:
1).在对话框类中定义一个变量来标识是否追踪当前鼠标状态,之所以要这样定义是要避免鼠标已经在窗体之上时,一移动鼠标就不断重复产生WM_MOUSEHOVER。
BOOL _bMouseTrack=TRUE;
2).在OnMouseMove中调用_TrackMouseEvent函数
if (_bMouseTrack) //若允许追踪,则。
{
TRACKMOUSEEVENT csTME;
csTME.cbSize = sizeof(csTME);
csTME.dwFlags = TME_LEAVE|TME_HOVER;
csTME.hwndTrack = m_hWnd;//指定要追踪的窗口
csTME.dwHoverTime = 10; //鼠标在按钮上停留超过10ms,才认为状态为HOVER
::_TrackMouseEvent(&csTME); //开启Windows的WM_MOUSELEAVE,WM_MOUSEHOVER事件支持
_bMouseTrack=FALSE; //若已经追踪,则停止追踪
}
3).在OnMouseLeave中再次允许追踪鼠标状态
_bMouseTrack=TRUE;
4).备注:这两个消息的映射要自己写
ON_MESSAGE(WM_MOUSEHOVER,OnMouseHover)
ON_MESSAGE(WM_MOUSELEAVE,OnMouseLeave)
3、子类化
子类化函数的参数说明:
BOOL SubclassDlgItem( UINT nID, CWnd* pParent);将一个 Windows 控件与 CWnd 或 CWnd 派生类的对象连接,然后使它通过 CWnd 或 CWnd 派生类的消息映射转发消息。其中nID为该控件的ID,pParent为控件的父窗口。
BOOL SubclassWindow( HWND hWnd );作用同SubclassDlgItem,只是该函数通过创后的句柄来完成子类化操作。hWnd为需要子类化的窗口句柄 HWND
UnsubclassWindow();反子类化,该函数使窗口与子类化所连接的类脱离,使用该控件窗口默认的消息处理函数WndProc来处理。函数返回取消子类化的窗口句柄。
4、使用Rich Edit控件前一定要用AfxInitRichEdit()初始化RichEdit环境。
5、一般画Windows控件的过程分为三大部分:
第一:在WM_MEASUREITEM消息影射函数中设置当前要画的Item的大小尺寸;创建控件。(一般自绘控件都响应WM_MEASUREITEM和WM_DRAWITEM消息)
第二:在WM_DRAWITEM消息影射函数中根据Item的大小尺寸来画该Item(图标/位图/字符串等);
第三:在WM_PAINT消息映射函数中不断的绘制当前的控件内容。
6、使用内存DC防止窗口闪烁
在使用VC开发图形相关的应用程序时,常常需要使用MFC的CDC类直接把图形画在窗口上。这通常是通过响应Windows的WM_PAINT消息实现的。如果要画的图形比较复杂,或者比较大,那么画图过程可能会造成窗口的闪烁。当窗口调整大小时,这种闪烁由为明显。
解决窗口闪烁问题的有效办法就是使用内存DC,也称为缓冲DC。在内存中准备一个和窗口DC相同属性的DC,在这个内存DC上执行画图操作。完成画图以后,把画图输出的内容整体复制到目标窗口DC上。因为画图操作不在窗口DC上进行,所以在画图的过程中窗口可以保持原来的内容。当画好的内容被复制到窗口DC时,因为复制操作执行的非常快,所以用户感觉窗口仿佛被立刻被画好,从而消除了从旧画面到白板再到新画面的闪烁现象。
生成内存DC主要用到以下四个函数:
CreateCompatibleDC(CDC* pDC )。CDC类的成员函数,用于创建一个和pDC指向的DC兼容的内存DC。
CreateDiscardableBitmap( CDC* pDC, int nWidth, int nHeight)。CBitmap类的成员函数,用于按指定尺寸创建一个和pDC指向的DC兼容的位图。
SelectObject(CBitmap * pBitmap)。CDC类的成员函数,执行以后,所以在该DC上的图像输出都将被画到pBitmap指向的位图上。
BOOL BitBlt (int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop )。CDC类的成员函数,用于从源DC(pSrcDC)复制一个矩形的图象到当前DC中。
对于一个窗口,我们可以用下面的代码来创建内存DC,在内存DC上输出,并最终复制到窗口DC上。
void PaintWnd(CWnd * pWnd)
...{
CDC * pWndDC = pWnd->GetWindowDC();
CRect WndRect = pWnd->GetWindowRect();
CDC MemDC;
CBitMap MemBitmap;
MemDC.CreateCompatibleDC(pWndDC); // 创建内存DC
MemBitmap.CreateCompatibleBitmap( // 创建兼容的位图
pWndDC,
WndRect.Width(),
WndRect.Height());
MemDC.SelectObject(MemBitmap); // 让内存DC输出到位图
// 使用MemDC画图
// 。。。。。。
pWndDC->BitBlt( // 从内存DC复制到窗口DC
0,0,
WndRect.Width(),
WndRect.Height(),
&MemDC,
0,0,
SRCCOPY);
}
当然,实际的情况下,我们需要考虑的更多,因为内存DC、位图的创建都可能会失败。为了简化代码,笔者定义了一个类CMemoryDC,包装了内存DC创建过程中的出错处理,内存DC的事后清理等操作,并自动复制内存DC的内容到目标DC上。