微软基础类MFC

(不定期更新内容)
内容:
1. MFC的类层次结构
2. MFC应用程序框架
3. 应用程序分析
4. 消息机制(与WIN32差不多)
5. 消息映射和消息传递
6. 自定义消息
7. 模态对话框和非模态对话框
介绍MFC:
MFC全称微软基础类MFC,是以C++类的形式封装了Windows的API,提供应用程序框架和开发应用的工具,用消息映射处理消息响应,简化开发工作,提高效率。
一.MFC的类层次结构
分为两大类:基础类和宏、全局变量、全局函数
二.MFC应用程序框架
三.应用程序分析
应用程序向导生成类
假设建立一个工程名为:CXb的单文档MFC(exe)的工程
MFC会自动为你准备如下类:Cxb_App、CMainFrame、CXb_View、CXb_Doc,其中,App继承CWinApp类,创建MFC应用程序就要用到这个基类,因为CWinApp不仅代表了程序中运行的主线程,而且代表了应用程序本身,在任何MFC应用程序中只有一个CWinApp对象,它用于应用程序的初始化、启动应用程序、运行主消息循环、终止应用程序。Cxb_App这个类隐藏了程序最开始执行的WinMain()函数(封装于CWinApp类),这个函数相当于C、C++、Java等语言的main()函数。
Cxp_App有一个InitInstance()该函数重载了CWinApp的同名函数,用于初始化工作,MFC应用程序启动时,首先创建应用程序对象theApp,创建对象时应用程序调用构造函数初始化theApp,然后由应用程序框架调用MFC提供的AfxWinMain()主函数,在WinMain()函数中利用AfxGetApp()获取theApp的指针pApp,然后通过pApp指针调用InitInstance()函数初始化应用程序,在WinMain()函数中还构造了文档模板、产生最初的文档、视图和主框架窗口,并生成工具栏和状态栏。当InitInstance执行完后,执行Run()函数进入消息循环,直到Run()受到一个叫WM_QUIT的命令消息后,会经历先调用CWinApp成员函数ExitInstance()用于处理收尾操作,可重载该函数如:用于释放内存,然后调用静态对象的析构函数,包括CWinApp对象,最后退出应用程序,将控制权交还给操作系统。
CMainFrame类是由MFC中的CFrameWnd类派生来的,是一个框架窗口,负责标题栏、菜单栏、工具栏、以及状态栏的生成。由于视图是覆盖在框架窗口之上的,因此并不需要经常对CMainFrame类进行操作,更多的是对视窗类进行操作。
应用程序框架通过主框架窗口与视图对象、文档对象利用消息进行互操作,如主框架窗口访问视图对象的方法是调用GetActiveView()成员函数,而访问文档对象的方法是调用GetActiveDocument().
Cxb_View是视图类,由CView派生,视图是显示文档数据的界面,它主要占据着主框架窗口的客户区,视图类可以把程序文档数据显示出来,还可以接受用户输入、编辑,视图对象也通过消息与主框架、文档对象进行互操作。
Cxb_Doc是文档类,由CDocument派生,文档对象是存储程序数据的地方。
视图是显示数据,数据存放在文档中,一个文档可同时具有有多个视图,文档和视图分割开能对不同文档分别进行处理。

四.消息机制
MFC基于消息驱动,类似于WIN32的方法,消息处理过程是定义在MFC的类和宏中,调用方法相当于switch-case语句
有四种消息:Windows消息、命令消息、控件通知消息、自定义消息。
Window消息:
WM_前缀的都为Windows消息(除WM_COMMAND之外)
由窗口和视图处理。
鼠标消息:(发生事件后产生影响)
单击 、双击、拖动、
一般形式:

    afx_msg void OnLButtonDown(UINT nFlags,CPoint point);

鼠标消息参数nFlags取值以及含义
MK_CONTROL Ctrl被按下 MK_RBUTTON 右键按下
MK_LBUTTON 左键按下 MK_SHIFT Shift键按下
MK_MBUTTON 中间键按下

窗口消息:(发生事件前产生)
WM_CREATE 窗口被创建
WM_DESTROY 窗口被销毁
WM_CLOSE 窗口被关闭
WM_MOVE 窗口发生移动( 位移 )
WM_PAINT 窗口发生重绘
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnClose();
afx_msg void OnMove(int x,int y);
afx_msg void OnSize(UINT nType,int cx,int cy);
afx_msg void OnPaint();消息WM_TIMER

