MFC包含的基本四个类

  • 框架类
  • 应用程序类
  • 文档类
  • 视图类

MFC程序运行启动

AFX_MODULE_STATE aaa;//当前程序模块状态信息aaa(臆造的名字)
AFX_MODULE_THREAD_STATE bbb;//当成程序线程状态信息CWinAPP::CWinApp()//构造全局对象CMyWinApp theApp
{  AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE(); //获取全局变量&aaa,_AFX_CMDTARGET_GETSTATE()为AfxGetModuleState()的宏定义AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread//获取全局变量&bbbpThreadState->m_pCurrentWinThread = this;//谁调用构造函数,谁就是this,即theApp的地址保存在变量m_pCurrentWinThread,bbb的一个成员中AfxGetThread(){AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();//获取全局变量&bbbCWinThread* pThread = pState->m_pCurrentWinThread;return pThread;//返回&theApp}//ASSERT断言判断pModuleState->m_pCurrentWinApp = this;//将&theApp的赋给aaa的一个成员AfxGetApp(){return afxCurrentWinApp;//afxCurrentWinApp是一个宏,AfxGetModuleState()->m_pCurrentWinApp,即return &theApp}}
  • 程序的启动,构造theApp的对象,调用父类CWinApp的构造函数:

    • 将theAPP对象的地址保存在线程状态信息中;
    • 将theApp对象的地址保存在模块状态信息中;
    • 进入WinMain函数,调用AfxWinMain函数。

程序的执行

//theApp是不是指导程序流程
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,_In_ LPTSTR lpCmdLine, int nCmdShow){AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow){CWinThread* pThread = AfxGetThread();CWinApp* pApp = AfxGetApp();//均为获取&theApppApp->InitApplication();//利用theApp对象调用应用程序类成员虚函数****初始化,多态性pThread->InitInstance()//利用theApp对象调用应用程序类成员虚函数****创建并显示窗口pThread->Run()//利用theApp对象调用应用程序类成员虚函数****消息循环{for(;;){while(没有消息时)OnIdle();//利用theApp对象调用应用程序类成员虚函数 空闲处理do{if(getMessage捉到WM_QUIT)return ExitInstance();//程序结束前,利用theApp对象调用应用程序类成员虚函数****善后处理}while(...)}};}}
  • 进入入口函数WinMain

    • 获取应用程序类对象theApp的地址;
    • 利用theApp地址调用InitApplication,初始化当前应用程序的数据;
    • 利用theApp地址调用InitInstance虚函数初始化程序,在函数中我们创建窗口并显示;
    • 利用theApp地址调用CWinApp的Run虚函数进行消息循环;
    • 如果没有消息,利用theApp地址调用OnIdle虚函数实现空闲处理;
    • 程序退出利用theApp地址调用ExitInstance虚函数实现退出前的善后处理工作。
  • 成员变量

    m_pMainWnd——当前应用程序的主窗口,一个指针

钩子函数

  • Win32的技术,MFC借用了这个技术,大量用于木马病毒;
  • 优先读取消息,把消息钩过来,所以称为钩子函数。

创建钩子

HHOOK SetWindowsHookEX(
int idHook,//钩子类型,在MFC里面一般为WH_CBT,即钩取WM_CREATE信息
HOOKPROC Lpfn,//钩子处理函数
HINSTANCE hMod,//应用程序实例句柄
DWORD dwThreadId//线程ID
);

钩子处理函数

LRESULT CALLBACK CBTProc(
int nCode,//钩子码,一般 为HCBT_CREATEWND
WPARAM wParam,//刚刚创建成功的窗口句柄
LPARAM lParam//...
)

更改窗口处理函数

LONG_PTR SetWindowLongPtr(HWND  hWnd,//窗口句柄int nIndex,//GWLP_WNDPROCLONG_PTR dwNewLong//新的窗口处理函数名(函数地址)
)

MFC的窗口如何创建

CMyFrameWnd *pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCBase");
//即Create的运行过程
  • 加载菜单
  • 调用CWnd::CreateEx函数创建窗口

{

​ 调用PreCreateWindow函数设计和注册窗口类

​ {

​ 调用AfxDeferRegisterClass函数

​ }

}

  • 调用AfxHookWindowCreate函数,埋下钩子
  • 调用CreateWindowEx函数创建窗口,马上调用钩子处理函数
  • 钩子处理函数_AfxCbtFilterHook
    • 将窗口句柄和框架类对象地址建立一对一的绑定关系
    • 使用SetWindowLong函数,将窗口处理的函数设置为AfxWndProc

消息映射机制

  • 在不重写WindowProc虚函数的大前提下,即可处理消息

  • 类必须具备的条件

    • 类内必须添加声明宏——DECLARE_MESSAGE_MAP()
    • 类外必须添加实现宏
      • BEGIN_MESSAGE_MAO(theClass, baseClass)
      • END_MESSAGE_MAP()
  • 总结:当一个类具备上述两个条件,这个类就可以按照消息映射进制来处理消息

  • example

    class CMyFrameWnd :public CFrameWnd {DECLARE_MESSAGE_MAP()
    public:LRESULT OnCreate(WPARAM wParam, LPARAM lParam);
    };BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)ON_MESSAGE(WM_CREATE,OnCreate)
    END_MESSAGE_MAP()LRESULT CMyFrameWnd::OnCreate(WPARAM wParam, LPARAM lParam) {AfxMessageBox("WM_CREATE");return 0;
    }
    
  • 运行机制——遍历链表(本类的静态变量和静态数组,父类的静态变量和静态数组),当消息产生的时候,带着消息的ID遍历链表寻找对应的消息函数完成相应的处理。

消息的分类

  • 标准windows消息

    ON_WM_XXX

    class CMyFrameWnd :public CFrameWnd {DECLARE_MESSAGE_MAP()
    public:int OnCreate(LPCREATESTRUCT pcs);
    };BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)ON_WM_CREATE()
    END_MESSAGE_MAP()int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {AfxMessageBox("WM_CREATER IDLE");return CFrameWnd::OnCreate(pcs);//防止父类的create函数被屏蔽
    }
    
  • 自定义消息

    ON_MESSAGE

  • 命令消息

    ON_COMMAND

菜单

  • 菜单相关问题

    • Win32-HMENU
    • MFC-CMenu类对象
  • CMenu封装了一个重要的成员变量m_hMenu(菜单句柄)

  • 菜单的创建方法

    • 添加菜单资源rc文件,同时注意#include “resource.h”,可视化操作新增菜单对象

    • 将菜单设置到窗口

      • 方法一:利用pFrame调用Create函数时,传参;

      • 方法二:在处理框架窗口的WM_CREATE消息时,

        CMenu menu;
        menu.LoadMenu(...);
        
  • example

#include<afxwin.h>
#include "resource.h"
class CMyFrameWnd :public CFrameWnd {DECLARE_MESSAGE_MAP()
public:int OnCreate(LPCREATESTRUCT pcs);
public:CMenu menu;//保证生命周期
};BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)ON_WM_CREATE()
END_MESSAGE_MAP()int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {menu.LoadMenu(IDR_MENU1);//绑定菜单句柄和menu对象this->SetMenu(&menu);return CFrameWnd::OnCreate(pcs);
}class CMyWinApp :public CWinApp {
public:virtual BOOL InitInstance();
};BOOL CMyWinApp::InitInstance() {CMyFrameWnd *pFrame = new CMyFrameWnd;//方法一,传参//pFrame->Create(NULL, "MFCMenu",WS_OVERLAPPEDWINDOW,CFrameWnd::rectDefault,//    NULL,(CHAR*)IDR_MENU1);pFrame->Create(NULL, "MFCMenu");m_pMainWnd = pFrame;pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;
}CMyWinApp theApp;//爆破点
  • 命令消息的处理顺序

    对于ON_COMMMADE而言,框架类>应用程序类(对于同一个消息)

  • 下拉菜单、菜单状态选项、右键菜单的例子

#include<afxwin.h>
#include "resource.h"
class CMyFrameWnd :public CFrameWnd {DECLARE_MESSAGE_MAP()
public:afx_msg int OnCreate(LPCREATESTRUCT pcs);afx_msg void OnNew();afx_msg void OnInitMenuPopup(CMenu* pPopupMenu,UINT nIndex,BOOL bSysMenu);afx_msg void OnContextMenu(CWnd* pWnd, CPoint pos);
public:CMenu menu;//保证生命周期,代表整个大菜单
};BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)ON_COMMAND(ID_new, OnNew)ON_WM_CREATE()ON_WM_INITMENUPOPUP()ON_WM_CONTEXTMENU()
END_MESSAGE_MAP()//右键菜单
void CMyFrameWnd::OnContextMenu(CWnd* pWnd, CPoint pos) {方法一:调用win32api //HMENU hPopup = ::GetSubMenu(menu.m_hMenu, 0);//获取顶层菜单某一菜单的下一菜单//::TrackPopupMenu(hPopup, TPM_LEFTALIGN | TPM_TOPALIGN, pos.x, pos.y, 0,//  this->m_hWnd, NULL);//方法二:采用MFC对象CMenu *pPopup = menu.GetSubMenu(0);pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_TOPALIGN, pos.x, pos.y,this);}//设置菜单项状态
void CMyFrameWnd::OnInitMenuPopup(CMenu* pPopupMenu,UINT nIndex,BOOL bSysMenu) {pPopupMenu->CheckMenuItem(ID_new, MF_CHECKED);//::CheckMenuItem(pPopupMenu->m_hMenu, ID_new, MF_CHECKED);
}//菜单选项消息响应
void CMyFrameWnd::OnNew() {AfxMessageBox("新建");
}//绑定UI菜单到程序
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {menu.LoadMenu(IDR_MENU1);//绑定菜单句柄和menu对象this->SetMenu(&menu);return CFrameWnd::OnCreate(pcs);
}class CMyWinApp :public CWinApp {
public:virtual BOOL InitInstance();
};//基本应用框架
BOOL CMyWinApp::InitInstance() {CMyFrameWnd *pFrame = new CMyFrameWnd;//绑定菜单//pFrame->Create(NULL, "MFCMenu",WS_OVERLAPPEDWINDOW,CFrameWnd::rectDefault,//   NULL,(CHAR*)IDR_MENU1);pFrame->Create(NULL, "MFCMenu");m_pMainWnd = pFrame;pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;
}CMyWinApp theApp;

工具栏

  • 相关的类

    • CToolBarCtrl——父类为CWnd,封装了关于工具栏控件的各种操作。(不是工具栏,是工具栏里面的一个一个的按钮)
    • CToolBar——父类CControlBar,封装了关于工具栏的操作,以及和框架窗口的关系。(这个才是工具栏)
  • 工具栏的创建使用
    • 添加工具栏资源(rc文件,可视化操作)
    • 创建工具栏 CToolBar::CreateEx
    • 加载工具栏CTool::LoadToolBar
    • 设置工具栏的停靠(看看就好)
      • CToolBar::EnableDocking
      • CFrameWnd::EnableDocking
      • CFrameWnd::DockControlBar
  • example
#include<afxwin.h>
#include "resource.h"
#include<afxext.h>//CToolBar类为拓展类class CMyFrameWnd :public CFrameWnd {DECLARE_MESSAGE_MAP()
public:afx_msg int OnCreate(LPCREATESTRUCT pcs);afx_msg void OnNew();afx_msg void OnSet();
public:CMenu menu;CToolBar toolbar;
};BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)ON_COMMAND(ID_set,OnSet)ON_COMMAND(ID_new,OnNew)ON_WM_CREATE()
END_MESSAGE_MAP()//绑定菜单、工具栏
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs){//绑定菜单menu.LoadMenu(IDR_MENU1);//绑定菜单句柄和menu对象this->SetMenu(&menu);//绑定工具栏toolbar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP);toolbar.LoadToolBar(IDR_TOOLBAR1);return CFrameWnd::OnCreate(pcs);
}void CMyFrameWnd::OnSet(){AfxMessageBox("green");
}
void CMyFrameWnd::OnNew() {AfxMessageBox("new");
}class CMyWinApp :public CWinApp {
public:virtual BOOL InitInstance();
};BOOL CMyWinApp::InitInstance() {CMyFrameWnd *pFrame = new CMyFrameWnd;pFrame->Create(NULL, "MFCToolBar");m_pMainWnd = pFrame;pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;}CMyWinApp theApp;

