MFC项目实战(1)文件管理器--准备篇
本程序主要实现如下功能:
程序通过左边的树形控件显示本地计算机中目录的结构,右边的列表控件则负责响应树形控件中选择的目录节点并把此节点中的所有项在列表框中显示出来,列表框支持奇偶行颜色设置,选中颜色设置和热点颜色设置;
本程序支持显示或不显示隐藏文件和系统文件,支持文件的重命名、打开、复制、移动和删除操作,删除操作支持删除到回收站和彻底删除选择。
同时支持地址栏跳转功能。支持属性栏的时间显示
程序主界面:
1. 模式与无模式对话框
本程序是基于对话框而设计的,对话框分为模式对话框和无模式对话框。对于模式对话框,当该对话框在关闭之前,用户不能够在同一应用程序中进行其它的操作,这类对话框如“打开文件”对话框等;而对于无模式对话框,当该对话框显示后用户可以进行其它的操作,这类对话框仅仅是一个弹出窗口,如“替换查找”对话框等。
新建一个MFC的Demo对话框程序,默认设置,Finished 完成新建。然后在资源视图中再新建两个Dialog,右键这新建的Dialog 把它们的Caption,改为ModeDlg 和NonModeDlg 把ID号分别改为IDD_MODEDLG 和IDD_NONMODEDLG ,改好后,ModeDlg 和NonModeDlg对话框模版就新建好了,那么如何来使用它们呢?若要在这些新建的对话框上进行消息响应,就要为这些对话框模版生成一个CDialog派生类才行,那么如何为ModeDlg和NonModeDlg生成它们的派生类呢?在ModeDlg对话框点击右键,选择类向导(Class Wizard)
,然后点击添加类(Add class)新建一个CModeDlg类(即在弹出的MFC类向导对话中,在对话框的“类名”栏中输入CModeDlg,并选择该类的基类为CDialog类。单击“完成”按钮以新建CModeDlg类。)如图:
同样,在NonModeDlg对话框中右键,选择类向导(Class Wizard)然后添加一个CNonModeDlg类。然后打开IDD_DEMO_DIALOG对话框,在它上面添加两个按钮,分别命名为ModeDlg和NonModeDlg ,同时这两个按钮的ID号分别换成IDC_MDlg ,IDC_NMDlg。然后右键点击ModeDlg按钮选择添加事件句柄(Add Event Handler...),在弹出的事件句柄向导对话框中,为按钮选择单击事件,所以消息类型选择BN_CLICKED,按钮是在IDD_DEMO_DIALOG对话框上,所以它的消息事件应该建立在CDemoDlg这个类里面,最后函数句柄的名称默认即可。具体如图:
单击添加并编辑以新建ModeDlg按钮响应函数OnBnClickedMdlg,以同样的方式为NonModeDlg按钮在CDemoDlg类中添加BN_CLICKED事件句柄函数OnBnClickedNmdlg,
为了在CDemoDlg类中使用ModeDlg对话框和NonModeDlg的类,首先需要将它们的头文件包含到DemoDlg.h文件中,即在DemoDlg.h文件添加:
#include"ModeDlg.h"
#include"NonModeDlg.h"
然后回到DemoDlg.cpp文件,在刚添加的ModeDlg按钮响应函数中,创建CModeDlg类的实例,并调用DoModal函数显示对话框,代码如下:
void CDemoDlg::OnBnClickedMdlg()
{
CModeDlg dlg; //创建CModeDlg类实例dlg
dlg.DoModal(); //调用DoModal()函数显示对话框
}
下面在添加的NonModeDlg按钮函数OnBnClickedNmdlg()来创建并显示无模式对话框,创建无模式对话框,首先生成一个CNonModeDlg类的实例,然后调用Create()函数来创建无模式对话框,最后调用ShowWindow()来显示无模式对话框,添加代码如下:
void CDemoDlg::OnBnClickedNmdlg()
{
CNonModeDlg *pDlg = new CNonModeDlg();// 创建一个CNonModeDlg类的实例
pDlg->Create(IDD_NONMODEDLG,this); // 创建无模式对话框
pDlg->CenterWindow(); //对话框居中显示
pDlg->ShowWindow(SW_SHOW); //显示对话框
}
注意Create()函数中的第一个参数是无模式对话框的ID号,当编写完
pDlg->Create(IDD_NONMODEDLG,this); 这行代码后,提示IDD_NONMODEDLG 没有定义,这是因为没有包含Resouce.h头文件,导致它找不到这个ID号,所以在DemoDlg.cpp中包含这个头文件即可。
还需要注意的是,关闭调用DoModal()函数创建的模式对话框时,系统会自动回收模式对话框申请的资源,但是当关闭无模式对话框时,系统不会自动回收和释放创建无模式对话框时所申请的资源,所以在无模式对话框被关闭时需要手动回收它创建时申请的资源,否则会造成内存泄露!那么如何手动来回收它申请的资源呢?
在“类视图“窗口中选择CNonModeDlg类,并单击属性窗口中的属性按钮,然后重写PostNcDestroy函数。在该函数中添加一条delete语句以销毁对话框,回收资源,代码如下:
void CNonModeDlg::PostNcDestroy()
{
delete this;
CDialog::PostNcDestroy();
}
下面介绍一下本程序需要程序的控件,在后面的程序中用到时再做更详细的使用说明。
控件名称 |
MFC类 |
说明 |
静态控件 |
CStatic |
用于显示一些固定不变的文字或图形 |
按钮 |
CButton |
用于产生某些命令消息,包括单选,复选和按键按钮 |
编辑框 |
CEdit |
用于完成文本和数字的输入和编辑 |
列表框 |
CListBox |
显示一个列表,让用户从中选取一个或多个项 |
组合框 |
CComboBox |
是一个列表框和编辑框组合的控件 |
列表控件 |
CListCtrl |
包含图标,标签显示内容的控件 |
树形控件 |
CTreeCtrl |
以树结构形式显示一组信息,能够反映这些信息的结构 |
默认情况下,所有静态控件的ID标识符都是IDC_STATIC,其表示是静态的并且显示的内容不能够改变,不能够为这类控件添加成员变量,消息响应函数等。如果需要动态地改变静态控件上的显示,则需要为这些控件重新指定一个非IDC_STATIC的ID标识符,然后才能在程序中为静态控件添加成员变量,消息响应函数等。
可以调用CWnd类的GetDlgItem成员函数来获取该控件的CWnd指针,或者通过调用GetDlgItemText或SetDlgItemText函数来改变静态控件显示的文本内容。
常见的按钮主要有三种类型:按键按钮、单选按钮和复选按钮。一般而言,需要为按钮添加单击事件BN_CLICKED消息的消息响应函数。同时可以调用CButton类的成员函数SetCheck和GetCheck分别来设置或获取指定按钮的选中状态,原型如下:
void SetCheck(int nCheck);
int GetCheck() const;
其中nCheck和GetCheck函数返回的值可是:0表示不选中,1表示选中,2表示不确定(仅用于三态按钮)。对于同组多个单选按钮的选中状态的设置或获取,需要调用窗口类Cwnd的成员函数CheckRadioButton 和GetCheckedRadioButton ,原型如下:
void CheckRadioButton (int nIDFirstButton, int nIDLastButton,int nIDCheckButton);
int GetCheckedRadioButton(int nIDFirstButton,int nIDLastButton);
其中nIDFirstButton 和nIDLastButton 分别指定同组单选按钮的第一个和最后一个按钮的ID值;nIDCheckButton 用来指定要设置选中状态的按钮的ID值;函数GetCheckedRadioButton返回被选中的按钮ID值。
在MFC中提供支持位图按钮的CBitmapButton类,它是CButton类的派生类,最多可以为位图按钮指定按钮的正常、按下、获得焦点和被禁用的四副状态位图。
在创建位图按钮时,首先需要为按钮定义CBitmapButton类型的变量,然后调用CWnd类的成员函数SubclassDlgItem将变量关联到按钮控件上,再调用该类的的成员函数LoadBitmaps为按钮加载相应状态的位图,最后还可以调用SizeToContent函数来调整按钮的大小以适合位图。这些函数的声明如下:
BOOL SubclassDlgItem(UINT ID,CWnd* pParent);
BOOL LoadBitmaps(LPCTSTR lpszBitmapResource,
LPCTSTR lpszBitmapResourceSel = NULL,
LPCTSTR lpszBitmapResourceFocus = NULL,
LPCTSTR lpszBitmapResourceDisabled =NULL);
BOOL LoadBitmaps(UINT nIDBitmapResource,
UINT nIDBitmapResourceSel = 0,
UINT nIDBitmapResourceFocus = 0,
UINT nIDBitmapResourceDisabled = 0);
在创建位图按钮时,需要设置按钮的OwnerDraw属性为TRUE.
编辑框,作为一个让用户从键盘输入和编辑文本的矩形框,当文本被修改或呗滚动时,会向父窗口发送一些消息,由此可以在属性窗口中为编辑框控件添加这些消息的响应函数。
消息列表:
EN_CHANGE 当编辑框中的文本已被修改,在新的文本显示之后发送此消息
EN_HSCROLL 当编辑框的水平滚动条被使用,在更新显示之前发送此消息
EN_KILLFOCUS 编辑框失去键盘输入焦点时发送此消息
EN_MAXTEXT 文本数目达到了限制值时发送此消息
EN_SETFOCUS 编辑框得到键盘输入焦点时发送此消息
EN_UPDATE 编辑框的文本已被修改,新的文本显示之前发送此消息
EN_VSCROLL 当编辑框的垂直滚动条被使用,在更新显示之前发送此消息
当编辑框显示多行文本内容时,通常需要使用LineScroll 成员函数来滚动显示文本,使用GetLineCount来获取多行文本的行数。
int GetLineCount() const;
void LineScroll( int nLine, int nChars = 0);
其中, nLine 指定了纵向滚动的行数,nChars指定了水平滚动的字符数。但是如果指定了编辑框的对齐属性,则nChars值无效。
列表框可分为单选,多选,扩展多选以及非选择四种类型,默认风格下的单选列表框让用户一次只能选择一个项;多选列表框则一次可以选择多个项,而扩展多选列表框则充许用户用鼠标拖动或其他特殊组合键进行选择;非选列表框则不提供选择功能。
当列表框创建之后,往往要添加、删除、修改或获取列表框中的列表项,这些操作都可调用MFC的CListBox类成员函数来实现。此外,列表框的项除了用字符串来标识外,还常常通过索引来确定。索引表明项目在列表框中排列的位置,它是以0为基准,即列表框中的第一项的索引是0,第二项为1等。列表框控件类CListBox常用的成员函数如下:
AddString 向列表框中添加一个字符串
InsertString 在列表框中指定位置插入一个字符串
DeleteString 从列表框中删除一个字符串
ResetContent 清空列表框所有入口
FindString 在列表框中删除一个字符串
GetCurSel 返回列表框中当前选择项的索引
SetCurSel 选择一个列表框字符串
GetText 从列表框中获取某项的字符串
组合框是一个列表框和编辑框组合的控件。组合框大致可分为两类,一类是对组合框中的列表框进行操作,另一类是对组合框中的编辑框进行操作,这些操作都可以调用CComboBox成员函数来实现,常用的成员函数如下:
AddString 在组合框中列表框的列表末尾添加一个字符串
DeleteString 删除组合框中列表框中的一个字符串
InsertString 在组合框的列表框中插入一个字符串
ResetContent 删除组合框中列表框和编辑框中的所有项
FindString 在组合框的列表框中查找具有指定前缀的第一个字符串
GetCurSel 取得组合框的列表框中当前选中项的索引
SetCurSel 在组合框的列表框中选择一个字符串
GetLBText 从组合框的列表框中取得第一个字符串
GetCount 取得组合框的项数
最后再来谈谈DDX和DDV技术
MFC提供的DDX和DDV技术使用户方便地对控件中的数据进行操作。DDX将数据成员变量与对话框中的控件相关联,使得数据在控件之间或者控件与对话框之间能够直接交换;DDV用于对控件中的数据进行校验,以保证数据的合法性和有效性。
在为对话框资源创建对话框类时,MFC类向导将会自动为对话框类中添加数据交换函数DoDataExchange, 并在该函数中添加相应的数据交换函数或数据校验函数。
需要注意的是,用户不能直接调用DoDataExchange 函数,数据交换和校验是由MFC负责处理的。数据交换和校验机制都是由MFC框架实现的,MFC通过一组DDX交换函数和DDV验证函数来实现控件数据的交换和数据的验证。根据不同的控件类型,MFC提供了不同的数据交换函数,它们都是以DDX_作为前缀的。同样数据校验函数也都是以DDV_作为前缀,这些函数都不需要用户自己添加,MFC会自动添加的。
需要注意的是: DoDataExchange 数据交换和数据校验函数都具有双向性。当为控件定义了数据变量后,可以通过CWnd::UpdateData函数实现对控件数据的输入和读取。当以TRUE参数调用UpdateData函数时,控件中的数据被保存到相关联的成员变量中;如果以FALSE为参数调用UpdateData函数时,则成员变量的值将被传递到控件上。