计时器消息:
周期性消息产生装置,想使用这个WM_TIMER 必须先设置一个SetTimer函数,否则不会调用计时器消息响应函数,WM_TIMER相应函数是
OnTimer原型:
afx_msg void OnTimer(UINT nIDEvent);
UINT SetTimer(UINT nIDEvent, UINT nElapse,void (CALLBACK EXPORT * lpfnTimer)(HWND,UINT,UINT,DWORD));
nIDEvent代表计时器标志,nElapse代表时间间隔,以毫秒为单位,lpfnTimer指定定时器消息处理函数,通常为NULL,表示以OnTimer为计时器消息处理函数,也可以重载该函数.

控件通知消息:
控件通知包含从控件和其他子窗口传递给父窗口的WM_COMMAND通知消息。由窗口和视图进行处理,特殊:单击控件按钮时发出的BN_CLICKED控件通知将作为命令消息来处理。

命令消息:
命令消息包括来自用户界面对象的WM_COMMAND通知消息,菜单项、工具栏按钮和加速键(快捷键)都是可以产生命令的用户界面对象。与其他消息不同的是,它将用户界面对象和命令联系在一起,是被作为特殊的消息来处理。例如:Edit菜单中的Copy命令就可以用ID_EDIT_COPY来表示,MFC类库预定义某些命令(如ID_EDIT_PASTEID_FILE_OPEN等),参见AFXRES.H文件.
命令消息可被更广泛的对象处理,如:文档、文档模板、应用程序对象、窗口和视图等。Windows把命令发给多个候选对象,每条命令都一般有一个对应的处理函数,处理函数处理命令的方法和处理Windows消息的方法是一样的,但是调用机制不一样。

自定义消息
区别于系统定义的消息:WM_USER(0x0400)至(0x7FFF)为用户保留的用于标志用户自定义信息的消息号,同一个应用程序中,用户自定义的多个消息的ID值不能相同,都是唯一的。WM_USER+100保险做法.
5.消息映射以及消息传递
消息映射是MFC内建的一个消息分派机制。类似于switch-case语句
若要在MFC类中实现消息映射,首先要在类的声明中宣布DECLARE_MESSAGE_MAP()一个由消息映射函数入口地址组成的数组和一个消息MAP的数据结构,messageMap(pBaseMap,lpEntries)可理解为父窗口句柄和消息处理函数入口.BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间的宏构成了_messageEntries[]中的消息映射.
例如:ON_WM_CREATE()宏把WM_CREATE消息映射到OnCreate处理函数上。
ON_BN_CLICKED(IDC_BUTTON,OnButton),左击IDC_BUTTON消息处理函数OnButton。用该语句就设置了这种消息与处理函数之间的映射,让系统知道这个点击按钮操作后会去哪执行相应的语句。
自定义消息:ON_MESSAGE(WM_MYMESSAGE,OnMyMessage)
WM_MYMESSAGE是自定义的窗口消息,OnMyMessage表示自定义消息处理函数。

afx_msg LRESULT OnMyMessage(WPARAM wParam,LPARAM lParam)

无符号整数返回值 参数是2个消息参数.
具体使用方法:
1.定义消息
#define WM_MY_MESSAGE WM_USER+100
2.声明消息处理函数
MainFrm.h文件中,类CMainFrame内,声明:
protect:
afx_msg LRESULT OnMyMessage(WPARAM wParam,LPARAM lParam);
3. 实现消息处理函数
MainFrm.cpp下增加代码:

    LRESULT OnMyMessage(WPARAM wParam,LPARAM lParam)
    {
             //代码操作.
             return 0;
    }

4.使用ON_MESSAGE宏指令将消息映射到消息处理函数中:
在CMainFrame类的消息块中:
BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)
ON_WM_CREATE()
ON_MESSAGE(WM_MY_MESSAGE,OnMyMessage)
END_MESSAGE_MAP()
5.调用自定义消息方式:SendMessage(消息宏名),PostMessage(消息宏名)
如:SendMessage(WM_MY_MESSAGE);

7.对话框
CDialog类的常用成员函数
DoModal 激活对话框并且显示出来,参数是对话框类的对象
Create 根据对话框模板资源创建非模态对话框窗口,若对话框不是Visible属性,还需要通过调用函数CWnd::ShowWindow()显示对话框,一般都为保证显示非模态对话框都写上这个语句。
OnOk 单击OK按钮时调用该函数,接受对话框输入数据,关闭对话框。
OnCancel 单击Cancel按钮或按Esc键时调用该函数,不接受对话框输入数据,关闭对话框。
OnInitDialog WM_INITDIALOG的消息处理函数,在调用DoModal()或Create()函数时系统发送WM_INITDIALOG消息,在显示对话框前调用该函数进行初始化工作。
EndDialog 关闭模态对话框窗口,关闭并销毁非模态对话框需要调用函数CWnd::DestroyWindow()