运行时类信息机制

  • MFC六大机制

    • 程序启动机制
    • 窗口创建机制
    • 消息映射机制
    • 运行时类信息机制
    • 动态创建机制
    • 序列化机制
  • 含义:在程序运行过程中可以获知对象的类的相关信息(例如:对象是否属于某个类)

  • 运行时类信息机制的使用(具备三个要件)

    • 类必须派生自CObject
    • 类内必须添加声明宏 DECLARE_DYNAMIC(theClass)
    • 类外必须添加实现宏 IMPLEMENT_DYNAMIC(theClass,baseClass)
    • 当一个类具备上述三个要件后,CObject::IsKindOf函数就可以正确判断对象是否属于某个类
  • example

#include<afxwin.h>
#include <iostream>
using namespace std;class CAnimal :public CObject {DECLARE_DYNAMIC(CAnimal)
};
IMPLEMENT_DYNAMIC(CAnimal,CObject)class CDog :public CObject {DECLARE_DYNAMIC(CDog)
};
IMPLEMENT_DYNAMIC(CDog,CAnimal)int main()
{CDog yellowdog;if (yellowdog.IsKindOf(RUNTIME_CLASS(CDog))) {cout << "yellowdog is dog";}else {cout << "yelllowdog isnot dog";}return 0;
}
  • 运行机制:还是宏展开链表追踪,重点的宏展开是RUNTIME_CLASS(class)

动态创建机制

  • 作用:在不知道类名的情况下,将类的对象创建出来。

  • 使用方法

    • 类必须派生自CObject

    • 类内必须添加声明宏 DECLARE_DYNCREATE(theClass)

    • 类外必须添加实现宏 IMPLEMENT_DYNCREATE(theClass,baseClass)

    • 当一个类具备上述三个要件后,CRuntimeClass::CreateObject(对象加工厂)

      函数就可以将类的对象创建出来

  • example

#include<afxwin.h>
#include <iostream>
using namespace std;class CAnimal :public CObject {DECLARE_DYNAMIC(CAnimal)
};
IMPLEMENT_DYNAMIC(CAnimal,CObject)class CDog :public CObject {DECLARE_DYNCREATE(CDog)
};
IMPLEMENT_DYNCREATE(CDog,CAnimal)int main()
{CObject *pob= RUNTIME_CLASS(CDog)->CreateObject();//对象加工厂函数if (pob) {cout << pob<<endl;}else {cout << "对象创建失败";}return 0;
}
  • 抛弃单兵作战的思想,你设置对象,MFC帮你创建

视图窗口

  • 提供一个用于显示数据的窗口

  • CView类,封装了关于视图窗口的各种操作,以及和文档类(后台处理数据)的数据交互,其父类为CWnd类。

  • 命令消息的处理顺序(暂时)

    对于ON_COMMMADE而言,视图类>框架类>应用程序类(对于同一个消息)

  • 视图窗口的使用

    • 定义一个自己的视图类(CMyView),派生自CView,并重写父类成员纯虚函数OnDraw(可用于绘图)
    • 其余框架类和应用层程序类代码不变
    • 在处理框架窗口的WM_CREATE消息时,定义CMyView类对象,并调用Create函数创建视图窗口,视图窗口的ID设置为AFX_IDW_PANE_FIRST(设置其大小同框架类画布)
  • example

#include<afxwin.h>
class CMyView : public CView {
public:void OnDraw(CDC *pDC);
};//重写纯虚函数
void CMyView::OnDraw(CDC *pDC) {
}class CMyFrameWnd : public CFrameWnd {DECLARE_MESSAGE_MAP()
public:afx_msg int OnCreate(LPCREATESTRUCT pcs);
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)ON_WM_CREATE()
END_MESSAGE_MAP()int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {CMyView * pView = new CMyView;pView->Create(NULL, "MFCVIEW", WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0, 0, 200, 200),this, AFX_IDW_PANE_FIRST);//视图ID设置为平铺在整个框架窗口上this->m_pViewActive = pView;//视图类对象地址绑定return CFrameWnd::OnCreate(pcs);
}class CMyWinAPP : public CWinApp {virtual BOOL InitInstance();
};BOOL CMyWinAPP::InitInstance() {CMyFrameWnd *pFrame = new CMyFrameWnd;pFrame->Create(NULL, "MFCVIEW");this->m_pMainWnd = pFrame;//框架类对象地址绑定pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;
}CMyWinAPP theApp;//爆破点
  • 对象关系
this->m_pMainWnd = pFrame;//框架类对象地址绑定
this->m_pViewActive = pView;//视图类对象地址绑定
——theApp->m_pMainWnd(pFrame框架类对象)->m_pViewActive(pView视图类对象地址)
一一绑定,获得theApp即可得到所有的类地址

文档类

  • CDocument:提供了一个用于管理数据的类,封装了关于数据的管理(数据管理,数据转换,数据存储等),并和视图类进行数据交互。

  • 程序的创建过程

    • 利用框架类对象地址pFrame调用LoadFrame函数,创建框架窗口;
    • (父类)在处理框架窗口的WM_CREATE的消息时,动态创建视图类对象,并创建视图窗口;
    • (父类)在处理视图窗口的WM_CREATE的消息时,将文档类对象和视图类对象建立关联关系。
  • 对象关系图

    • 文档类对象用一个链表成员变量,保存视图类对象地址;
    • 视图类对象用一个普通成员变量,保存文档类对象地址;
——theApp|->m_pMainWnd(pFrame框架类对象地址)|->m_pViewActive(pView视图类对象地址,活动视图类对象地址,视图可以有多个,但活动视图只有一个)|->m_pDocument(文档类对象地址pDoc)|->m_viewList(所有视图类对象地址,一个链表,m_viewList.AddTail(pView))
//一一绑定,获得theApp即可得到所有的类地址;
//一个视图对应一个文档;一个文档可以对应多个视图;
  • example
#include<afxwin.h>
#include<afxext.h>
#include "resource.h"
class CMyDoc :public CDocument {
};class CMyView :public CView {DECLARE_DYNCREATE(CMyView)//动态创建机制DECLARE_MESSAGE_MAP()//消息映射机制
public:virtual void OnDraw(CDC * pDC);//重写纯虚函数afx_msg int OnCreate(LPCREATESTRUCT pcs);
};
IMPLEMENT_DYNCREATE(CMyView,CView)
BEGIN_MESSAGE_MAP(CMyView,CView)ON_WM_CREATE()
END_MESSAGE_MAP()int CMyView::OnCreate(LPCREATESTRUCT pcs) {//return 0;//文档类和视图类没法关联return CView::OnCreate(pcs);//将文档类对象和视图类对象建立关联关系
}void CMyView::OnDraw(CDC * pDC) {pDC->TextOut(100, 100, "this is view window");//父类处理视图动态创建的说明
}class CMyFrameWnd :public CFrameWnd {DECLARE_MESSAGE_MAP()
public:afx_msg int OnCreate(LPCREATESTRUCT pcs);//pcs这个参数可以获取::CreateWindowEx的12个参数
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)ON_WM_CREATE()
END_MESSAGE_MAP()int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {//return 0;//MFC父类自动创建视图窗口的证明return CFrameWnd::OnCreate(pcs);
}class CMyWinApp :public CWinApp {
public:virtual BOOL InitInstance();
};BOOL CMyWinApp::InitInstance() {CMyFrameWnd *pFrame = new CMyFrameWnd;CMyDoc *pDoc = new CMyDoc;CCreateContext cct;cct.m_pCurrentDoc = pDoc;//文档类对象地址cct.m_pNewViewClass = RUNTIME_CLASS(CMyView);//&CMyView::classCMyView,即视图类的静态变量的地址pFrame->LoadFrame(IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL,&cct);//创建框架窗口m_pMainWnd = pFrame;pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;
}CMyWinApp theApp;

窗口切分

  • 相关类

    CSplitterWnd——不规则框架窗口类,封装了关于不规则框架窗口的操作;

  • 窗口切分的使用

    重写CFrameWnd类的成员虚函数OnCreateClient

    • 在虚函数中调用CSplitterWnd::CreateStatic创建不规则框架窗口
    • 在虚函数中调用CSplitterWnd::CreateView创建视图窗口
  • example

