很久不用mfc,曾几何时以为以后会很少接触它了,以前总是嫌弃它,老古董,做mfc的没前途。。。
最近又用它做东西时,有了不少感悟,mfc是属于界面类库,而且是属于比较成熟,有历史的库,相较于目前做界面比较华丽多彩的duilib、BCG、QT等从外观上略显逊色,但是都了解过之后会有一些想法界面类库实现原理大致类同,类似于同一锅里蒸出的馒头,大小形态不一,本质差别不大,精通于任意一种都会对此领域有较深刻的理解,所以现在我用起mfc没有以前那种偏见了,而且现在使用对它的理解也逐渐准确了许多,本文旨在对mfc基础的用法以及整体结构做一次总结(有一些基础用法已经忘记了),对基础东西理解的不透彻,是不会有更深的体会的,大脑在不断接触一些基础知识的时候,它会自己融合创新,当基础知识量达到一定层次,就会产生质变,引发爆炸式的增长反应。
在WinUser.h中有MSG的结构定义:
typedef struct tagMSG {
HWND hwnd; //句柄
UINT message; //消息
WPARAM wParam; //参数1
LPARAM lParam; //参数2
DWORD time; //产生消息的事件
POINT pt; //产生消息时鼠标的位置
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
关于结构体中的每个字段的含义,注释中已经简单提到。
系统定义消息ID范围:[0x0000, 0x03ff]
用户自定义的消息ID范围:
WM_USER: 0x0400-0x7FFF (例:WM_USER+10)
WM_APP(winver> 4.0):0x8000-0xBFFF (例:WM_APP+4)
RegisterWindowMessage:0xC000-0xFFFF【用来和其他应用程序通信,为了ID的唯一性,使用::RegisterWindowMessage来得到该范围的消息ID 】
即与窗口的内部运作有关的消息,如创建窗口,绘制窗口,销毁窗口等。
可以是一般的窗口,也可以是MainFrame,Dialog,控件等。
如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL等
当用户从菜单选中一个命令项目、按下一个快捷键或者点击工具栏上的一个按钮,都将发送WM_COMMAND命令消息。LOWORD(wParam)表示菜单项,工具栏按钮或控件的ID;如果是控件, HIWORD(wParam)表示控件消息类型。
#define LOWORD(l) ((WORD)(l))
#define HIWORD(l) ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))
随着控件的种类越来越多,越来越复杂(如列表控件、树控件等),仅仅将wParam,lParam将视为一个32位无符号整数,已经装不下太多信息了。
为了给父窗口发送更多的信息,微软定义了一个新的WM_NOTIFY消息来扩展WM_COMMAND消息。
WM_NOTIFY消息仍然使用MSG消息结构,只是此时wParam为控件ID,lParam为一个NMHDR指针,
不同的控件可以按照规则对NMHDR进行扩充,因此WM_NOTIFY消息传送的信息量可以相当的大。
注:Window 9x 版及以后的新控件通告消息不再通过WM_COMMAND 传送,而是通过WM_NOTIFY 传送,
但是老控件的通告消息, 比如CBN_SELCHANGE 还是通过WM_COMMAND 消息发送。
windwos也允许程序员定义自己的消息,使用SendMessage或PostMessage来发送消息。
(1) 队列消息(Queued Messages)
消息会先保存在消息队列中,消息循环会从此队列中取出消息并分发到各窗口处理
如:WM_PAINT,WM_TIMER,WM_CREATE,WM_QUIT,以及鼠标,键盘消息等。
其中,WM_PAINT,WM_TIMER只有在队列中没有其他消息的时候才会被处理,
WM_PAINT消息还会被合并以提高效率。其他所有消息以先进先出(FIFO)的方式被处理。
(2) 非队列消息(NonQueued Messages)
消息会绕过系统消息队列和线程消息队列,直接发送到窗口过程进行处理
如:WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR,WM_WINDOWPOSCHANGED
① Windows内核的系统消息队列
② App的UI线程消息队列
③ 处理消息的窗体对象
Windows内核维护着一个全局的系统消息队列;按照线程的不同,系统消息队列中的消息会分发到应用程序的UI线程的消息队列中;
应用程序的每一个UI线程都有自己的消息循环,会不停地从自己的消息队列取出消息,并发送给Windows窗体对象;
每一个窗体对象都使用窗体过程函数(WindowProc)来处理接收到的各种消息。
在程序示例中资源包括一个对话框,一个菜单,一个图标,一个版本信息。对话框中包含了一个list control控件(用于演示控件向主窗口发送消息即控件通知类消息WM_NOTIFY),一个按钮(用于测试程序自定义消息WM_USER响应),菜单用于测试命令消息WM_COMMAND,一个鼠标右键弹窗操作用来测试窗口消息WM_RBUTTONDOWN。
为了方便理解我把窗口类代码全部粘贴上来,代码如下:
MFC_list-controlDlg.h
// MFC_list-controlDlg.h : 头文件
//
#pragma once
#include "afxcmn.h"
#include "afxwin.h"
// CMFC_listcontrolDlg 对话框
class CMFC_listcontrolDlg : public CDialogEx
{
// 构造
public:
CMFC_listcontrolDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MFC_LISTCONTROL_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
CListCtrl m_listcontrol;
CEdit m_Edit;
public:
void AddCols(UINT id);
void AddNewRols();
afx_msg void OnDeleteList();
afx_msg void OnAddList();
afx_msg void OnEditList();
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnNMDblclkList(NMHDR *pNMHDR, LRESULT *pResult);
afx_msg LRESULT OnUserTest(WPARAM wParam, LPARAM lParam);
afx_msg void OnBnClickedButton1();
};
MFC_list-controlDlg.cpp
// MFC_list-controlDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "MFC_list-control.h"
#include "MFC_list-controlDlg.h"
#include "afxdialogex.h"
using namespace std;
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define WM_USER_TEST WM_USER + 100
// CMFC_listcontrolDlg 对话框
CMFC_listcontrolDlg::CMFC_listcontrolDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_MFC_LISTCONTROL_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFC_listcontrolDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST1, m_listcontrol);
}
BEGIN_MESSAGE_MAP(CMFC_listcontrolDlg, CDialogEx)
ON_WM_PAINT() //窗口消息
ON_WM_QUERYDRAGICON() //窗口消息
ON_COMMAND(ID_32771, &CMFC_listcontrolDlg::OnDeleteList) //菜单命令消息
ON_COMMAND(ID_32772, &CMFC_listcontrolDlg::OnAddList) //菜单命令消息
ON_COMMAND(ID_32773, &CMFC_listcontrolDlg::OnEditList) //菜单命令消息
ON_WM_RBUTTONDOWN() //窗口消息
ON_WM_LBUTTONDOWN() //窗口消息
ON_NOTIFY(NM_RCLICK, IDC_LIST1, &CMFC_listcontrolDlg::OnNMDblclkList) //子控件通知父窗口响应鼠标右键按下
ON_MESSAGE(WM_USER_TEST,OnUserTest) // 自定义消息
ON_BN_CLICKED(IDC_BUTTON1, &CMFC_listcontrolDlg::OnBnClickedButton1) //按钮消息(关于控件特有的消息宏定义,可以查看afxmsg_.h了解更多不同控件的宏)
END_MESSAGE_MAP()
// CMFC_listcontrolDlg 消息处理程序
BOOL CMFC_listcontrolDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
AddCols(IDC_LIST1);
AddNewRols();
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CMFC_listcontrolDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFC_listcontrolDlg::OnQueryDragIcon()
{
return static_cast(m_hIcon);
}
void CMFC_listcontrolDlg::AddCols(UINT id)
{
CRect rect;
CWnd *wnd = NULL;
wnd = GetDlgItem(id);
if (NULL == wnd)
{
MessageBox(_T("相应控件不存在"));
}
wnd->GetWindowRect(&rect);
ScreenToClient(&rect);
int width = ((int)rect.right - (int)rect.left) / 5;
m_listcontrol.InsertColumn(0, _T(" "), LVCFMT_CENTER, width, -1);// 插入列
m_listcontrol.InsertColumn(1, _T("参数名称"), LVCFMT_CENTER, width, -1);
m_listcontrol.InsertColumn(2, _T("变量类型"), LVCFMT_CENTER, width, -1);
m_listcontrol.InsertColumn(3, _T("变量值"), LVCFMT_CENTER, width, -1);
m_listcontrol.InsertColumn(4, _T("变量代号"), LVCFMT_CENTER, width, -1);
}
void CMFC_listcontrolDlg::AddNewRols()
{
vector * m_vecMessage = NULL;
m_vecMessage = new vector ;//定义一个容器,用来存放每一条记录
//查找数据库,获得数据类表
m_vecMessage->push_back(_T("项目序号"));
m_vecMessage->push_back(_T("台宽"));
m_vecMessage->push_back(_T("公式"));
m_vecMessage->push_back(_T("M+100"));
m_vecMessage->push_back(_T("tdb"));
//NormalMessageVector(A2W(sTempVaule.c_str()), m_vecMessage);
for (int i = 0; i < 10; ++i)
{
m_listcontrol.InsertItem(i, m_vecMessage->at(0));//插入行
m_listcontrol.SetItemText(i, 1, m_vecMessage->at(1));
m_listcontrol.SetItemText(i, 2, m_vecMessage->at(2));
m_listcontrol.SetItemText(i, 3, m_vecMessage->at(3));
m_listcontrol.SetItemText(i, 4, m_vecMessage->at(4));
}
}
void CMFC_listcontrolDlg::OnDeleteList()
{
// TODO: 在此添加命令处理程序代码
int count = m_listcontrol.GetSelectedCount();
if (count > 0)
{
POSITION pos = m_listcontrol.GetFirstSelectedItemPosition();
while (pos != NULL)
{
int delId = m_listcontrol.GetNextSelectedItem(pos);
m_listcontrol.DeleteItem(delId);
}
}
}
void CMFC_listcontrolDlg::OnAddList()
{
// TODO: 在此添加命令处理程序代码
}
void CMFC_listcontrolDlg::OnEditList()
{
// TODO: 在此添加命令处理程序代码
}
void CMFC_listcontrolDlg::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//添加菜单
CMenu m_menu,*pm;
m_menu.LoadMenuW(IDR_MENU1);
pm = m_menu.GetSubMenu(0); //获取子对话框
CPoint pot;
GetCursorPos(&pot);//获取鼠标当前位置
pm->TrackPopupMenu(TPM_LEFTALIGN, pot.x, pot.y, this);//在鼠标位置弹出菜单
CDialogEx::OnRButtonDown(nFlags, point);
}
void CMFC_listcontrolDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
}
void CMFC_listcontrolDlg::OnNMDblclkList(NMHDR *pNMHDR, LRESULT *pResult)
{
CMenu m_menu, *pm;
m_menu.LoadMenuW(IDR_MENU1);
pm = m_menu.GetSubMenu(0); //获取子对话框
CPoint pot;
GetCursorPos(&pot);//获取鼠标当前位置
pm->TrackPopupMenu(TPM_LEFTALIGN, pot.x, pot.y, this);//在鼠标位置弹出菜单
}
LRESULT CMFC_listcontrolDlg::OnUserTest(WPARAM wParam, LPARAM lParam)
{
AfxMessageBox(_T("自定义消息"));
return 0;
}
void CMFC_listcontrolDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
SendMessage(WM_USER_TEST, 0, 0);
}
头文件:afxmsg_.h
太长了不贴出来,可以自己查看本机文件,类似于:
#define ON_WM_PAINT() \
{ WM_PAINT, 0, 0, 0, AfxSig_vv, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnPaint)) },
#define ON_WM_SYNCPAINT() \
{ WM_SYNCPAINT, 0, 0, 0, AfxSig_vv, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnSyncPaint)) },
#define ON_WM_CLOSE() \
{ WM_CLOSE, 0, 0, 0, AfxSig_vv, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnClose)) },
Message map tables for Control Notification messages
包含各种按钮,列表,richedit等的宏定义
// Message map tables for Control Notification messages
// Static control notification codes
#define ON_STN_CLICKED(id, memberFxn) \
ON_CONTROL(STN_CLICKED, id, memberFxn)
#define ON_STN_DBLCLK(id, memberFxn) \
ON_CONTROL(STN_DBLCLK, id, memberFxn)
#define ON_STN_ENABLE(id, memberFxn) \
ON_CONTROL(STN_ENABLE, id, memberFxn)
#define ON_STN_DISABLE(id, memberFxn) \
ON_CONTROL(STN_DISABLE, id, memberFxn)
// Edit Control Notification Codes
#define ON_EN_SETFOCUS(id, memberFxn) \
ON_CONTROL(EN_SETFOCUS, id, memberFxn)
#define ON_EN_KILLFOCUS(id, memberFxn) \
ON_CONTROL(EN_KILLFOCUS, id, memberFxn)
#define ON_EN_CHANGE(id, memberFxn) \
ON_CONTROL(EN_CHANGE, id, memberFxn)
#define ON_EN_UPDATE(id, memberFxn) \
ON_CONTROL(EN_UPDATE, id, memberFxn)
#define ON_EN_ERRSPACE(id, memberFxn) \
ON_CONTROL(EN_ERRSPACE, id, memberFxn)
#define ON_EN_MAXTEXT(id, memberFxn) \
ON_CONTROL(EN_MAXTEXT, id, memberFxn)
#define ON_EN_HSCROLL(id, memberFxn) \
ON_CONTROL(EN_HSCROLL, id, memberFxn)
#define ON_EN_VSCROLL(id, memberFxn) \
ON_CONTROL(EN_VSCROLL, id, memberFxn)
#define ON_EN_ALIGN_LTR_EC(id, memberFxn) \
ON_CONTROL(EN_ALIGN_LTR_EC, id, memberFxn)
#define ON_EN_ALIGN_RTL_EC(id, memberFxn) \
ON_CONTROL(EN_ALIGN_RTL_EC, id, memberFxn)
// Richedit Control Notification Codes
#define ON_EN_IMECHANGE(id, memberFxn) \
ON_CONTROL(EN_IMECHANGE, id, memberFxn)
#define ON_EN_ALIGNLTR(id, memberFxn) \
ON_CONTROL(EN_ALIGNLTR, id, memberFxn)
#define ON_EN_ALIGNRTL(id, memberFxn) \
ON_CONTROL(EN_ALIGNRTL, id, memberFxn)
// Animation Control Notification Codes
#define ON_ACN_START(id, memberFxn) \
ON_CONTROL(ACN_START, id, memberFxn)
#define ON_ACN_STOP(id, memberFxn) \
ON_CONTROL(ACN_STOP, id, memberFxn)
// 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)
// old BS_USERBUTTON button notifications - obsolete in Win31
#define ON_BN_PAINT(id, memberFxn) \
ON_CONTROL(BN_PAINT, id, memberFxn)
#define ON_BN_HILITE(id, memberFxn) \
ON_CONTROL(BN_HILITE, id, memberFxn)
#define ON_BN_UNHILITE(id, memberFxn) \
ON_CONTROL(BN_UNHILITE, id, memberFxn)
#define ON_BN_DISABLE(id, memberFxn) \
ON_CONTROL(BN_DISABLE, id, memberFxn)
// Listbox Notification Codes
#define ON_LBN_ERRSPACE(id, memberFxn) \
ON_CONTROL(LBN_ERRSPACE, id, memberFxn)
#define ON_LBN_SELCHANGE(id, memberFxn) \
ON_CONTROL(LBN_SELCHANGE, id, memberFxn)
#define ON_LBN_DBLCLK(id, memberFxn) \
ON_CONTROL(LBN_DBLCLK, id, memberFxn)
#define ON_LBN_SELCANCEL(id, memberFxn) \
ON_CONTROL(LBN_SELCANCEL, id, memberFxn)
#define ON_LBN_SETFOCUS(id, memberFxn) \
ON_CONTROL(LBN_SETFOCUS, id, memberFxn)
#define ON_LBN_KILLFOCUS(id, memberFxn) \
ON_CONTROL(LBN_KILLFOCUS, id, memberFxn)
// Check Listbox Notification codes
#define CLBN_CHKCHANGE (40)
#define ON_CLBN_CHKCHANGE(id, memberFxn) \
ON_CONTROL(CLBN_CHKCHANGE, id, memberFxn)
// Combo Box Notification Codes
#define ON_CBN_ERRSPACE(id, memberFxn) \
ON_CONTROL(CBN_ERRSPACE, id, memberFxn)
#define ON_CBN_SELCHANGE(id, memberFxn) \
ON_CONTROL(CBN_SELCHANGE, id, memberFxn)
#define ON_CBN_DBLCLK(id, memberFxn) \
ON_CONTROL(CBN_DBLCLK, id, memberFxn)
#define ON_CBN_SETFOCUS(id, memberFxn) \
ON_CONTROL(CBN_SETFOCUS, id, memberFxn)
#define ON_CBN_KILLFOCUS(id, memberFxn) \
ON_CONTROL(CBN_KILLFOCUS, id, memberFxn)
#define ON_CBN_EDITCHANGE(id, memberFxn) \
ON_CONTROL(CBN_EDITCHANGE, id, memberFxn)
#define ON_CBN_EDITUPDATE(id, memberFxn) \
ON_CONTROL(CBN_EDITUPDATE, id, memberFxn)
#define ON_CBN_DROPDOWN(id, memberFxn) \
ON_CONTROL(CBN_DROPDOWN, id, memberFxn)
#define ON_CBN_CLOSEUP(id, memberFxn) \
ON_CONTROL(CBN_CLOSEUP, id, memberFxn)
#define ON_CBN_SELENDOK(id, memberFxn) \
ON_CONTROL(CBN_SELENDOK, id, memberFxn)
#define ON_CBN_SELENDCANCEL(id, memberFxn) \
ON_CONTROL(CBN_SELENDCANCEL, id, memberFxn)
比如自定义消息的消息映射宏等
// User extensions for message map entries
// for Windows messages
#define ON_MESSAGE(message, memberFxn) \
{ message, 0, 0, 0, AfxSig_lwl, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \
(memberFxn)) },
// for Registered Windows messages
#define ON_REGISTERED_MESSAGE(nMessageVariable, memberFxn) \
{ 0xC000, 0, 0, 0, (UINT_PTR)(UINT*)(&nMessageVariable), \
/*implied 'AfxSig_lwl'*/ \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \
(memberFxn)) },
// for Thread messages
#define ON_THREAD_MESSAGE(message, memberFxn) \
{ message, 0, 0, 0, AfxSig_vwl, \
(AFX_PMSG)(AFX_PMSGT) \
(static_cast< void (AFX_MSG_CALL CWinThread::*)(WPARAM, LPARAM) > \
(memberFxn)) },
// for Registered Windows messages
#define ON_REGISTERED_THREAD_MESSAGE(nMessageVariable, memberFxn) \
{ 0xC000, 0, 0, 0, (UINT_PTR)(UINT*)(&nMessageVariable), \
/*implied 'AfxSig_vwl'*/ \
(AFX_PMSG)(AFX_PMSGT) \
(static_cast< void (AFX_MSG_CALL CWinThread::*)(WPARAM, LPARAM) > \
(memberFxn)) },
MFC消息分类与消息队列
MFC的自定义消息的定义与使用
MFC 消息类型
MFC List Control 控件