介绍模态对话框和非模态对话框:
模态对话框:
一般都用模态对话框,显示出这个模态对话框后,不能选择应用程序(主窗口)的其他功能,原因是用DoMotal()显示模态对话框后,退出了主窗口的消息循环,直到退出子窗口也就是DoMotal()函数调用结束后,重新获得主窗口消息循环才可以对主窗口进行操作。
非模态对话框:
显示出模态对话框后,还可以对选择应用程序的其他功能,原因是非模态对话框是用Create()显示非模态对话框的,这个函数在显示出非模态对话框后就会返回到主窗口中,所以并没有出现模态对话框的中断消息循环现象。
对非模态对话框的创建与模态对话框的创建的相同点和不同点:
相同点:创建方式都是以插入Dialog资源方式创建,并且按Ctrl+W就会询问是否创建一个对话框类,输入对话框类型,继承CDialog默认就可以,按确定就建立了一个对话框类。
不同点:模态对话框的启动和显示方式是用对话框对象的DoMotal()函数激活并显示,而非模态对话框是用对话框对象的Create()函数激活并显示,Create(对话框ID,传输父窗口路径(CWnd*) )。

以下是两个创建模态对话框和非模态对话框后,用对话框对主窗口视图进行Draw操作的部分代码(省略创建窗口步骤):
窗口名都为CInputDlg ID可自定义
在主窗口菜单资源中设置一个选项,名称为输入角度(&A),然后为该菜单按钮定义一个COMMAND函数,函数是定义在View类中的,接着如下代码:
CInputDlg dlg;
if(dlg.DoModal()==IDOK)
{
m_nViewAngle=dlg.m_nAngle;//对话框消息传递给视图窗口
Invalidate();//刷新视图,调用OnPaint()再调用OnDraw;
}

该函数功能仅仅是从对话框中获取一个信息m_nAngle角度值,然后就退出,写if的这种形式,表示进入对话框并且设置编辑框m_nAngle值(int类型)按确定修改,然后通过按确定按钮,退出对话框并返回IDOK值,然后进入if中对视图成员m_nViewAngle赋值的操作,接着刷新视图。(if要不要都无所谓),重点是一定要在DoModal()返回后(对话框消失)才可以对视图进行操作。若你按取消按钮或者X是没有修改信息的,其实可以是得到了一个对话框的默认值角度,对话框的ONOk函数不要删除CDialg::ONOK语句,不然会失效。 一般新建的对话框是默认有这个ONOK函数的。(“确认”就是对应ONOK函数)
在视图类头文件中加上CInputDlg类的头文件。(这个类是对话框类,自定义的,名字随你起的..)

void CMy_TestDoModalView::OnDraw(CDC* pDC)
{
    CMy_TestDoModalDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // TODO: add draw code for native data here
    POINT start,end,center;
    CRect cRect,dRect;
    CBrush *brushold,brushnew;
    int r=200;
    int theta=m_nViewAngle;//对话框设置的角度数值
    GetClientRect(&cRect);
    center=cRect.CenterPoint();
    dRect.SetRect(center.x-r,center.y-r,center.x+r,center.y+r);
    start.x=center.x+r;
    start.y=center.y;
    end.x=center.x+(int)(r*cos(theta*3.1416/180)+0.5);
    end.y=center.y+(int)(r*sin(theta*3.1416/180)+0.5);
    brushnew.CreateStockObject(LTGRAY_BRUSH);
    brushold=pDC->SelectObject(&brushnew);
    pDC->Pie(dRect,start,end);
    pDC->SelectObject(brushold);
}

以上代码是在视图类的OnDraw函数中设置的,画一个饼图,因为要用到”math.h” 的2个函数sin,cos所以要加上这个头文件在该类头文件中。(PS:#include “math.h” 不要写成

#include "MyTest_No1View.h"
//class CMyTest_No1AView;
CInputDlg::CInputDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CInputDlg::IDD, pParent)
{
    //{{AFX_DATA_INIT(CInputDlg)
    m_pDlg = 0;//int成员 编辑框设置角度.
    m_pParent =(CMyTest_No1View*) pParent;//将pParent指向窗口句柄指针强制转化为指向视图指针,并进行赋值操作
    //}}AFX_DATA_INIT                       
}