#include<afxwin.h>
#include<afxext.h>
#include "resource.h"
class CMyDoc :public CDocument {
};class CMyView :public CView {DECLARE_DYNCREATE(CMyView)//动态创建机制DECLARE_MESSAGE_MAP()//消息映射机制
public:virtual void OnDraw(CDC * pDC);//重写纯虚函数afx_msg int OnCreate(LPCREATESTRUCT pcs);
};
IMPLEMENT_DYNCREATE(CMyView,CView)
BEGIN_MESSAGE_MAP(CMyView,CView)ON_WM_CREATE()
END_MESSAGE_MAP()int CMyView::OnCreate(LPCREATESTRUCT pcs) {//return 0;//文档类和视图类没法关联return CView::OnCreate(pcs);//将文档类对象和视图类对象建立关联关系
}void CMyView::OnDraw(CDC * pDC) {pDC->TextOut(100, 100, "this is view window");//父类处理视图动态创建的说明
}class CMyFrameWnd :public CFrameWnd {DECLARE_MESSAGE_MAP()
public:CSplitterWnd split;//提高生命周期
public:afx_msg int OnCreate(LPCREATESTRUCT pcs);//pcs这个参数可以获取::CreateWindowEx的12个参数afx_msg BOOL OnCreateClient(LPCREATESTRUCT pcs, CCreateContext *pContext);
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)ON_WM_CREATE()
END_MESSAGE_MAP()BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT pcs, CCreateContext *pContext) {//创建两个视图窗口,父类处理OnCreate消息时自动把两个视图和唯一的文档类关联split.CreateStatic(this, 1, 2);//父类,1行两列split.CreateView(0, 0, pContext->m_pNewViewClass, CSize(100, 100), pContext);split.CreateView(0, 1, pContext->m_pNewViewClass, CSize(100, 100), pContext);return TRUE;
}int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {//return 0;//MFC父类自动创建视图窗口的证明return CFrameWnd::OnCreate(pcs);
}class CMyWinApp :public CWinApp {
public:virtual BOOL InitInstance();
};BOOL CMyWinApp::InitInstance() {CMyFrameWnd *pFrame = new CMyFrameWnd;CMyDoc *pDoc = new CMyDoc;CCreateContext cct;cct.m_pCurrentDoc = pDoc;//文档类对象地址cct.m_pNewViewClass = RUNTIME_CLASS(CMyView);//&CMyView::classCMyView,即视图类的静态变量的地址pFrame->LoadFrame(IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL,&cct);//创建框架窗口m_pMainWnd = pFrame;pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;
}CMyWinApp theApp;
  • 命令消息的处理顺序

    对于ON_COMMMADE而言,视图类>文档类>框架类>应用程序类(对于同一个消息)

  • 文档类成员函数

    ​ 当文档类数据发生变化是,调用UpDateAllViews函数刷新和文档类对象相关联的视图类对象(视图窗口)

    void CMyDoc::OnNew() {this->str = "hello world!";//表示接收到的数据,比如网络的socket等//this->UpdateAllViews(NULL);//刷新和这个文档类对象(this)关联的所有视图窗口//刷新单独的窗口POSITION pos = this->GetFirstViewPosition();//GetFirstXXXPosition,迭代器函数,获取链表头结点的上一个结点CView *pView = this->GetNextView(pos);//GetNextXXXPosition,迭代器函数,获取链表的下一个结点this->UpdateAllViews(pView);//刷新和这个文档类对象this关联的除了pView指向的视图窗口
    }//---------------------------------------------------
    void CMyView::OnDraw(CDC * pDC) {CMyDoc *pDoc = (CMyDoc*)this->m_pDocument;pDC->TextOut(100, 100, pDoc->str);//父类处理视图动态创建的说明
    }
    
    #include<afxwin.h>
    #include<afxext.h>
    #include "resource.h"
    class CMyDoc :public CDocument {DECLARE_MESSAGE_MAP()
    public:afx_msg void OnNew();
    public:CString str;//数据
    };
    BEGIN_MESSAGE_MAP(CMyDoc, CDocument)ON_COMMAND(ID_NEW, OnNew)
    END_MESSAGE_MAP()void CMyDoc::OnNew() {this->str = "hello world!";//表示接收到的数据,比如网络的socket等//this->UpdateAllViews(NULL);//刷新和这个文档类对象(this)关联的所有视图窗口//刷新单独的窗口POSITION pos = this->GetFirstViewPosition();//GetFirstXXXPosition,迭代器函数,获取链表头结点的上一个结点CView *pView = this->GetNextView(pos);//GetNextXXXPosition,迭代器函数,获取链表的下一个结点this->UpdateAllViews(pView);//刷新和这个文档类对象this关联的除了pView指向的视图窗口
    }class CMyView :public CView {DECLARE_DYNCREATE(CMyView)//动态创建机制DECLARE_MESSAGE_MAP()//消息映射机制
    public:virtual void OnDraw(CDC * pDC);//重写纯虚函数afx_msg int OnCreate(LPCREATESTRUCT pcs);afx_msg void OnNew();
    };
    IMPLEMENT_DYNCREATE(CMyView,CView)
    BEGIN_MESSAGE_MAP(CMyView,CView)ON_WM_CREATE()//ON_COMMAND(ID_NEW,OnNew)
    END_MESSAGE_MAP()void CMyView::OnNew() {AfxMessageBox("view class deal with WM_COMMAND message");
    }int CMyView::OnCreate(LPCREATESTRUCT pcs) {//return 0;//文档类和视图类没法关联return CView::OnCreate(pcs);//将文档类对象和视图类对象建立关联关系
    }void CMyView::OnDraw(CDC * pDC) {CMyDoc *pDoc = (CMyDoc*)this->m_pDocument;pDC->TextOut(100, 100, pDoc->str);//父类处理视图动态创建的说明
    }class CMyFrameWnd :public CFrameWnd {DECLARE_MESSAGE_MAP()
    public:CSplitterWnd split;
    public:afx_msg int OnCreate(LPCREATESTRUCT pcs);//pcs这个参数可以获取::CreateWindowEx的12个参数afx_msg BOOL OnCreateClient(LPCREATESTRUCT pcs, CCreateContext *pContext);
    };
    BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)ON_WM_CREATE()
    END_MESSAGE_MAP()BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT pcs, CCreateContext *pContext) {//创建两个视图窗口,父类处理OnCreate消息时自动把两个视图和唯一的文档类关联split.CreateStatic(this, 1, 2);//父类,1行两列split.CreateView(0, 0, pContext->m_pNewViewClass, CSize(100, 100), pContext);split.CreateView(0, 1, pContext->m_pNewViewClass, CSize(100, 100), pContext);this->m_pViewActive = (CView*)split.GetPane(0, 0);//设置活动窗口0行0列return TRUE;
    }int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {//return 0;//MFC父类自动创建视图窗口的证明return CFrameWnd::OnCreate(pcs);
    }class CMyWinApp :public CWinApp {
    public:virtual BOOL InitInstance();
    };BOOL CMyWinApp::InitInstance() {CMyFrameWnd *pFrame = new CMyFrameWnd;CMyDoc *pDoc = new CMyDoc;CCreateContext cct;cct.m_pCurrentDoc = pDoc;//文档类对象地址cct.m_pNewViewClass = RUNTIME_CLASS(CMyView);//&CMyView::classCMyView,即视图类的静态变量的地址pFrame->LoadFrame(IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL,&cct);//创建框架窗口m_pMainWnd = pFrame;pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;
    }CMyWinApp theApp;
    
  • 视图类成员函数

    获取和视图类对象关联的文档类对象,调用GetDocument(),这是公有函数,和m_pDocument的保护成员不一样,但返回的都是文档的地址。

代码基本同上

void CMyView::OnDraw(CDC * pDC) {//CMyDoc *pDoc = (CMyDoc*)this->m_pDocument;CMyDoc *pDoc = (CMyDoc*)this->GetDocument();pDC->TextOut(100, 100, pDoc->str);//父类处理视图动态创建的说明
}

单文档视图架构

  • 特点:只能有一个文档类对象,只能有一份数据

  • 参与架构的类

    CFrameWnd/ CWinApp/ CView /CDocument

  • 需要用到的类

    • CDocTemplate(文档模板类)

      ​ |->CSingleDocTemplate(单文档模板类)

    • CDocManager(文档管理类)

  • win32程序书写:参与架构的四个类除了应用程序外,其余三个类均支持动态创建机制

  • 执行过程

theApp|->m_pDocManager//文档管理类对象地址|->m_templateList//单文档模板类对象地址|->CSingleDocTemplate *pTemplate|->m_pOnlyDoc//唯一的文档类对象地址|->m_pDocClass//RUNTIME_CLASS(CMyDoc),|->m_pFrameClass//RUNTIME_CLASS(CMyFrameWnd), |->m_ppViewClass//RUNTIME_CLASS(CMyView),
  • example
#include <afxwin.h>
#include "resource.h"class CMyDoc: public CDocument{DECLARE_DYNCREATE(CMyDoc)
};
IMPLEMENT_DYNCREATE(CMyDoc,CDocument)class CMyView :public CView {DECLARE_DYNCREATE(CMyView)
public:virtual void OnDraw(CDC *pDC);
};
IMPLEMENT_DYNCREATE(CMyView,CView)void CMyView::OnDraw(CDC *pDC) {pDC->TextOut(100, 100, "view window");
}class CMyFrameWnd :public CFrameWnd {DECLARE_DYNCREATE(CMyFrameWnd)
};
IMPLEMENT_DYNCREATE(CMyFrameWnd,CFrameWnd)class CMyWinApp :public CWinApp {
public:virtual BOOL InitInstance();
};BOOL CMyWinApp::InitInstance(){CSingleDocTemplate *pTemplate = new CSingleDocTemplate(IDR_MENU1,RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CMyFrameWnd), RUNTIME_CLASS(CMyView));AddDocTemplate(pTemplate);OnFileNew();m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();return TRUE;
};
CMyWinApp theApp;
  • MFC自动生成:善用类向导,创建消息、命令函数。(类视图和类向导,神器)

