工作需要用到MFC,需要能快速上手,中间碰到不懂的简单的看了下源码,参考了些资料。
目标:做一个简单的计算器,代码就不考虑了,主要强调如何上手MFC,和简单了解MFC的框架。
1.如何创建一个MFC工程项目
创建MFC的过程如下:(visual studio 2012)
1>.新建->项目:选择MFC应用程序,名称这里用test(随意,和后面代码那里一致)。然后点确定。
2>.出现MFC生成向导:这里选择基于对话框,其他默认。
2.界面设计方式
2.1.拖拉控件及修改空间属性
1>.界面设计主要是在这个资源文件中修改。
2>.控件在工具箱中拖拽出来放到界面上。
3>.修改控件属性
单击控件后,可以在属性中修改控件的属性。
常用的属性:
Caption 标题
ID 控件标识
2.2.修改控件布局
这个没查资料,感觉可以设置布局。
界面的左边和上方能控制水平和垂直方向上的自动对齐。
可以在设置好位置后,将控件移动对齐到这个方向的位置,后面拖动这个位置的坐标就可以进行整体对齐移动了。
3.控件的事件回调函数处理
双击控件,可自动跳转到点击控件的事件回调处理函数。
可以在跳转到的函数回调上编写处理代码:
void CtestDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString str = NULL;
GetDlgItemText(IDC_EDIT1, str);
SetDlgItemText(IDC_EDIT1, str+_T("1"));
}
这里在界面上除自身的代码外比较常用的就是这些属性相关(设置和获取)的函数。
这部分可以通过搜索引擎或者MSDN解决。
4.粗略分析自动生成的代码
4.1.关于自动生成的几个类
这里可能涉及到框架的部分程序了,这里做简单的了解。
首先最简单的Dialog中间有这几个类:CAboutDlg,CtestApp,CtestDlg。
其中CAboutDlg类和CtestDlg类被放到了testDlg.cpp中实现。
1>.CAboutDlg
CAboutDlg是用于应用程序“关于”菜单项的 CAboutDlg 对话框。估计是这个关于的对话框。在.rc的资源中的dialog中的IDD_ABOUTBOX中可修改。。
这部分是CAboutDlg的代码,可以看出一个简单的对话框窗口:
1.继承CDialogEx类;
2.有自己的映射关系;
3.DoDataExchange函数;这个函数在MFC框架中的UpdateData会调用。
4.类中声明的IDD枚举???
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
enum { IDD = IDD_ABOUTBOX };// 对话框数据
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD){}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
2>.CtestDlg
所以CtestDlg应该是主界面的代码。结构和上述类似。只是增加了几个消息回调函数。但中间有结果回调函数比较特殊。应该是和后面几个消息回调连着的,可能不需要给用户自定义,连名字都省略了= =
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
注:afx_msg 是个标识,来表示MFC的消息处理
OnQueryDragIcon从注释上意思:当用户拖动最小化窗口时系统调用此函数取得光标
OnPaint从注释上意思:来绘制该图标。对于使用文档/视图模型的 MFC 应用程序。在初始化后会向窗口发送WM_PAINT消息,然后框架自动回调。这部分的代码也是由框架封装好的CDialogEx::OnPaint(),可以跳转进去看看,用来绘制图形的。
OnSysCommand不详。。。
另外:BOOL CtestDlg::OnInitDialog()是初始化消息的回调函数。用来初始化窗口。
3>.CtestApp
CtestApp类在test.h/test.c中,继承了CWinApp,定义应用程序的类行为,且有唯一一个对象theApp,在一开始就会被构造。
这个类中的构造函数添加了支持重新启动管理器的flag。
另一个方法是初始化实例InitInstance。
这里需要提到MFC框架的入口和初始化过程:
theApp在一开始就被构造,初始化了一些变量,然后将当前应用指到本身。
MFC的入口在源代码的APPMODUL.CPP文件中的_tWinMain函数中。_tWinMain中调用了AfxWinMain函数。
AfxWinMain为一个MFC框架的
全局函数
。在这个函数中模仿了WIN32的创建一个应用的过程:
1.获得一个CWinThread和app的指针(CWinApp也是继承CWinThread的,通过宏可知这两个指针一样。)
2.通过APP指针初始化应用(InitApplication),初始化CWinThread指针的实例(InitInstance方法)。
InitApplication做框架的内部管理工作。
这里InitInstance方法就调用CtestApp重写的部分(这部分代码其实也是系统自动生成的)。这个函数完成了窗口CtestDlg创建,显示,并向窗口发送WM_PAINT消息的功能。
注:这里好像不同的写法(使用方式)中间的调用过程也不大一样。如果是前面那种方式在DoModal里就调用了DoDataExchange函数来加载资源??
3.运行theApp
4.2.关于函数回调部分的代码
前面说双击回调后,会自动跳转生成的代码。这里自动生成的部分主要有:
回调处理函数:
void CtestDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
}
关系映射:
BEGIN_MESSAGE_MAP(CtestDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &CtestDlg::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, &CtestDlg::OnBnClickedButton2)
ON_BN_CLICKED(IDC_BUTTON3, &CtestDlg::OnBnClickedButton3)
ON_BN_CLICKED(IDC_BUTTON4, &CtestDlg::OnBnClickedButton4)
ON_BN_CLICKED(IDC_BUTTON5, &CtestDlg::OnBnClickedButton5)
ON_BN_CLICKED(IDC_BUTTON6, &CtestDlg::OnBnClickedButton6)
ON_BN_CLICKED(IDC_BUTTON7, &CtestDlg::OnBnClickedButton7)
ON_BN_CLICKED(IDC_BUTTON8, &CtestDlg::OnBnClickedButton8)
ON_BN_CLICKED(IDC_BUTTON9, &CtestDlg::OnBnClickedButton9)
ON_BN_CLICKED(IDC_BUTTON10, &CtestDlg::OnBnClickedButton10)
END_MESSAGE_MAP()
声明:
afx_msg void OnEnChangeEdit1();
调到宏里面看看原理:(截取了一段button的)
// User Button Notification Codes
#define ON_BN_CLICKED(id, memberFxn) \
ON_CONTROL(BN_CLICKED, id, memberFxn)
#define ON_BN_DOUBLECLICKED(id, memberFxn) \
ON_CONTROL(BN_DOUBLECLICKED, id, memberFxn)
#define ON_BN_SETFOCUS(id, memberFxn) \
ON_CONTROL(BN_SETFOCUS, id, memberFxn)
#define ON_BN_KILLFOCUS(id, memberFxn) \
ON_CONTROL(BN_KILLFOCUS, id, memberFxn)
定义了几种动作的宏(每种控件间会有差异),将控件标识和对应的动作类型和对应的响应回调函数连接起来。宏里面的结构如下,看上去像一个表的形式。
// for general controls
#define ON_CONTROL(wNotifyCode, id, memberFxn) \
{ WM_COMMAND, (WORD)wNotifyCode, (WORD)id, (WORD)id, AfxSigCmd_v, \
(static_cast< AFX_PMSG > (memberFxn)) },
5.其他注意事项
5.1.关于命名的问题
命名的规则:
5.2.MFC自定义的类型
第一次用MFC中,出现了一些类型,比如CString,可以在afx.h头文件中查看它的定义。另外使用MSDN可以查看。
另外:这里要注意字符集的问题。可以用_T("XXXX")来统一字符集。
5.3.窗口中定义的控件
拖进界面生成的控件的资源被,按照一定的组织格式放在了test.rc这个文件中。MFC框架中在CtestApp::InitInstance()里就加载这个.rc的文件的部分并进行了显示。
注*:这里的控件也可在窗口创建的回调函数中创建。只不过这里的方法不同。
5.4.关于一些辅助的宏
看到了随便加进来一下:
①.
TRACE
TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");
追踪路径,先看TRACE的定义:
#define TRACE ATLTRACE
#define ATLTRACE ATL::CTraceFileAndLineInfo(__FILE__, __LINE__)
void __cdecl operator()(
_In_ DWORD_PTR dwCategory,
_In_ UINT nLevel,
_In_z_ const char *pszFmt,
...) const
{
va_list ptr; va_start(ptr, pszFmt);
ATL::CTrace::s_trace.TraceV(m_pszFileName, m_nLineNo, dwCategory, nLevel, pszFmt, ptr);
va_end(ptr);
}
再往下好像封装进去了,大概可以通过这个宏,可以把追踪信息文件,行数,必要的提示,警告等级之类的都显示下来。
TRACE宏只对Debug 版本的工程产生作用。
②.ASSERT
编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是
异常处理
的一种高级形式。断言表示为一些
布尔表达式
,程序员相信在程序中的某个特定点该表达式值为真。可以在任何时候启用和禁用断言验证,
因此可以在测试时启用断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。
看看MFC中的ASSERT。
#define ASSERT(f) DEBUG_ONLY((void) ((f) || !::AfxAssertFailedLine(THIS_FILE, __LINE__) || (AfxDebugBreak(), 0)))
#define DEBUG_ONLY(f) (f)
相当于DEBUG模式下,直接判断ASSERT中的表达式f是否满足条件。如果满足表达式满足,就会调用AfxAssertFailedLine,然后中断退出。
6.相关参考
VC++深入讲解
7.总结
在大致看完之后,MFC其实就是Windows下的API进行进一步的封装。如果直接在.rc文件中设计,只需添加对应的回调函数再加处理即可。这样比较快。
另外一种是对MFC提供出来的框架的部分进行重写(根据自己需求),但大体上也是封装Windows的API的过程,这样会相对麻烦一些。