我在这里遇到一个问题那就是强制转换不成功,原因是(CMyTest_No1View*)我写成了(CMyTest_No1View *),多了一个空格分割开指针标识符。
这个构造函数在new一个对象的时候被调用,参数是父窗口指针。
此时,接口如何获取问题已解决,那么接口的来源是从哪来的呢?
以及对话框的创建,初始化,显示步骤:
答:接口来源是View视图类,在我的这个代码例子中叫:CMyTest_No1View
所以,对话框的创建,初始化,显示都应该在视图类的某个函数中实现,
为了创建一个对话框,必须让视图类知道有这个对话框类,所以在视图头文件中写入#include “InputDlg.h”
这个头文件是对话框类头文件。
然后,在视图类声明一个对话框指针成员 m_pDlg
CInputDlg * m_pDlg;//这个值初始化为NULL (必须);
//初始化操作在视图构造函数中初始化!
同理也要有一个int m_nViewAngle;用于保存对话框 送来的信息。

根模态对话框一样,在你菜单按钮响应函数中输入以下代码:

void CMyTest_No1View::OnEditInputangle() 
{
    // TODO: Add your command handler code here
    if(m_pDlg)//非空就直接显示出对话框
        m_pDlg->SetActiveWindow();//激活非模态对话框
    else//若该对象为空
    {
        m_pDlg=new CInputDlg(this);//新建m_pDlg指针并调用CInputDlg的构造函数,参数就是this(指向视图句柄的指针?)
        //m_pDlg->m_pParent= this;//再赋值父视图窗口指针(保险操作)
        m_pDlg->Create(IDD_INPUTANGLE,this);//创建非模态对话框,对话框ID名,父窗口指针this.
        m_pDlg->ShowWindow(SW_SHOW);//显示窗口SW_SHOW默认方式显示
    }
}

从以上代码可见,在父中若m_pDlg不存在 用new创建并用this对这个类对象指针初始化,this可理解为是父窗口指针,在这里是视图窗口指针,
Create没怎么仔细研究,但是对对话框的构造函数送的this肯定是一个视图指针,也可以说是父窗口指针进入构造函数后,通过强制转换为视图指针对对话框的m_pParent赋值。
到此,父视图窗口为对话框的创建,初始化,显示工作完毕,在这里你会想到,为什么非模态对话框的声明不是在视图的菜单按钮响应函数中的呢?因为创建需要new啊!new是需要分配MFC资源空间的,每次调用这个函数都new一次那还用玩的~所以需要建立为视图类的公有成员指针,这样当它不存在的时候,创建一次就好,以后想调用它的时候就用已经存在的就可以了。
此后,就是对话框传送信息并刷新父窗口的函数操作,是在OnOk函数中进行的.

void CInputDlg::OnOK() 
{
    // TODO: Add extra validation here
    UpdateData(true);//编辑框显示信息传送到编辑框对应变量m_pDlg.
    m_pParent->m_nCViewAngle=m_pDlg;//对视图窗口的成员赋值
    m_pParent->Invalidate();//刷新,先调用OnPaint(),然后调用OnDraw()
    /*CDialog::OnOK();//仔细看这里,注释了OnOK函数,现在可以知道原因了吗?*/
}

注释了OnOK()可使得对话框不会退出,并且还能对主窗口进行操作。

void CInputDlg::OnCancel() 
{
    // TODO: Add extra cleanup here
    if(m_pParent!=NULL)
    {
        m_pParent->m_pDlg=NULL;
        DestroyWindow();
    }
    //CDialog::OnCancel();
}

取消按钮实现的是当父亲存在,父亲的对话框指针成员先要设置为空,再销毁对话框。其实可以不要这个if的,因为对话框的建立的条件是建立于父亲视图窗口存在的情况下。可能是为了稳保起见这种思维还是要有的,可能会出现创建对话框虽然成功,但是可能输入的this参数对构造函数的赋值不起作用 或者说是 创建对话框没有用new方式创建故只调用了对话框类的默认构造函数,就会导致m_pParent=NULL,仔细的同学可以看到对话框的CWnd父窗口指针默认值是NULL.
完毕,以上都只是我的猜想,我只是一个大三在校生,有任何错误都可以在评论中指出。
参考文献- MFC

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值