多文档视图架构

  • 可以管理多份文档,可以有多份数据

  • 参与架构的类(5个)

    CMDIFrameWnd/ CMDIChildWnd/ CWinApp/ CView / CDocument

  • 需要用到的类

    • CDocTemplate(文档模板类)

      ​ |->CMultiDocTemplate(单文档模板类)

    • CDocManager(文档管理类)

  • 两个框架窗口,一个主框架,一个子框架

  • 执行过程

theApp|->m_pDocManager//文档管理类对象地址|->m_templateList//多文档模板类对象地址|->CMultiDocTemplate *pTemplate|->m_docList//保存多个文档类对象地址|->m_pDocClass//RUNTIME_CLASS(CMyDoc)|->m_pFrameClass//RUNTIME_CLASS(CMyChild)|->m_pViewClass//RUNTIME_CLASS(CMyView)

MFC绘图

  • WINDOWS下的绘图,需要 绘图设备

    • WIN32——绘图设备句柄(HDC)

    • MFC——类对象

    • 所以类对象要和HDC绑定一对一的关系才行

  • 绘图相关类

    • CDC类(绘图设备类):封装了各种绘图相关的函数,以及两个非常重要的成员变量m_hDC和m_hAttribDC(这两个成员变量保存着绘图设备句柄)

      • CPaintDC类,封装了在WM_PAINT消息中绘图的绘图设备
      • CClientDC类,封装了在客户区绘图的绘图设备
    • CGdiObject(绘图对象类):封装了各种绘图对象相关的操作,以及一个非常重要的成员变量m_hObject(绘图对象句柄)
      • CPen类,封装了画笔的操作;
      • CBrush类,封装了画刷的操作;
      • CFont类,封装了字体的操作;
      • CBitmap,封装了位图的操作;
  • example:在view类下面创建消息(使用类向导)

//窗口一变即OnPaint,可以一直存在,即WM_PAINT消息
void CMFCDrawView::OnPaint()
{CPaintDC dc(this); // device context for painting// TODO: 在此处添加消息处理程序代码// 不为绘图消息调用 CView::OnPaint()//dc.Rectangle(100, 100, 300, 300);//画家在视图窗口上画矩形
}//不能一直保持,窗体大小改变即消失,需要重现点击
void CMFCDrawView::OnClient()
{// TODO: 在此添加命令处理程序代码CClientDC dc(this);dc.Ellipse(300, 300, 500, 500);
}//画笔绘制矩形
void CMFCDrawView::OnPen()
{// TODO: 在此添加命令处理程序代码CClientDC dc(this);//创建设备CPen pen(PS_SOLID, 2, RGB(255, 0, 0));//内部句柄已经绑定CPen *oldpen= dc.SelectObject(&pen);//把笔给设备,并返回老的笔dc.Rectangle(100, 100, 300, 300);//绘图设备绘图dc.SelectObject(oldpen);//选择老的笔pen.DeleteObject();//销毁创建的笔
}//画刷填充矩形蓝色
void CMFCDrawView::OnBrush()
{// TODO: 在此添加命令处理程序代码CClientDC dc(this);CBrush brush(RGB(0, 0, 255));CBrush *oldbrush = dc.SelectObject(&brush);dc.Rectangle(100, 100, 300, 300);dc.SelectObject(oldbrush);brush.DeleteObject();
}//输出不同字体
void CMFCDrawView::OnFont()
{// TODO: 在此添加命令处理程序代码CClientDC dc(this);CFont font;VERIFY(font.CreatePointFont(120, _T("黑体"), &dc));//断言判断CFont* def_font = dc.SelectObject(&font);dc.TextOut(5, 5, _T("Hello"), 5);dc.SelectObject(def_font);font.DeleteObject();}void CMFCDrawView::OnBitmap()
{/*//添加位图资源,可视化,不需写代码//创建一个和当前DC相匹配的内存DCCClientDC dc(this);CDC memdc;memdc.CreateCompatibleDC(&dc);//将位图数据送给内存DCCBitmap bmp;bmp.LoadBitmap(IDB_BITMAP1);CBitmap *oldbmp = memdc.SelectObject(&bmp);//成像dc.BitBlt(100, 100, 100,100, &memdc, 0, 0, SRCCOPY);//将位图数据要回来memdc.SelectObject(oldbmp);//销毁位图bmp.DeleteObject();//销毁内存DCmemdc.DeleteDC();*///方式二:居中视图CClientDC dc(this);CBitmap bmp;if (bmp.LoadBitmap(IDB_BITMAP1)){// 获取文件大小BITMAP bmpInfo;bmp.GetBitmap(&bmpInfo);//创建一个和当前DC相匹配的内存DCCDC dcMemory;dcMemory.CreateCompatibleDC(&dc);//将位图数据送给内存DCCBitmap* pOldBitmap = dcMemory.SelectObject(&bmp);// 找到居中区域CRect rect;GetClientRect(&rect);int nX = rect.left + (rect.Width() - bmpInfo.bmWidth) / 2;int nY = rect.top + (rect.Height() - bmpInfo.bmHeight) / 2;//显示到居中区域dc.BitBlt(nX, nY, bmpInfo.bmWidth, bmpInfo.bmHeight, &dcMemory,0, 0, SRCCOPY);dcMemory.SelectObject(pOldBitmap);dcMemory.DeleteDC();}else{TRACE0("ERROR: Where's IDB_BITMAP1?\n");AfxMessageBox(_T("error"));}bmp.DeleteObject();
}

序列化机制

  • MFC六大机制

    • 程序启动机制
    • 窗口创建机制
    • 消息映射机制
    • 运行时类信息机制
    • 动态创建机制
    • 序列化机制
  • 文件操作相关类:

    • CFile——文件操作类,封装了关于文件读写等操作

      • CFile::Open//创建和打开文件
      • CFile::Write/Read
      • CFile::Close
      • CFile::SeekToBegin/SeekToEnd/Seek
      • example
      #include<afxwin.h>
      #include<iostream>
      using namespace std;
      void File() {CFile file;file.Open(_T("E:/pro_test/MFC/MFCFile/file.txt"), CFile::modeCreate | CFile::modeReadWrite);//没有就新建,有就读写char str[] = "hello file";file.Write(str, strlen(str));file.SeekToBegin();//从头开始读写char buf[256] = {0};//初始化long nlen = file.Read(buf, 255);//返回字节数cout << buf << " " << nlen << endl;file.Close();}
      int main() {File();return 0;
      }
      
  • 序列化的基本类型

    • 以二进制流的形式读写硬盘文件,但效果很高。
    • 序列化机制的相关类
      • CFile——文件操作类,完成硬盘文件的读写操作;
      • CAchive——归档类,完成内存数据的读写操作;
  • 序列化机制的使用:

    • 创建或打开文件——CFile::Open
    • 定义归档类对象——CArchive ar
    • 数据序列化(存储/写) ar<<数据
    • 关闭归档类对象 ar.Close()
    • 关闭文件 CFile::Close()
  • example

void Store() {//序列化(创建/加载/写)CFile file;file.Open(_T("E:/pro_test/MFC/MFCFile/serial.txt"), CFile::modeCreate | CFile::modeWrite);CArchive ar(&file, CArchive::store, 4096);//归档类对象,维护缓冲区,大小4096字节long age = 18;ar << age;float score = 88.5;ar << score;CString name = "zhangsan";cout << name << endl;ar << name;ar.Close();file.Close();}void Load() {//反序列化(加载/读)CFile file;file.Open(_T("E:/pro_test/MFC/MFCFile/serial.txt"), CFile::modeRead);CArchive ar(&file, CArchive::load, 4096);long age;ar >> age;float score;ar >> score;CString name;ar >> name;ar.Close();file.Close();cout << age << " " << score << " " << name << endl;
}int main() {Store();Load();return 0;
}

序列化对象

  • 序列化对象的使用方法

    • 类必须派生自CObject
    • 类内必须添加声明宏 DECLARE_SERIAL(theClass)
    • 类外必须添加实现宏 IMPLEMENT_SERIAL(theClass, baseClass, 1)
    • 类必须重写虚函数 Serialize
    • 当类满足以上四个要件的时候,类对象就可以序列化到文件中保存了。
  • example

#include<afxwin.h>
#include<iostream>
using namespace std;class CMyDoc :public CDocument {DECLARE_SERIAL(CMyDoc)
public:CMyDoc(int age = 0,float score= 0.0,CString name=""):m_age(age),m_score(score),m_name(name){}int m_age;float m_score;CString m_name;virtual void Serialize(CArchive &ar);
};
IMPLEMENT_SERIAL(CMyDoc,CDocument,1)void CMyDoc::Serialize(CArchive &ar) {if (ar.IsStoring()) {ar << m_age << m_score << m_name;}else {ar >> m_age >> m_score >> m_name;}
}void Store() {//序列化(创建/加载/写)CFile file;file.Open(_T("E:/pro_test/MFC/MFCFile/serial_class.txt"), CFile::modeCreate | CFile::modeWrite);CArchive ar(&file, CArchive::store, 4096);//归档类对象,维护缓冲区CMyDoc data(18, 88.5, "zhangsan");ar << &data;ar.Close();file.Close();}void Load() {//反序列化(加载/读)CFile file;file.Open(_T("E:/pro_test/MFC/MFCFile/serial_class.txt"), CFile::modeRead);CArchive ar(&file, CArchive::load, 4096);CMyDoc *pdate = NULL;ar >> pdate;ar.Close();file.Close();cout << pdate->m_age << " " << pdate->m_score << " " << pdate->m_name << endl;
}int main() {Store();Load();return 0;
}

无模式对话框

  • 创建方法

    • 添加对话框资源
    • 查找资源 FindResource
    • 加载资源 LoadResource
    • 锁定资源 LockResource
    • 创建无模式对话框 CreateDialogIndirect
  • 参与架构的类 CDialog/ CWinApp

  • 代码书写

    • 添加对话框资源
    • 定义一个自己的对话框类(CMyDlg),管理对话框资源,派生自CDialog或CDialogEx均可
    • 其余代码详见以下的

对象和控件绑定

  • 一定是在对话框类里面进行成员变量的绑定,而不是应用程序类

  • 将控件窗口和类对象绑定具有两大作用

    • 如果和数据类对象绑定(CString等),对象和控件可以进行数据交换
    • 如果和控件类对象绑定(CButton等),对象可以代表整个控件
  • 和数据类对象绑定的使用

    • 重写父类虚函数DoDateExchange,在函数内部通过一系列的DDX_xxx函数,实现控件和数据类型对象的数据交互
    • 如果需要实现数据交互,调用UPdateData函数
      • UpdataData(TRUE):控件->变量
      • UpdataData(TRUE):变量->控件

滑块控件——Slide Control

  • dialog资源文件设置,插入控件,更改ID
  • 设置成员变量,使用类向导,绑定控件
  • OnInit函数初始化
BOOL CMFCDialgDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动//  执行此操作SetIcon(m_hIcon, TRUE);           // 设置大图标SetIcon(m_hIcon, FALSE);        // 设置小图标// TODO: 在此添加额外的初始化代码//滑块初始化m_slider.SetRange(0, 100);m_slider.SetPos(50);return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}
  • 消息映射机制,拉动使用,发出WM_COMMAND消息(通知码NM_CUSTOMDRAW)

PS:可双击控件直接创建,也可使用类向导创建

void CMFCDialgDlg::OnNMCustomdrawSlider1(NMHDR *pNMHDR, LRESULT *pResult)
{LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);// TODO: 在此添加控件通知处理程序代码int pos = m_slider.GetPos();//获取位置CString strPos;strPos.Format(_T("滑块位置%d"), pos);this->SetWindowText(strPos);*pResult = 0;
}

进度条控件——Progress Control

  • CProgressCtrl类

    • SetRange
    • SetPos
    • GetPos
  • 操作同滑块控件
BOOL CMFCDialgDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动//  执行此操作SetIcon(m_hIcon, TRUE);           // 设置大图标SetIcon(m_hIcon, FALSE);        // 设置小图标// TODO: 在此添加额外的初始化代码//初始化m_slider.SetRange(0, 100);m_slider.SetPos(50);m_progress.SetRange(0, 100);m_progress.SetPos(50);return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}//联动滑块和进度条
void CMFCDialgDlg::OnNMCustomdrawSlider1(NMHDR *pNMHDR, LRESULT *pResult)
{LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);// TODO: 在此添加控件通知处理程序代码int pos = m_slider.GetPos();CString strPos;strPos.Format(_T("滑块位置%d"), pos);this->SetWindowText(strPos);m_progress.SetPos(pos);*pResult = 0;
}

下压式按钮——CButton类

  • 消息WM_COMMAND,消息码(BN_CLICKED)

  • 对话框类绑定成员变量——类向导

  • 双击即可创建消息函数

  • 一般没有数据交换

编辑框——Edit Control

  • 应该先输入完然后和按钮联动,这样效果比较好

  • //只是例子,效果是每一个输入都会有弹框,即都有反应
    void CMFCDialgDlg::OnEnChangeEdit1()
    {// TODO:  在此添加控件通知处理程序代码TCHAR bud[256];int length=m_edit.GetWindowText(bud,256);AfxMessageBox(bud);
    }
    

复选框按钮——Check Box

  • 勾选和非勾选两种状态
  • 仍然是CButton类
  • m_check.GetCheck()——勾选即返回1
  • m_check.SetCheck(!m_check.GetCheck())
  • 属性的push_like是那个更改为TRUE,即变成多态按钮

单选框按钮——Radio Button

  • 仍然是CButton类
  • 一组单选框只能选一个
  • 一般不出现在类向导里面,要在属性里面的group框选为TRUE才可以
  • 互斥的关系要在点击单选框的消息函数里面自己写

分组框按钮——gruop box

  • 提高界面友好性
  • 仍然是CButton类
  • 就是框选单选按钮,看的更清楚,就是示意
  • ID: IDC_STATIC——不会出现在类向导里,更改之后可出现

文本静态框——Static Text

  • SetWindowText可设置内容,这是父类CWin的函数,一般控件都可用;
  • 但是文本静态一般只是展示,提高界面友好性的
  • ID: IDC_STATIC——不会出现在类向导里,更改之后可出现

图像静态框——Picture Control

  • 属性的杂项的type->Icon/Bitmap,一般是这两种类型
  • 属性里面的image可以一开始设置图片
  • ID: IDC_STATIC——不会出现在类向导里,更改之后可出现,如更改成IDC_PS
  • 显示图像操作
    • CStatic::SetIcon
    • CStatic::SetBitmap
  • example
m_ps.SetBitmap(::LoadBitmap(AfxGetInstanceHandle,MAKEINTRESOURCE()));//需要位图的句柄;如果是加载本程序的资源位图ID的话就需要获取当前程序的实例句柄和资源ID

组合框——Combo Box

  • 一般是下拉式,支持组合框编辑
  • 下拉链表时,不支持组合框编辑
  • CComboBox::AddString()——添加组合框选项
  • CComboBox::DeleteString——删除选项
  • CComboBox::GetCurSel——获取选择性索引
  • 获取选项文本内容——CComboBox::GetLBText
  • 选择项发生变化的时候,会发送WM_COMMAND消息

列表框——List Box

  • CListBox类
  • 常见操作
    • 添加选项CListBox::AddString
    • 删除选项CListBox::DeleteString
    • 获取选择项索引 CListBox::GetCurSel
    • 获取选项文本内容 CListBox::GetItemText

列表控件——List Control

  • CListCtrl类
  • 风格
    • 图标(LVS_ICON)
    • 小图标(LVS_SMALLICON)
    • 列表(LVS_LIST)
    • 报表(详细信息)(LVS_REPORT)
  • 修改风格 CWnd::ModifyStyle
  • 常见操作
    • 添加选项
    • 删除选项
    • 添加列
    • 设置文本内容
    • 设置图像
    • 设置附加数据
    • 获取附加数据
  • 当列表被双击,发出WM_COMMAND消息,通知码(LBN_DBLCLK)

树控件——Tree Control

  • CTreeCtrl
  • 常见操作

子对话框的初始化

  • 打开类向导——>找到虚函数一列——>找到initdialog函数双击它,这个时候你的cpp文件就有init函数生成了
  • 属性->border选none,关闭打叉按钮
  • 属性->style选child,选择为子对话框

编辑框

以下几个函数都是CWnd类的成员函数:

GetDlgItemInt将所给的控件中的字符串转化int型,并且返回该值;
GetDlgItemText函数返回拷贝的字节数,并把控件中的内容赋值到一个变量中;
SetDlgItemText函数将某一个控件的内容设定为某个值。

整型和字符串相互转化

//整型转字符串
CString d;
d.Format(_T("%d"),c);
AfxMessageBox(d);
//字符串转整型
int e = _ttoi(_T("123"));

单文档、多文档、对话框

  • 单文档只能进行一份文档或图片的操作,就是你不能同时在一个程序打开两个文件。
  • 多文档可以用多个窗口显示不同的信息,进行不同的任务,有多个视图环境,可以同时操作多个文件。
  • 对话框模板适合于做交互界面;单文档模板适合于做文件处理;多文档模板适合于做多文件处理。
  • 从源码中的类型来看:
    • 基于对话框—>一般含以下3个类:CAboutDlg、程序名App、程序名Dlg
    • 基于单文档—>一般含以下4个类:CMainFrame、程序名App、程序名Doc、程序名View
    • 基于多文档—>一般含以下5个类:CMainFrame、CChildFrame、程序名App、程序名Doc、程序名View

对话框的句柄和指针处理

  • 按照资源的类型划分,句柄又分为以下几种:

    • HINSTANCE – 实例的句柄

    • HWND – 窗口的句柄,操作系统给窗口的唯一标识号。

    • HCURSOR – 光标的句柄

    • HICON – 图标句柄

  • 获取窗口句柄及其相关函数

    //首先,窗口句柄,在窗口类中直接使用成员变量m_hWnd,在窗口外最常见是用AfxGetMainWnd (获取主窗口指针,其成员变量m_hWnd为主窗口句柄):
    HWND hWnd = AfxGetMainWnd()->m_hWnd;//返回对话框主窗口句柄GetTopWindow
    //函数功能:该函数检查与特定父窗口相联的子窗口z序(Z序:垂直屏幕的方向,即叠放次序),并返回在z序顶部的子窗口的句柄。
    函数原型:HWND GetTopWindow(HWND hWnd);
    参数:hWnd:被查序的父窗口的句柄。如果该参数为NULL,函数返回Z序顶部的窗口句柄。
    返回值:如果函数成功,返回值为在Z序顶部的子窗口句柄。如果指定的窗口无子窗口,返回值为NULL。GetForegroundWindow
    //函数功能:该函数返回当前系统的前台窗口的窗口句柄。
    函数原型:HWND GetForegroundWindow(VOID)  
    返回值:函数返回前台窗回的句柄。GetActiveWindow
    //函数功能: 获取当前窗口句柄,该函数可以获得与调用该方法的线程的消息队列相关的活动窗口的窗口句柄(就是取得当前进程的活动窗口的窗口句柄)。
    函数原型:HWND GetActiveWindow(VOID)
    返回值:返回值是与调用线程的消息队列相关的活动窗口的句柄。否则,返回值为NULL。GetSafeHwnd
    //函数功能:获取某个窗口对象(CWnd的派生对象)指针的句柄(HWND)时,最安全的方法是使用GetSafeHwnd()函数。通过下面的例子来看其理由:CWnd *pwnd = FindWindow(“ExploreWClass”,NULL); //希望找到资源管理器HWND hwnd = pwnd->m_hwnd; //得到它的HWND
    //这样的代码当开始得到的pwnd为空的时候就会出现一个“General protection error”,并关闭应用程序,因为一般不能对一个NULL指针访问其成员,如果用下面的代码,就不会出现问题,因为尽管当pwnd是NULL时,GetSafeHwnd仍然可以用,只是返回NULLCWnd *pwnd = FindWindow(“ExploreWClass”,NULL); //希望找到资源管理器   HWND hwnd = pwnd->GetSafeHwnd(); //得到它的HWND IsWindowVisible
    //函数功能:该函数获得给定窗口的可视状态。
    函数原型:BOOL IsWindowVisible(HWND hWnd);
    参数;hWnd:被测试窗口的句柄。
    返回值:如果指定的窗口及其父窗口具有WS_VISIBLE风格,返回值为非零;如果指定的窗口及其父窗口不具有WS_VISIBLE风格,返回值为零。由于返回值表明了窗口是否具有Ws_VISIBLE风格,因此,即使该窗口被其他窗口遮盖,函数返回值也为非零。
    //备注:窗口的可视状态由WS_VISIBLE位指示。当设置了WS_VISIBLE位,窗口就可显示,而且只要窗口具有WS_VISIBLE风格,任何画在窗口的信息都将被显示。IsWindow:
    //函数功能:该函数确定给定的窗口句柄是否标示一个已存在的窗口。 
    函数原型:BOOL IsWindow(HWND hWnd);
    参数:hWnd:被测试窗口的句柄。
    返回值:如果窗口句柄标识了一个已存在的窗口,返回值为TURE;如果窗口句柄未标识一个已存在窗口,返回值为FALSE。FindWindow:
    /*函数功能:该函数获得一个顶层窗体的句柄,该窗体的类名和窗体名与给定的字符串相匹配。这个函数不查找子窗体。在查找时不区分大写和小写。函数型:HWND FindWindow(LPCTSTR IpClassName,LPCTSTR IpWindowName);
    參数:IpClassName :指向一个指定了类名的空结束字符串,或一个标识类名字符串的成员的指针。假设该參数为一个成员,则它必须为前次调用theGlobafAddAtom函数产生的全局成员。该成员为16位,必须位于IpClassName的低 16位,高位必须为 0。IpWindowName:指向一个指定了窗体名(窗体标题)的空结束字符串。假设该參数为空,则为全部窗体全匹配。返回值:假设函数成功,返回值为具有指定类名和窗体名的窗体句柄;假设函数失败,返回值NULL。
    */FindWindowEx:
    /*函数功能:该函数获得一个窗口的句柄,该窗口的类名和窗口名与给定的字符串相匹配。这个函数查找子窗口,从排在给定的子窗口后面的下一个子窗口开始。在查找时不区分大小写。函数原型:HWND FindWindowEx(HWND hwndParent,HWND hwndChildAfter,LPCTSTR lpszClass,LPCTSTR lpszWindow);参数:hwndParent:要查找子窗口的父窗口句柄。如果hwnjParent为NULL,则函数以桌面窗口为父窗口,查找桌面窗口的所有子窗口。Windows NT5.0 and later:如果hwndParent是HWND_MESSAGE,函数仅查找所有消息窗口。hwndChildAfter:子窗口句柄。查找从在Z序中的下一个子窗口开始。子窗口必须为hwndPareRt窗口的直接子窗口而非后代窗口。如果HwndChildAfter为NULL,查找从hwndParent的第一个子窗口开始。如果hwndParent和 hwndChildAfter同时为NULL,则函数查找所有的顶层窗口及消息窗口。lpszClass:指向一个指定了类名的空结束字符串,或一个标识类名字符串的成员的指针。如果该参数为一个成员,则它必须为前次调用theGlobaIAddAtom函数产生的全局成员。该成员为16位,必须位于lpClassName的低16位,高位必须为0。lpszWindow:指向一个指定了窗口名(窗口标题)的空结束字符串。如果该参数为NULL,则为所有窗口全匹配。返回值:如果函数成功,返回值为具有指定类名和窗口名的窗口句柄。如果函数失败,返回值为NULL。*/SetWindowText
    //窗口标题的改变,我们可以通过SetWindowText来实现:AfxGetMainWnd ( ) -> SetWindowText (_T("Application title") )  
  • 获取控件句柄

    hwndScroll = GetDlgItem(hwnd, IDC_SCROLL);
    /*假设一个父窗口中有多个子窗口。那么本函数是返回一个子窗口句柄。
    第一个参数:父窗口句柄
    第二个参数:子窗口ID
    如上代码 父窗口是一个对话框,子窗口是对话框中的一个滚动条
    */
    1.  CWnd   *pWnd   =   GetDlgItem(ID_***); // 取得控件的指针
    2.  HWND hwnd = pWnd->GetSafeHwnd();  // 取得控件的句柄  3.  取得CDC的指针是CDC* pdc = pwnd->GetWindowDC();
  • 注意点

    在提取到各个句柄之后,因为初次提取的都是标准类句柄,所以,在使用时要注意将标准句柄转换成自己的类的句柄。
    如:
    AfxGetApp();//得到的是WinApp类的句柄,
    所以操作前记得转换成自己定义的类的句柄。
    如:
    ((CMyApp*)AfxGetApp())->XXXX();//这的xxxx()就是你定义的类中间的成员。
  • 通过句柄获取指针

    CWnd::FromHandle与CWnd::FromHandlePermanent的区别
    /*FromHandle 和 FromHandlePermanent 的实现,这两个函数都是在公共的 CMapHandle 中查找句柄对应的 CWnd 对象(通过一个CBT钩子,CWnd 对象将创建时得到的句柄和自己的指针纪录到 CMapHandle)区别是如果找不到相关的对象,FromHandle 在CMapHandle 的 temporarylist 中创建并返回一个临时对象的指针 ,而 FromHandlePermanent 返回 NULL(此外 FromHandlePermanent 不使用 temporarylist ,所以不查找 temporarylist 下的句柄)
    */
    
  • 经验之谈

    无论是主窗口的tap控件创建的子窗口,还是其他子窗口,都可以在父窗口的OnInit函数里面Create类对象并和父窗口关联后,直接设置一个新的HWND句柄成员变量,保存在父类里面,这时候可以直接在子窗口调用父窗口的成员变量完成两个子窗口的交互

    //方法一
    CRect rect;
    C3DReconstructionDlg* pMainWnd = (C3DReconstructionDlg*)AfxGetMainWnd();
    HWND h_r = pMainWnd->m_right_h;//子窗口句柄
    HWND Right_Hwnd = pMainWnd->m_rightImg_show.m_right_pic.m_hWnd;//子窗口控件句柄
    CrightImg * C_r =(CrightImg *) FromHandle(Right_Hwnd);//返回控件指针
    C_r->GetClientRect(&rect);//指针调用成员函数//方法二:直接通过父窗口的成员类访问其成员函数
    CRect rect;
    C3DReconstructionDlg* pMainWnd = (C3DReconstructionDlg*)AfxGetMainWnd();
    HWND h_r = pMainWnd->m_right_h;
    HWND Right_Hwnd = pMainWnd->m_rightImg_show.m_right_pic.m_hWnd;
    pMainWnd->m_rightImg_show.m_right_pic.GetClientRect(&rect);
    

MFC常见函数

  • MoveWindow(&rect):设置控件大小和位置

  • GetDlgItem(ID_***):获取控件的指针

  • SetTimer():设置定时器

  • CreateDirectory(LPCTSTR lpPathName,LPSECURITY_ATTRIBUTES lpSecurityAttributes
    );简介:CreateDirectory 是Win32API函数,用于创建文件夹。参数 lpPathName 表示路径参数 lpSecurityAttributes 表示安全属性所以使用要如下:(加域符)::CreateDirectory(m_target_point, NULL);//创建文件夹
    
  •   如果我们在程序运行界面中输入被加数,则通过CAddition的DoDataExchange()函数可以将输入的值保存到m_editSummand变量中,反之如果程序运行中修改了变量m_editSummand的值,则通过CAddition的DoDataExchange()函数也可以将新的变量值显示到被加数的编辑框中。但是这种数据交换机制中,DoDataExchange()并不是被自动调用的,而是需要我们在程序中调用CDialogEx::UpdateData()函数,由UpdateData()函数再去自动调用DoDataExchange()的。CDialogEx::UpdateData()函数的原型为:BOOL UpdateData(BOOL bSaveAndValidate = TRUE);参数:bSaveAndValidate用于指示数据传输的方向,TRUE表示从控件传给变量,FALSE表示从变量传给控件。默认值是TRUE,即从控件传给变量。返回值:CDialogEx::UpdateData()函数的返回值表示操作是否成功,成功则返回TRUE,否则返回FALSE。
    

MFC定时器设置步骤

  • 设置定时器ID和时间、回调(NULL表示使用默认的消息队列WM_TIMER)

    • SetTimer(1, 1000, NULL);//设置定时器ID为1,时间1s

    • 参数nIDEvent指定一个非零的定时器ID;

      参数nElapse指定间隔时间,单位为毫秒;

      参数lpfnTimer指定一个回调函数的地址,如果该参数为NULL,则WM_TIMER消息被发送到应用程序的消息队列,并被CWnd对象处理。如果此函数成功则返回一个新的定时器的ID,我们可以使用此ID通过KillTimer成员函数来销毁该定时器,如果函数失败则返回0。

    • CWnd类的SetTimer成员函数只能在CWnd类或其派生类中调用,而API函数SetTimer则没有这个限制,这是一个很重要的区别。

    • 通过SetTimer成员函数我们可以看出,处理定时事件可以有两种方式,一种是通过WM_TIMER消息的消息响应函数,一种是通过回调函数。

    • 如果要启动多个定时器就多次调用SetTimer成员函数。另外,在不同的CWnd中可以有ID相同的定时器,并不冲突。

  • 调用WM_TIMER消息

    • 采用类向导,消息里面可以直接生成

    • 多个定时器可以这么写

    • void DIALOG_FindPoint::OnTimer(UINT_PTR nIDEvent)
      {// TODO: 在此添加消息处理程序代码和/或调用默认值if (m_PointNum > 20) {KillTimer(1);CDialog::OnTimer(nIDEvent);//自己可以设置标志销毁定时器return;}else {switch (nIDEvent) {//采集图片case 1: {}case 2:{}...}CDialog::OnTimer(nIDEvent);return;}
      }
      

MFC多线程编程

  • MFC中有两类线程,分别称之为工作者线程和用户界面线程。二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环。

  • 工作者线程没有消息循环,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。

  • 用户界面线程一般用于处理独立于其他线程之外的用户输入,响应用户及系统产生的事件和消息等。

  • 但对于Win32的API编程而言,这两种编程是没有区别的,他们都只需要线程的启动地址即可启动线程来执行任务。

  • 在MFC中,一般用全局函数AfxBeginThread()来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作者线程和用户界面线程。两种重载函数原型和参数分别说明如下:

    • CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam,int nPriority = THREAD_PRIORITY_NORMAL,UNT nStackSize = 0,DWORD dwCreateFlags = 0,LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);//用于创建工作者线程PfnThreadProc:指向工作者线程的执行函数的指针,线程函数原型必须声明如下:UINT ExecutingFunction(LPVOID pParam);请注意,ExecutingFunction()应返回一个UINT类型的值,用以指明该函数结束的原因。一般情况下,返回0表明执行成功。pParam:一个32位参数,执行函数将用某种方式解释该值。它可以是数值,或是指向一个结构的指针,甚至可以被忽略;nPriority:线程的优先级。如果为0,则线程与其父线程具有相同的优先级;nStackSize:线程为自己分配堆栈的大小,其单位为字节。如果nStackSize被设为0,则线程的堆栈被设置成与父线程堆栈相同大小;dwCreateFlags:如果为0,则线程在创建后立刻开始执行。如果为CREATE_SUSPEND,则线程在创建后立刻被挂起;lpSecurityAttrs:线程的安全属性指针,一般为NULL;表示新创建的线程具有和主线程一样的安全性.
      
    • CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,int nPriority = THREAD_PRIORITY_NORMAL,UNT nStackSize = 0,DWORD dwCreateFlags = 0,LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);//用于创建用户界面线程pThreadClass 是指向 CWinThread 的一个导出类的运行时类对象的指针,该导出类定义了被创建的用户界面线程的启动、退出等;其它参数的意义同形式1。使用函数的这个原型生成的线程也有消息机制,在以后的例子中我们将发现同主线程的机制几乎一样。下面我们对CWinThread类的数据成员及常用函数进行简要说明。m_hThread:     当前线程的句柄;m_nThreadID:   当前线程的ID;m_pMainWnd: 指向应用程序主窗口的指针BOOL CWinThread::CreateThread(DWORD dwCreateFlags=0,UINT nStackSize=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);该函数中的dwCreateFlags、nStackSize、lpSecurityAttrs参数和API函数CreateThread中的对应参数有相同含义,该函数执行成功,返回非0值,否则返回0。一般情况下,调用AfxBeginThread()来一次性地创建并启动一个线程,但是也可以通过两步法来创建线程:首先创建CWinThread类的一个对象,然后调用该对象的成员函数CreateThread()来启动该线程。virtual BOOL CWinThread::InitInstance();重载该函数以控制用户界面线程实例的初始化。初始化成功则返回非0值,否则返回0。用户界面线程经常重载该函数,工作者线程一般不使用InitInstance()。virtual int CWinThread::ExitInstance();在线程终结前重载该函数进行一些必要的清理工作。该函数返回线程的退出码,0表示执行成功,非0值用来标识各种错误。同InitInstance()成员函数一样,该函数也只适用于用户界面线程。
      
  • 线程同步

    • 在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解。正常情况下对这种处理结果的了解应当在其处理任务完成后进行。
    • 如果不采取适当的措施,其他线程往往会在线程处理任务结束前就去访问处理结果,这就很有可能得到有关处理结果的错误了解。例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。
    • 为了确保读线程读取到的是经过修改的变量,就必须在向变量写入数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。象这种保证线程能了解其他线程任务处理结束后的处理结果而采取的保护措施即为线程同步。
    • 线程的同步可分用户模式的线程同步和内核对象的线程同步两大类。
    • 用户模式中线程的同步方法主要有原子访问和临界区等方法。其特点是同步速度特别快,适合于对线程运行速度有严格要求的场合。
    • 内核对象的线程同步则主要由事件、等待定时器、信号量以及信号灯等内核对象构成。由于这种同步机制使用了内核对象,使用时必须将线程从用户模式切换到内核模式,而这种转换一般要耗费近千个CPU周期,因此同步速度较慢,但在适用性上却要远优于用户模式的线程同步方式。(内核互斥)
  • 参考blog。

  • AfxBeginThread是MFC的全局函数,是对CreateThread的封装。  CreateThread是Win32 API函数,AfxBeginThread最终要调到CreateThread。而_beginthread是C的运行库函数。
    //例子
    UINT  myproc(LPVOID  lParam)
    {
    CITTDlg *pWnd = (CITTDlg *)lParam;         //将窗口指针赋给无类型指针
    pWnd->KMeansSegment();                         //要执行的函数
    return 0;
    }void CITTDlg::KMeansSegment()
    {
    // 主要处理函数在这里写
    }void CITTDlg::OnKMeansSegment()             //按钮点击执行
    {AfxBeginThread(myproc, (LPVOID)this);//启动新的线程}  
  • 如果要在线程内结束线程,可以在线程内调用 AfxEndThread.

    一般直接用AfxBeginThread(ThreadProc,this);等待返回值即线程结束,一般返回零

    注意,工作者线程的函数必须是全局函数或静态成员函数,不能是普通的成员函数

  • 函数: GetExitCodeThread()
    功能: 获取一个结束线程的返回值
    函数原形: BOOL GetExitCodeThread( HANDLE hThread, LPDWORD lpExitCode);
    参数: hThread 指向欲获取返回值的线程对象的句柄lpExitCode 用于存储线程的返回值(一般设置为0),若为STILL_ACTIVE则说明线程未结束。返回值:函数执行成功则返回非0值,否则返回 0(FALSE)
    
  • //afxbeginthread创建的线程返回值为一个指针。不过通过这个指针m_pthread2,可以找到m_hThread,即线程句柄m_pthread1 = AfxBeginThread(hellothread,this,THREAD_PRIORITY_NORMAL,0,0);
    SuspendThread(m_pthread2->m_hThread); //挂起第一个线程。“暂停”
    ResumeThread(m_pthread2->m_hThread); //释放第一个线程。”播放“//线程正常return后,句柄会自动销毁,资源全部收回。不需要CloseHandle(),这里是对于MFC的AfxBeginThread函数而言
    

线程互斥

  • CCriticalSection类实现线程同步

    当多个线程访问一个独占性共享资源时,可以使用Critical Section(临界区)对象。任一时刻只有一个线程可以拥有临界区对象,拥有临界区的线程可以访问被保护起来的资源或代码段,其他希望进入临界区的线程将被挂起等待,直到拥有临界区的线程放弃临界区时为止。因此,任一时刻,只有一个线程可以拥有临界区对象,而只有拥有临界区对象的线程才可以访问受保护的数据。

    MFC的CCriticalSection类提供了对临界区对象的支持,其用法也相当简单,有两种用法。

    • 1.单独使用CCriticalSection对象

    1)定义CCriticalSection类的一个全局对象,以使各个线程均能访问,例如:CCriticalSection criticalsection;//CCriticalSection类的构造函数只有一种形式,即不带任何参数

    2)在访问需要保护的资源或代码之前,调用CCriticalSection类的成员函数Lock获得临界区对象。代码如下:critical section.Lock();

    如果此时没有其他线程占有临界区对象,则调用Lock函数的线程获得临界区;否则,线程即将挂起,并放人到一个系统队列中等待,直到当前拥有临界区的线程释放了临界区时为止。

    3)访问临界区完毕后,使用CCriticalSection的成员函数Unlock来释放临界区。代码如下:critical section.Unlock()

    • 2.与同步辅助类CSingleLock或CMutiLock一起使用

    下面以类CSingleLock为例,简单说明使用步骤:

    1)定义CCriticalSection类的一个全局对象,格式如下:CCriticalSection critical_section;

    2)在访问需要保护的资源之前,定义CSingleLock类的一个变量,并将critical_ section的地址传送给构造函数:CSingleLock singlelock(&critical_ section):

    3)使用CSingleLock类的成员函数LOCk请求获得临界区。代码如下:singlelock.Lock();如果临界区已经被其他线程占用,则本线程挂起,等待临界区被释放。获得临界区对象后返回。

    4)本线程中访问临界区中的共享资源后,调用CSingleLock类的成员函数Unlock来释放临界区:singlelock.Unlock();

  • 参考网址

子线程获取主窗口指针

  • 一般在类外面

    • 在窗口外最常见是用AfxGetMainWnd (获取主窗口指针,其成员变量m_hWnd为主窗口句柄):
      HWND hWnd = AfxGetMainWnd()->m_hWnd;//返回对话框主窗口句柄
      
  • 然而在子线程里面不可以,AfxGetMainWnd()得到的是子窗口指针。

  • 但是可以使用AfxGetApp()->m_pMainWnd访问主窗口的指针

    • C3DReconstructionDlg* pMainWnd = (C3DReconstructionDlg*)(AfxGetApp()->m_pMainWnd);
      
  • CWinApp* AfxGetApp( );

    返回值:指向应用程序的单一CWinApp对象的指针。

    说明:
    这个函数返回的指针可以被用来访问应用程序的信息,如主消息调度代码以及顶层窗口等。

    MFC在开发的时候封装了一些全局的API函数,在这些函数的前面都会加上Afx这样的标志,因为是全局的函数,所以你在App类中不会找到,返回值是获得一个CWINApp类指针,至于什么时候会用到,当你要访问CWINApp类成员时候可以用啊,还有共享的时候也可以用的。AfxGetApp();在文档中十分重要,它是一个全局函数,可以取得类的指针。在视图、框架、文档中起到穿针引线的作用,用它可以在它们之间随意的访问调用。

  • 只有CWinThread对象才有m_pMainWnd。(例如用户界面线程)

  • InitInstance是用来执行程序每次开始时都需要进行的初始化工作的最好地方。

    • 如果用框架窗口作为主窗口,
      m_pMainWnd = new CYourMainWindow;

    • 如果用对话框作为主窗口,
      CYourDlg dlg;
      m_pMainWnd = &dlg;

    • 以对话框为例子

      • BOOL C3DReconstructionApp::InitInstance()
        {...//重点C3DReconstructionDlg dlg;m_pMainWnd = &dlg;INT_PTR nResponse = dlg.DoModal();//...return FALSE;
        }
        

CImage图像处理类

  • Visual C++的CBitmap类的功能是比较弱的,它只能显示出在资源中的图标、位图、光标以及图元文件的内容,而不像VB中的Image控件可以显示出绝大多数的外部图像文件(BMP、GIF、JPEG等)。如果想要在对话框或其他窗口中显示外部图像文件则只能借助于第三方提供的控件或代码,未免过于繁琐。

  • 现在,.net引入了一个功能非常强大的新类 ----- CImage.有了CImage类,Visual C++在图像方面的缺憾将一去不复返。CImage是MFC和ATL共享的新类,它能从外部磁盘中调入一个JPEG、GIF、BMP和PNG格式的图像文件加以显示,而且这些文件格式可以相互转换。例如通过简单的几句,就可以实现CImage类和CBitmap类实例的:

    • HBITMAP hBitmap=image.Detach();
      CBitmap bmp;
      bmp.Attach(hBitmap);这样一来,就又回归到以前操纵CBitmap的方式了.CImage本身封装了DIB(设备无关位图)的功能,因而能够处理每个位图像素。
      
  • 使用CImage的一般是这样的过程:(1) 打开应用程序的stdafx.h文件添加CImage类的包含文件:#include <atlimage.h>(2) 定义一个CImage类对象,然后调用CImage::Load方法装载一个外部图像文件。(3) 调用CImage::Draw方法绘制图像。Draw方法具有如下定义:
    BOOL Draw( HDC hDestDC, int xDest, int yDest,int nDestWidth, int nDestHeight, int xSrc, int ySrc,int nSrcWidth, int nSrcHeight );
    BOOL Draw( HDC hDestDC, const RECT& rectDest, const RECT& rectSrc );
    BOOL Draw( HDC hDestDC, int xDest, int yDest );
    BOOL Draw( HDC hDestDC, const POINT& pointDest );
    BOOL Draw( HDC hDestDC, int xDest, int yDest, int nDestWidth, int nDestHeight );
    BOOL Draw( HDC hDestDC, const RECT& rectDest );
    //其中,hDestDC用来指定绘制的目标设备环境句柄,(xDest, yDest)和pointDest用来指定图像显示的位置,这个位置和源图像的左上角点相对应。nDestWidth和nDestHeight分别指定图像要显示的高度和宽度,xSrc、ySrc、nSrcWidth和nSrcHeight用来指定要显示的源图像的某个部分所在的位置和大小。 rectDest和rectSrc分别用来指定目标设备环境上和源图像所要显示的某个部分的位置和大小。//例子
    CDC *pDC = pMainWnd->m_rightImg_show.m_right_pic.GetDC();//获得pictrue控件的DC
    CImage img;
    img.Load("1.jpg");
    if (!img.IsNull())
    {  img.Draw(pDC->m_hDC,CRect(0,0,100,100));
    }
    

MFC写入数据到txt

CFile file(_T("bspline.txt"), CFile::modeCreate | CFile::modeWrite);
CString string1, string2, string3;
for (int i = 0; i < bspline.size(); ++i)
{string1.Format(_T("%f"), bspline[i].Getx());string2.Format(_T("%f"), bspline[i].Gety());string3.Format(_T("%d"), i);file.Write("\r\n", 2);file.Write(string3, strlen(string1));file.Write("\0", 1);file.Write(string1, strlen(string1));file.Write("\0", 1);file.Write(string2, strlen(string1));file.Write("\0", 1);
}
file.Close();
  • CFile是MFC的封装的类

  • //input :CString filename
    CString txtname = filename+_T(".txt");
    CFile file(txtname, CFile::modeCreate|CFile::modeWrite);
    CString str,str1;
    str1=_T("\r\n");
    str=_T("");int size = VLine.size();
    for (j=0; j!=size; j++)
    {CString s1;s1.Format(_T("%8.3f\t%-8.3f\t"), height - 1 - VLine[size - 1 - j].y,VLine[size - 1 -j].x);str=str+s1+str1;}
    file.Write(str, str.GetLength());
    file.Close();
    
  •   CString的Format方法给我们进行字符串的转换带来了很大的方便,比如常见的int、float和double这些数字类型转换为CString字符串只需一行代码就可以实现。先看看Format用于转换的格式字符:%c                 单个字符%d                 十进制整数(int)%ld                十进制整数(long)%f                 十进制浮点数(float)%lf                十进制浮点数(double)%o                 八进制数%s                 字符串%u                 无符号十进制数%x                 十六进制数
    
  • %8.3f是右对齐
    %-8.3f是左对齐
    8是最小宽度,如果输出的数不够八位,用0或空格补齐,.3代表精度,精确到小数点后三位,f代表实数
    \t表示制表
    
  • CString有很多方法

    • CString::Empty
      Void Empty( );
      没有返回值  清空操作;
      例子
      CString s( "abc" );
      s.Empty();
      ASSERT( s.GetLength( ) == 0 );
      
    • CString::GetLength
      int GetLength( ) const;
      返回值
      返回字符串中的字符计数。
      说明
      此成员函数用来获取这个CString 对象中的字符计数。这个计数不包括结尾的空字符。CString::GetLength()方法上。当采用Unicode编码时,而且http header字符串中出现了中文或其其他多字节字符,该方法仍旧只返回字符数,而不是实际的字节数,这样在http header中的信息会比实际的少,导致http发送的数据小于预期的。所以::::!!!!CStringA(x_y).GetLength()*2才是实际的字节数(采用UINCODE)或者DWORD le0 = str.GetLength() * sizeof(TCHAR);
      
    • CString::Left
      CString Left( int nCount ) const;
      throw( CMemoryException );
      返回的字符串是前nCount个字符。
      例子
      CString s( _T("abcdef") );
      ASSERT( s.Left(2) == _T("ab") );
      
    • 参考CString。

C++的读写txt

#include <fstream>
ofstream         //文件写操作 内存写入存储设备
ifstream         //文件读操作,存储设备读区到内存中
fstream          //读写操作,对打开的文件可进行读写操作 //写入,保存标记连通成分文件
fstream f2;
f2.open("ImageData.txt", ios::out | ios::trunc);//读取前清空
if (f2.is_open() != 0)cout << "successful creat file" << endl;
elsecout << "fail creat file" << endl;for (int i = 0; i < pix_row; i++)
{for (int j = 0; j < pix_col; j++){int temp = out[i][j];f2 << temp << " ";}f2 << endl;
}
cout << "ok" << endl;
f2.close();//关闭C++写入文件
//读取数组
ifstream f;
f.open("2.txt");string str;
vector<vector<int> > array;
while (getline(f, str))
{istringstream input(str);vector<int> tmp;int a;while (input >> a)tmp.push_back(a);array.push_back(tmp);
}
//适用一行两数据,纯数据
ifstream f1;f1.open("R1.txt");vector<Point2D> vPointsR;while (getline(f1, str)){istringstream input(str);Point2D tem1;double a;int i = 1;while (input >> a){if (i == 1) {tem1.y = a;}else {tem1.x = a;}i++;}vPointsR.push_back(tem1);}f1.close();

C语言的读txt

FILE *pFile;
_wfopen_s(&pFile, strPath, _T("rb"));//读取线条
Point2D pt;
while (1)
{fwscanf_s(pFile, _T("%lf%lf"), &pt.y, &pt.x);if (feof(pFile)){break;}vPoints.push_back(pt);
}fclose(pFile);对于数组之类的,要加缓冲空间TCHAR fn[64] = {0};fwscanf_s(pFile, _T("%s"), fn , (unsigned)_countof(fn));

MFC对话框调用其他对话框的线程

  • 在a.cpp文件对话框里定义线程

    UINT  myproc_leftImage(LPVOID  lParam)
    {DIALOG_FindPoint *pWnd = (DIALOG_FindPoint *)lParam;//将窗口指针赋给无类型指针pWnd->FindPoint_Auto_left();                         //要执行的函数//AfxMessageBox(_T("左相机处理完成"));return 0;//线程完成标志
    }
    
  • 在b.cpp文件对话框里面调用a对话框的线程

    C3DReconstructionDlg* pMainWnd = (C3DReconstructionDlg*)AfxGetMainWnd();//主对话框指针
    a * pChildWnd = &(pMainWnd->m_findPoint);//FindPoint对话框的指针a
    AfxBeginThread(myproc_leftImage, (LPVOID)pChildWnd);
    

VS的编码问题

  • GBK
  • UTF-8
  • UTF-16
  • VS默认编码是GBK,三维重建源代码也是UNICODE,GBK+

经验之谈

  • 写入数据到txt,用CFile类,注意GetLength()的返回值,是DWORD。
  • 读取数据用ifstream,按行读取,减去的CString转换double的麻烦。
  • updatedata()函数的使用

MFC_Session2相关推荐

最新文章

  1. 微信小应用,又一大神,音乐播放器
  2. 网络编程学习笔记(RES_USE_INET6解析器选项)
  3. 《OpenGL ES 3.x游戏开发(下卷)》一1.2 顶点数组对象
  4. 005_HttpServlet
  5. python前缀表达式求值_python数据结构与算法 11 后缀表达式求值
  6. xshell怎么连接windows server_未能连接一个Windows服务 怎么回事
  7. boost::stl_interfaces模块实现节点迭代器的测试程序
  8. HDU - 1043 Eight(bfs打表)
  9. ListBox类似组件,鼠标右键点击事件得到选中Item
  10. 为PHP添加swoole异步并行扩展
  11. 数据结构实验之查找一:二叉排序树 (SDUT 3373)
  12. Codeforces Testing Round #1_C. Circular RMQ
  13. table td高度设置无效_Bootstrap Blazor 组件介绍 Table(一)
  14. C# 调用系统API 内核 简单样例
  15. Qt QJson遍历
  16. DOS7.1安装与学习
  17. 可涂鸦音乐光立方(DIY)
  18. Ubuntu 16.04 常用软件安装
  19. linux 下MySQL本地安装mysql - u root - p 无法登入
  20. 将手机、平板变成电脑第二屏

热门文章

  1. 爬虫学的好,牢饭吃的好 (入门篇)
  2. python3.7 统计某一只股票每天的开盘,收盘,最高,最低价格
  3. 工作之外如何实现自我提升
  4. 漏洞分析丨HEVD-11.DoubleFetch[win7x86]
  5. 使用设计模式出任CEO迎娶白富美(4)--走马上任,华丽转身
  6. 面试官让我写SQL,我没有写出来
  7. 数据中台=大数据平台+数据资产管理平台+数据服务平台
  8. matplotlib  plt.lengend
  9. fedora dnf_如何使用DNF升级Fedora Linux系统
  10. Rethinking Image Aesthetics Assessment:Models,Datasets and Benchmarks