《VC++ 深入详解》--孙鑫
《MFC深入浅出》 --侯捷
《C++标准程序库》--侯捷
《visual c++典型模块与项目实战大全》 ---电子工业出版社
《visual c++ 串口通信程序》
一、接触MFC day01
1. 使用资源:
①insert resource,对话框资源
②resource view下面,右键对话框,修改properties,重要的是资源ID,一般为IDD_Dialogxx
③对资源的操作一般都是通过类来完成,因此要操作资源需要为资源添加类,基类一般都是CDialog
注意:添加类后,在class view中没有该类的浏览信息
方法一:可以删除 .ncb文件(no compile browse 无编译浏览文件),重新编译一遍
方法二:在文件视图,右键工程,添加 cpp文件和h文件(----win7下右键工程添加文件没有反应---)
方法三:直接修改dsp文件
|-------用记事本打开dsp文件----①SOURCE=.\xxx.cpp
|---②SOURCE=.\xxx.h
2. 对话框的创建;
2.1 头文件包含include “dialog.h”
2.2 先创建后显示
2.2.1 创建模态对话框:Ctest dlg; dlg.DoModal();
2.2.2 创建非模态对话框:
①static CTestDlg dlg; 或者设置成员变量dlg;或者使用堆对象dlg
②dlg.Create();
③dlg.ShowWindow(); 如果在Create函数中设置了WS_VISIBLE参数,则不需要调用ShowWindow函数
④如果是堆中的对象,还需要重写OnOK函数,在其中销毁dlg:dlg->Destroy()---------------------重要
3. Button的创建(通上面Dialog的创建)
CButton btn;
if(b_isCreated)
{
btn.Create();
btn.ShowWindow(SW_SHOW);
Sleep(2000); 睡眠2s
}
else
{
btn.Destroy(); // 现在就销毁资源,如果是堆空间,这句话非常重要
}
4. 让static text 静态文本框 接收消息:
①由于所有的static都是相同的ID_STATIC,因此无法添加消息,修改ID后可以通过消息映射添加消息处理函
②style中设置允许通告消息notify
5. 获得对话框中的对象: CWnd* GetDlgItem(ID_XXX)
SetWindowText("xxx");
GetWindowText(CString&);
6. 控件访问的7种方式: 原始方式 控件绑定 消息方式
6.1 GetDlgItem()->Get(Set)WindowText();
结合:atoi和itoa函数
/--------------------------------------------------/
char n1[16],n2[16],n3[16];
int i1,i2,i3;
GetDlgItem(IDC_EDIT1)->GetWindowText(n1,sizeof(n1));
..
i1=atoi(n1);
i2=atoi(n2);
i3=i1+i2;
GetDlgItem(IDC_EDIT3)->SetWindowText(itoa(i3,n3,10));
/---------------------------------------------------/
6.2 GetDlgItemText()/SetDlgItemText()
6.3 GetDlgItemInt()/SetDlgItemInt();
/---------------------------------------------------/
int i1,i2,i3;
i1=GetDlgItemInt(IDC_EDIT1);
i2=GetDlgItemInt(IDC_DEIT2);
i3=i1+i2;
SetDlgItemInt(IDC_EDIT3);
/---------------------------------------------------/
6.4 将控件和整型相关联
①ClassWizard -> Member Variables
②DDX_Text
③DoDataExchange
UpdateData(true); // 获取值
m_num3=m_num1 + m_num2;
UpdateData(false); // 更新值
注意:使用DDX,DDV时候,需要使用UpdateData函数,一般安装先真后假的顺序执行
6.5 将控件和控件变量关联
一个控件可以和多个,多种类型的变量绑定
m_Edit.GetWindwoText(buf,sizeof(buf));
m_Edit.SetWindowText(buf);
6.6 SendMessage()
WM_GETTEXT 获取消息
WM_SETTEXT 设置消息
方法1:使用::SendMessage(hwnd,uMsg,wParam,lParam);
char buf[64]={'\0'};
::SendMessage(m_Edit.m_hWnd,WM_GETTEXT,sizeof(buf),(LPARAM)buf);
::SendMessage(GetDlgItem(IDC_EDIT)->m_hWnd,WM_GETTEXT,sizeof(buf),(LPARAM)buf);
int i=atoi(buf);
itoa(xx,xx,xx);
::SendMessage(m_Edit.m_hWnd,WM_SETTEXT,0,(LPARAM)buf);
方法2:使用成员 .SendMessage(uMsg,wParam,lParam);
m_Edit.SendMessage(WM_GETTEXT,sizof(buf),(LPARAM)buf);
注意:这里说明,在不同的环境下,wParam和lParam代表的参数不同含义
6.7 SendDlgItemMessage()
SendDlgItemMessage(IDC_EDIT,WM_GETTEXT,sizeof(buf),(LPARAM)buf);
7. 属性表单、向导程序wizard
属性表单和属性页
7.1 创建属性页----------------------------------------------------------需要界面资源
①插入属性页资源,设计"属性页"资源,
insert resource -> 对话框 IDD_PROPAGE_XXX
insert resource -> 对话框 IDD_PROPSHEET_XXX
②为页面添加类(继承CPropertyPage) 需要属性页资源
注意:在resource view中,复制一个资源,直接粘贴,可以快速产生多个资源
7.2 创建属性表单类(包含3个属性页类的成员) (继承CPropertySheet)-----不需要界面资源
①insert -> new class : CPropSheet Base class:CPropertySheet
②物理上添加属性表单:右键属性表单类名 -->> 添加成员变量 m_page1,m_page2,...
③逻辑上添加属性表单:构造函数中:AddPage(m_page1);AddPage(m_page2);...
④主对话框添加一个按钮,添加消息响应:sheet.DoModal();
void CHelloDialog::OnBtn(){
CPropSheet sheet(第一个表单程序);
sheet.SetWizardMode();
sheet.DoModal();
}
注意:中文乱码,将资源属性中,将字体改成新宋体
向导程序
在DoModal之前调用,sheet.SetWizarMode()
总结:
-------------------------------------------
类型 基类 是否需要资源
-------------------------------------------
属性页 CPropertyPage 需要
属性表单 CPropertySheet 不需要
-------------------------------------------
7.3 完善Wizard:
关键点:上一步,下一步,确定,取消 等按钮属于Sheet
CPropertySheet::SetWizardButtons(xxx)
①属性表单处于Active状态时候: 要取消上一步,下一步按钮,
必须复写虚函数:virtual CPropertyPage::OnSetActive()
BOOL CProp1::OnSetActive(){
// 这里需要转型,因为GetParent返回一个CWnd*指针,
// 而SetWizardButtons不是虚函数,因此必须转型为CPropertySheet类型
((CPropertySheet*)GetParent())->SetWizardButtons(PS_);
}
②virtual CPropertyPage::OnWizardNext() -----------单击下一步,CPropertyPage派生类调用该函数
LRESULT CProp1::OnWizardNext(){
if(条件不满足){xxx,return -1}
CPropertyPage::OnWizardnext();
}
二、MFC下基于socket的网络通信程序 day02 看mfc的文档,注意一些框架相关的东西,这些东西以afx开头
/----------------------------------------------/
>stdafx.h
#include "afxsock.h" // afxsock.h 每个类都要使用,因此包含在应用程序公共头文件中
/----------------------------------------------/
2.1 socket版本协商
在CWinApp派生类的InitInstance中,使用AfxSocketInit()进行socket版本协商
(使用AfxSocketInit()会调用SWAStartup和WSACleanup,协商socket版本,加载套接字动态链接库)
/------------------------------------------------------------------------------------------/
服务器:协商windowssocket版本,调用合适的动态链接库(windows的socket版本比较多,需要协商)
WSAStartup() (window socket application) win32 api
SWACleanup()
/-------------------------------------------------------------------------------------------/
使用MFC,可以直接使用封装好的协商机制: 在应用程序类的InitInstance函数中进行版本协商(AfxSocketInit())
BOOL CChatApp::InitInstance(){
if(!AfxSocketInit()){
AfxMessageBox("加载套接字失败");
return FALSE;
}
}
2.2 初始化套接字: 定义一个初始化服务器套接字的函数InitSocket,并在对话框InitDialog中调用InitSocket
①在CChatDlg中加载成员函数:BOOL InitSocket()
右键类名-->> add member function
SOCKET m_socket;
BOOL CChatDlg::InitSocket(){ // m_socket为SOCKET类型
m_socket = socket(AF_INET,SOCK_DGRAM,0); // SOCK_STREAM SOCK_DGRAM
if(-1==m_socket){
AfxMessageBox("套接字出错");
return false;
}
struct socketaddr_in ser_addr;
ser_addr.sin_family = AF_INET;
ser_addr.sin_addr.s_addr = INADDR_ANY;
ser_addr.sin_port = htons(5000);
// 字节序问题byte order
// intel x86系列是小端模式;internet上是大端模式
// 0x12345678 (高->低)
//地址 低->高 12 34 56 78 小端模式
//地址 低->高 78 56 34 12 大端模式
int ret = bind(m_socket,(struct sockaddr*)&ser_addr,sizeof(sockaddr));
if(ret == -1){
AfxMessageBox("套接字绑定出错);
}
return true;
}
注意:在OnInitDialog结尾处调用InitSocket();
② 服务器接收数据是阻塞过程,单独创建一个线程,用于接收数据
typedef struct {
SOCKET sock;
HWND hWnd;
}
CChatDlg::OnInitDialog()
{
InitSocket();
//---------------------[
// RECVPARAM rp ;
// rp.sock = m_sock;
// rp.hWnd = m_hWnd;
RECVPARAM *rp = new RECVPARAM;
rp->sock = m_socket;
rp->hWnd = m_hWnd;//-----------------
CreateThread(0,0,RecvProc,rp,0,NULL); // 创建线程不是阻塞的,结束后,线程什么时候被调度不能确定
}
ThreadProc是回调函数,因此必须使用WINAPI,以遵循帕斯卡调用约定
static DWORD WINAPI RecvProc(LPVOID p)
{
RECVPARAM *rp = (RECVPARAM*)p;
while(true){
char recvBuf[128]={'\0'}
struct sockaddr_in from_addr;
int from_len = sizeof(struct sockaddr);
// 第3个参数为0,表示立即接收和发送
int ret = recvfrom(rp->sock,recvBuf,sizeof(recvBuf),0,(struct sockaddr_in*)&from_addr,&from_len);
if(-1 == ret){
AfxMessageBox("接收数据有误");
break;
}
CString str=recvBuf;
AfxMessageBox(str);
}
}
注意:①上述代码导致界面卡住,应该单独开一个线程:
②上述代码,会执行“接收数据有误”,而recvfrom是一个阻塞型函数,这只能说明传过来的m_socket是错误的
原因:创建线程时候,传递的参数在OnIniitDialog结束时候,参数已经销毁了,参数结构体里面是一片蛮荒之地
③使用 char buf[256],--------sprintf()
使用CString --------------str.Format();
③自定义消息:static 线程回调函数中不能使用GetDlgItem成员函数,因此无法将消息显示在IDC_EDIT2中
方法一:发送消息SendMessage,发送自定义消息,自定义消息的四个步骤
第一步:定义消息,在对话框头文件开头地方 #define WM_RECVDATA WM_USER+1
第二部:消息处理函数的定义和声明:
①消息处理函数宏申明 afx_msg OnRecvData();
// Generated message map functions
//{{AFX_MSG(CChatDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam); // 框架添加的
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnBtnsend();
//}}AFX_MSG
afx_msg void OnRecvData(WPARAM wParam,LPARAM lParam); // 用户添加的
②编写OnRecvData函数
第三部:添加消息映射,源代码中,构造函数中,ON_MESSAGE(WM_RECVDATA,OnRecvData)
BEGIN_MESSAGE_MAP(CChatDlg, CDialog)
//{{AFX_MSG_MAP(CChatDlg)
ON_WM_SYSCOMMAND() // 框架添加的
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTNSEND, OnBtnsend)
//}}AFX_MSG_MAP
ON_MESSAGE(WM_RECVDATA,OnRecvData) // 用户添加的
END_MESSAGE_MAP()
方法二:使用::SetDlgItemText(hWnd,IDC_EDIT2,"XXX") // 需要对话框的句柄
void CChatDlg::OnRecvData(WPARAM wParam,LPARAM lParam){
SetDlgItemText(IDC_EDIT1,(char*)wParam);
}
④客户端处理
IPAddressCtrl控件: CIPAddressCtrl::GetAddress(); 注意这个函数父类没有,必须转型
DWORD dip;
(CIPAddress*)GetDlgItem(IDC_IPADDRESS)->GetAddress(dip);
ser_addr.sin_addr.s_addr = htonl(dip);
void CChatDlg::OnBtnsend()
{
// TODO: Add your control notification handler code here
SOCKET sock_send = socket(AF_INET,SOCK_DGRAM,0); // AF_INET--internet SOCK_DGRAM--UDP protocol--0,返回socket描述符
struct sockaddr_in ser_addr;
ser_addr.sin_family = AF_INET;
ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 字符串ip地址 转换为 网络字节序
ser_addr.sin_port = htons(5000);
int addr_len = sizeof(struct sockaddr);
CString str;
GetDlgItemText(IDC_EDIT2,str);
sendto(sock_send,str,str.GetLength()+1,0,(struct sockaddr*)&ser_addr,addr_len);
SetDlgItemText(IDC_EDIT2,"");
}
三、win32 应用程序,了解mfc原理
①Windows的程序入口
#include "windows.h"
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine, //project -> settings -> debug -> Program arguments :添加命令行参数
int nCmdShow
){
创建一个完整的窗口的步骤:
第一步:设计一个窗口类 WNCCLASS wndclass;
第二部:注册窗口类 RegisterClass(&wndclass);
第三部:创建窗口 HWND hWnd = CreateWindow("xx",...);
第四步:显示、更新窗口 ShowWindow(hWnd,SW_SHOWNORMAL);
Update
第五步:消息循环 message cycle
MSG msg;
while(GetMessage(&msg,hWnd,0,0){
TranslateMessage(&msg);
Dispatchmessage(&msg); // 投递给WinProc回调函数
}
return 0;
}
//------------------------
//project -> settings -> debug -> Program arguments :添加命令行参数
第六步:编写窗口过程函数处理消息
switch(uMsg){
case WM_PAINT:
{
PAINTSTRUCT ps; // 用于保存设备信息
HDC hdc = BeginPaint(hwnd,&ps); // 获得dc
char buf[64]="xxx";
TextOut(hwnd,,100,50,buf,sizeof(buf)); // 使用dc绘图
EndPaint(hwnd,&ps);
}
break;
case WM_LBUTTONDOWN:
HDC hdc = GetDC(hwnd); // 获得设备句柄
char buf[64]="xxx";
TextOut(hdc,200,50,buf,sizeof(buf));
Release(hdc);
break;
case WM_CHAR:
// WM_KEYDOWN 和 相关信息,由TranslateMessage组装成WM_CHAR消息
// 键值存放在 wParam中
char buf[64]={'\0'};
sprintf(buf,"char is %c",wParam);
MessageBox(hwnd,buf,"msg",MB_OK);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
case WM_DESTROY:
// SendMessage(hwnd,WM_QUIT); // ERROR 不能使用这个函数,不产生队列消息,因此消息循环不结束
// PostMessage(hwnd,WM_QUIT);
// PostQuitMessage(0);
default:
return DefWindowProc(hWnd,);
}
注意:
①CALLBACK = WINAPI pascal调用约定
②获得与绘图相关的系统对象(GDI对象):
GetStockObject() // 返回HGDIOBJ 句柄
LoadCursor(NULL,IDC_CROSS) // 第一个参数为NULL,说明使用系统资源,如果为hInstance,只能使用当前应用的资源
LoadIcon(NULL,IDI_ERROR)
③应用程序通过GetMessage获得消息, 一个窗口通过窗口过程WndProc获得消息
应用程序通过DispatchMessage派送消息
④close--WM_CLOSE:DestroyWindow()--->> WM_DESTROY:PostMessage(hWnd,WM_QUIT,0,0)-->> WM_QUIT:
Post和Send的区别:①Post后,产生队列消息,不管消息是否处理,都直接返回,因此应用程序直接销毁了;
②Send直接调用WinProc,并不是产生队列消息,因此消息循环一直不会结束,应用程序也不会结束
⑤BeginPaint() EndPaint()专用于case WM_PAINT:
GetDC() ReleaseDC() 用在除WM_PAINT之外所有的位置
⑥WM_CHAR消息,则wParam中为字符码
⑦TranslateMessage: combine WM_KEYDOWN,WM_KEYUP,produce WM_CHAR
-------
考题:
-------
①Windows中GetMessage()与PeekMessage()的区别
GetMessage从消息队列获取消息,直到有一个“合适”的投递消息可用时才能够返回(阻塞型)
如果是WM_QUIT消息,则返回0,否则非0,出错是-1
PeekMessage不会等待有合适的投递消息到达(非阻塞型,瞟一眼)
附加参数remove option :可以决定是否决定移除取到的消息
②Windows编程中PostMessage()与SendMessage的区别
SendMessage发出的不是队列消息,因此不经过消息循环;
PostMessage发出的消息进入线程队列,因此消息循环可以得到
③WM_CLOSE,WM_DESTROY,WM_QUIT三个消息的区别
WM_CLOSE: 是关闭按钮被按下的时候,应该调用DestroyWindow销毁窗口,DestroyWindow发出WM_DESTROY消息
WM_DESTROY: 应该发出WM_QUIT消息
④TranslateMessage()有什么作用
⑤Windows应用程序中消息如何路由
⑥while(GetMessage(lpMsg,hWnd,0,0))这样的代码为什么要尽量避免
//--------------------------------------------------------------------
因为GetMessage可能的返回值为: WM_QUIT 0; 其他消息 nonzero; error--> -1
//--------------------------------------------------------------------
while(1){
int ret = GetMessage(&msg,NULL,0,0); // null表示接受所有窗口消息
if(ret == 0){
break;
}
else if(ret == -1){
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}//-----------------------------------------------------------------
四、MFC----Menu 菜单编程 单文档编程
4.1 利用appwizard生成一个单文档,View Frame(不包含菜单),Doc
标准消息:除WM_COMMAND之外的,以WM_开头的消息
从CWnd继承的类可以接收这种消息
命令消息:WM_COMMAND 菜单栏,工具栏,加速键产生
MFC中以菜单ID标志;SDK中以wParam标示
通告消息:控件产生的消息,如按钮,列表框产生的消息,为的是向父窗口通知事件发生。
通常以WM_COMMAND呈现,从CCmdTarget派生的类可以接收
--------------------------------------------------
消息类型 消息ID 消息映射宏
--------------------------------------------------
标准消息 WM_XX ON_WM_XX()
命令消息 WM_COMMAND ON_COMMAND(id,memberFxn)
通告消息 WM_COMMAND ON_COMMAND(id,memberFxn)
XN_XXXX ON_XN_XXX(id,memberFxn)
例:
BN_XXXX ON_BN_XXX(id,memberFxn) // BN_CLICKED= button_notify_clicked
自定消息 自定义IDXX ON_MESSAGE(IDXX,memberFxn)
-----------------------------------------------------------
4.2 消息路由
①菜单命令消息的路由:
消息映射宏: WM_COMMAND(ID_MENU,OnMENU)
路由顺序:view---doc---frame---app
注意:一定不会交由CAboutDlg处理
②通告消息路由:
控件消息(对话框上的消息),通告消息:向父窗口通知事件发生,让父窗口处理
消息映射宏:WM_COMMAND
4.3 菜单项:
标记菜单项CheckMenuItem
缺省菜单项SetDefaultItem
图形标记菜单项SetMenuItemBitmaps
GetSystemmetrics(SM_CXMENUCHECK)
new bitmap
import bitmap:自己制作bitmap,菜单上是13*13
菜单项生效,失效EnableMenuItem
note:m_bAutoMenuEnable = false;命令更新机制,注意变化
菜单设置,取消SetMenu
Detach()
LoadMenu()
①在CMainFrame的OnCrete中初始化菜单:
①GetMenu: CWnd成员函数,获得菜单栏
②GetSubMenu:获得子菜单
③CheckMenuItem: MF_BYCOMMAND | MF_CHECKED MF_BYPOSITION | MF_CHECKED
④SetMenuItemBitmap():
①特别注意bitmap的大小
int w = GetSystemMetrics(SM_CXMENUCHECK); // CX
int h = GetSystemMetrics(SM_CYMENUCHECK); // CY
QString str;
str.Format("w=%d,h=%d",w,h);
MessageBox(str);
②注意Bitmap对象生存期的问题:
使用static或者成员变量
使用Detach()函数
⑥EnableMenuItem():
// 因为afx的菜单更新机制,下面代码不起作用,需要将CMainFrame构造函数中的m_bAutoMenuEnable =false
GetMenu()->GetSubMenu(3)->EnableMenuItem(0,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
CMainFrame::CMainFrame()
{
// TODO: add member initialization code here
m_bAutoMenuEnable = false;
}
⑦SetMenu()
// OnCreate中的范例代码----------------------------------------------------------------------
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_OPEN,MF_BYCOMMAND | MF_CHECKED);
GetMenu()->GetSubMenu(0)->SetDefaultItem(1,true);
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
GetMenu()->GetSubMenu(2)->SetMenuItemBitmaps(ID_VIEW_STATUS_BAR,MF_BYCOMMAND,&bitmap,&bitmap);
bitmap.Detach();
//-------------------------------------------------------------------------------------------
②菜单的加载和移除
menu.LoadMenu(IDR_XX);
menu.Detach();
③添加组件和控件: project - add to project -- components and controls
五、mfc源码分析: appmodule.cpp--WinMain appcore.cpp--CWinApp
案例:
在view的200,200位置显示:hello visual c++!
考题:
①什么是appwizard
②WinMain在MFC程序中是如何被隐藏的:
//----------------------------
mfc中所有类以C开头
src search WinMain
CXXApp构造函数设置断点
theApp设置断点
积累CWinApp
SRC search CWinApp
APPCORE.cpp(窗口菜单找)
Go Definition of CWinApp构造函数
父类构造函数中的this指向谁
//----------------------------------------
安装目录---vc98--mfc---src---appmodule.cpp
theApp 构造函数--------CWinApp构造函数
_tWinMain和WinMain一模一样,是一个宏
_tWinMain---AfxWinMain----InitInstance----创建文档模板
| |-------------
| |-------------显示,更新窗口
|
|---------pThread->Run()
③theApp是如何被分配的
④MFC框架中几个类的作用于相互关系
⑤MFC框架窗口是如何产生和销毁的
⑥窗口类的PreCreateWindow和OnCreate两个函数的关系
⑦Windows窗口与C++中的CWnd类的关系
六、绘图:学会使用GetStockObject
6.1 绘图DC的种类,CBrush,CPen
①HDC hdc = ::GetDC(m_hWnd);
②CDC* cdc = GetDC();
③CClient dc(this);
④CWindowDC dc(this);
dc.SelectObject();
⑤CBrush(CBitmap)
注意:①HGIDOBJ GetStockObject() sdk全局函数
②CBrush::FromHandle() 转换一个handle到指针
6.2 绘图DC :
关键函数:
|--------GetStockObject()
|--------FromHandle()
|--------SelectObject()
//-----------------------------------------
案例:根据菜单选项,绘制点、线、矩形、椭圆
//-----------------------------------------
①添加菜单:IDM_POINT IDM_LINE IDM_RECTANGLE IDM_ELLIPSE
②添加消息相应函数:OnPoint(); OnLine();OnRectangle();OnEllipse()
③添加成员:UINT m_nDrawType-------构造函数中初始化
|-------------消息响应函数中设置绘图类型
④WM_LBUTTONDOWN 记录原点;WM_LBUTTONUP,画图
注意:
① COLORREF: 是一个宏,定义为一个整型,可以通过 RGB(X,Y,Z) 生成COLORREF
② GetStockObject() 返回一个HGDIOBJ句柄
FromeHandle(): 类中一般不直接使用句柄,而是使用指针,因此有: 句柄-----指针
(HBRUSH)HGDIOBJ GetStockObject(NULL_BRUSH); 获得一个透明画刷的句柄
CBrush::FromHandle(hbrush); 从句柄返回一个指针
//-------------------------------------------------
CBrush* pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
dc.SelectObject(pBrush);
//-------------------------------------------------
6.3 CBrush:
CBrush* pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
dc.SelectObject(pBrush);
6.4 CPen 线型,线宽,颜色
CPen pen(PS_SOLID,5,RGB(255,0,0)); // RGB宏构造COLORREF参数
dc.SelectPen(&pen); //
重要的宏的总结:
------------------------------------------------------------------
名称 使用场合
------------------------------------------------------------------
MAKEINTRESOURCE(IDI_XX) ::LoadIcon的时候需要用到
RGB(X,Y,Z) 构造GDI对象的时候
------------------------------------------------------------------
6.4 颜色对话框
void CGraphicView::OnColor()
{
// TODO: Add your command handler code here
CColorDialog dlg(m_color);
// 设置初始化颜色,或者使用一下语句:
// 不能直接dlg.m_cc.Flags = CC_RGBINIT | CC_FULLOPEN; 程序会崩溃
// CC_FULLOPEN可以使得ColorDialog右边的彩色板完全张开
dlg.m_cc.Flags |= CC_RGBINIT | CC_FULLOPEN;
dlg.m_cc.rgbResult = RGB(0,255,0);
if(IDOK == dlg.DoModal()){
m_color = dlg.m_cc.rgbResult;
}
}
6.5 字体对话框----①使用CFontDialog建立一个对象dlg
|----②显示对话框dlg.DoModal()
|----③如果返回IDOK,查看m_font的m_hOjb看是否有相关资源
|-----①没有资源:----------------------m_font.CreateFotnIndeirect创建资源
|-----②有资源:m_font.DeleteObject()---m_font.CreateFotnIndeirect创建资源
void CGraphicView::OnFont()
{
// TODO: Add your command handler code here
CFontDialog dlg;
/* if(IDOK == dlg.DoModal()){
// 崩溃,因为create了很多次,所有Create的函数基本都只能Create一次
m_font.CreateFontIndirect(dlg.m_cf.lpLogFont);
m_strFontName = dlg.m_cf.lpLogFont->lfFaceName;
m_font.DeleteObject();
}
*/
if(IDOK == dlg.DoModal()){
// 如果字体和资源对象相关联,解除类的对象和资源对象的关联
// windows gdi object 资源
// CGDIObject 类的对象
if(m_font.m_hObject) m_font.DeleteObject();
m_font.CreateFontIndirect(dlg.m_cf.lpLogFont);
m_strFontName = dlg.m_cf.lpLogFont->lfFaceName;
}
Invalidate();
}
6.6 设置对话框
----------------
IDD_SETTING资源:
--------------------------------------------\
| 线宽 |--Editbox--| 确定 | |
| 取消 | |
| 线型--------------- 预览---------- |
| | 。solid | | | |
| | 。dash | | | |
| | 。dash_dot | | | |
| ------------------ ------------ |
`-------------------------------------------
void CGraphicView::OnSetting()
{
// TODO: Add your command handler code here
CSettingDlg dlg;
dlg.m_nLineWidth = m_nLineWidth; // 自对话框控件绑定的变量始终是publc,方便parent访问
dlg.m_nLineStyle = m_nLineStyle;
dlg.m_clr = m_color;
if(IDOK == dlg.DoModal()){
m_nLineWidth = dlg.m_nLineWidth;
m_nLineStyle = dlg.m_nLineStyle;
}
}
6.7 设置预览
①当设置对话框中发生-------①Edit内容改变,发送EN_CHANGED消息,在OnEnChanged()中添加Invalidate();
|-----②Radio被点击,发送BN_CLICKED消息, 在OnRadiox()中添加Invalidate();
只要一发生上述动作之一,引发对话框窗口重绘,重写OnPaint()
②重写OnPaint()----------①保存获得参数
|---------②用保存的最新参数设置DC
|---------③绘制预览效果:-----①获得组合框矩形区域(相对屏幕)
|----②矩形区域到客户区坐标转换
|----③绘制预览效果
void CSettingDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
UpdateData(true); // 更新界面数据到变量
CPen pen(m_nLineStyle,m_nLineWidth,m_clr);// 根据最新数据设置DC画笔
dc.SelectObject(pen);
// TODO: Add your message handler code here
// 下面是实现预览关键部分,每次界面的选择,引发重绘,
// 重绘就需要预览出当前选择参数的实际效果
CRect rect; // 获得预览组框的矩形矩形区域,相对屏幕坐标
GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);
ScreenToClient(&rect); // 屏幕坐标转换成Client区坐标
// 预览效果绘制:开始按照现在选择的参数,进行预览效果绘制
dc.MoveTo(rect.left+10,rect.top+rect.Height()/2);
dc.LineTo(rect.right-10,rect.top+rect.Height()/2);
// Do not call CDialog::OnPaint() for painting messages
}
OnDraw只有view有,因此CDialog中不能编辑OnDraw()
CDialog中可以重写OnPaint(): 只能使用BeginPaint()和EndPaint(),对应的MFC类是CPaint
6.8 图形的保存和重绘
方法一: 新建一个类,用该类成员保存绘制的对象-----包括对象的点
|------对象的绘图dc参数)
//---------------------------------------------------------------------
①新建一个类,inert---class----generic(有三种:Generic,mfc,form);
②添加成员:
UINT m_nDrawType;
CPoint m_ptOrigin;
CPoint m_ptEnd;
③修改构造函数:
Graph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd)
④在GraphView中添加成员:
CPtrArray m_ptrArray // (类似CStringArray的用法,容器,专门用于存放指针)
⑤在GraphView的OnLButtonUp中保存绘制的图形;
Graph *grapn = new Graph(m_nDrawType,m_ptOrigin,point);
m_ptrArray.Add(graph);
⑥在OnDraw中遍历容器,绘制图形对象
for(int i=0;i
switch(pg->m_nDrawType){
case 1:
pDc->SetPixel(pg->m_ptOrigin,m_color);
break;
case 2:
pDC->MoveTo(pg->m_ptOrigin);
dc.LineTo(pg->m_ptEnd);
break;
case 3:
pDC->Rectangle(CRect(pg->m_ptOrigin,pg->m_ptEnd));
break;
case 4:
pDC->Ellipse(CRect(pg->m_ptOrigin,pg->m_ptEnd));
break;
default:
break;
}
}
//----------------------------------------------------------------
注意:CStringArray 专门用于存放字符串
CPtrArray 专门用于存放指针
方法二:
---------
考题:
---------
① OnDraw和OnPaint的区别
OnDraw():view中绘图相关的操作,
窗口重绘的时候,View子类调用CView类的OnPaint函数,OnPaint调用OnDraw(),实现客户区内容重绘
OnPaint(): WM_PAINT消息的处理函数
② SelectObject(&pen), SelectObject(pen);都正确的原因
③需要LPCRECT,LPRECT,RECT的地方都可以传递一个 CRect对象?
原因:CRect重载了操作符---operator LPCRECT;
|----operator LPRECT;
④radio 的group设置:设置了group属性的radio以及后面的radio都在一组;
直到遇到第二个设置了组的radio
⑤OnCreate和OnInitDialog的区别
七、改变应用程序外观Style
目标:
①图像:修改图标、光标、背景的三种方法
②工具栏:
|-----增加、删除工具栏按钮;
|-----增加、显示,隐藏工具栏;
③状态栏:
|-----定制状态栏,状态栏添加时间显示;
|-----添加进度条
④操作状态栏:CView中获取状态栏对象的方法
⑤托盘:为应用程序添加启动画面
7.1 修改cs: 框架窗口出现之前修改: PreCreateWindow中的cs参数
PreCreateWindow(CREATESTRUCT &cs)的cs中修改启动后的初始外观:
①
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
cs.x = 0;
cs.y = 0;
cs.cx = 300;
cs.cy = 250;
return TRUE;
}
②修改图标光标:自定义窗口类:框架窗口预创建中定义窗口类,view中cs.lpszClass="class name"
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndcls.hCursor = LoadCursor(NULL,IDC_HELP);
wndcls.hIcn = Load(NULL,IID_ERROR); // 使用系统资源,因此,第一个参数为NULL
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.lpfnWndProc = ::DefWindowProc; // 窗口过程必须使用四个参数的窗口过程
wndcls.lpszClassName = "wepull";
wndcls.lpszmenuName = NULL;
wndcls.style = CS_HREDRAW | CS_VREDRAW;
::RegisterClass(&wndcls);
...
// 为修改光标、背景,View的PreCreateWindow中也要加:cs.lpszClass = "your class name"
cs.lpszClass = "your class name";
return TRUE;
}
更方便的方法:
AfxRegisterWndClass()
cs.lpszClass=AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,0,0,LoadIcon(NULL,IDI_CURSOR));
7.2 调用api: 框架窗口运行起来后修改:
CMainFrame::OnCreate()中修改
SetWindowLong
GetWindowLong
SetWindowPos
7.3 调用AfxRegisterWndClass
调用SetClassLong
实例:修改ICON,达到动画效果
①import 自定义ICON
②添加成员:HICON m_hicons[4]
③在CMainFrame::OnCreate中:
// 注意:因为是应用程序自己的ICON,因此第一个参数为application的句柄
m_hicon[0]=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON0));
...
SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hicons[0]);
SetTimer(1,1000,NULL);
void CMainFrame::OnTimer(UINT nIDEvent)
{
SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hisons[1]);
CFrameWnd::OnTimer(nIDEvent);
}
注意:①需要将一个ID转换为一个字符串的场合:MAKEINTRESOURCE
②一个类中启动定时器id,只能在这个类中处理该定时器的超时消息;其他类可以启动相同id的定时器
7.4 CToolBar
7.4.1 添加一个toolbar
①CMainFrame的OnCreate中:
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
②设置停靠
7.4.2 工具栏图标
7.4.2 通过菜单,显示,隐藏toolbar ShowControlBar 涉及停靠的时候,需要重新计算布局并停靠
第一种方式:
if(m_newToolBar.IsWindowVisible()){
m_newToolBar.ShowWindow(SW_HIDE);
}else {
m_newToolBar.ShowWindow(SW_SHOW);
}
RecalcLayout(); // 重新计算布局,并停靠
DockControlBar(&m_newToolBar);
第二种方式:
ShowControlBar(&m_newToolBar,!m_newToolBar.IsWindowVisible(),FALSE);
7.4.2 菜单命令更新
void CMainFrame::OnUpdateNetoobar(CCmdUI* pCmdUI){
pCmdUI->SetCheck(m_newToolBar.IsWindowVisible());
}
7.5 状态栏
①在string table中添加ID
②在CMainFrame的indicators数组中添加IDS_XXX
static UINT indicators[] = {
ID_SEPERATOR,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL
}
③创建
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
④控制状态栏元素:
SetPlaneInfo(); 设置状态栏元素属性
SetPlaneText() 设置状态栏元素文本
CommandToIndex() 由ID得到索引
//代码参考------------------------Status Bar Pane SBP
CClientDC dc(this);
CSize sz = dc.GetTextExtent(str);
m_StatusBar.SetPaneInfo(1,IDS_TIMER,SBPS_NORMAL,size.cx);
// 最后一个参数0必须注意,如果设置为true,则一设置文本,则invalidate,开始更新
m_StatusBar.SetPaneText(1,"text",0);
//-------------------------------------
7.5 状态栏上的进度条:CProgressCtrl
①CMainFrame添加成员:CProgressCtrl ;m_progress
②OnCreate中:
OnCreate(){
...
PostMessage(UM_PROGRESS,0,0);
}
拖动定位出现问题,解决办法:在OnPaint中实现:
void CMainframe::OnPaint(){
CRect rect;
m_wndStatusBar.GetItemRect(2,&rect);// 不能再OnCreate中调用,需要自己定义消息
if(!m_progress.m_hWnd){
m_progress.Create(WS_CHILD|WS_VISIBLE PBS_VERTICAL,rect,&m_wndStatusBar,111);
}else{
m_progress.MoveWindow(rect);
m_progress.SetPos(50);
}
}
7.6 托盘
托盘的实现:
|------- ①添加NOTIFYICONDATA nid;并使用一个成员函数初始化,调用Shell_NotifyIcon(NIM_ADD,&nid);对应①②③
|------- ②处理托盘消息:
| 操作(鼠标,按键...)托盘(tray)会产生消息(这个消息名字自己指定):自定义消息类型,并添加消息处理函数;对应④
|------- ③处理系统命令:最大化,最小化的处理
`------- ④处理close消息:
添加NOTIFYICONDATA nid成员,并初始化
①添加成员:在CMainFrame中添加成员:NOTIFYICONDATA nid;
②初始化:--------结构体赋值
|-------调用Shell_NotifyIcon(NIM_ADD,&nid)
添加成员函数
void CMainFrame::toTray(){
nid.cbSize=(DWORD)sizeof(NOTIFYICONDATA);
nid.hWnd=this->m_hWnd;
nid.uID=IDR_MAINFRAME;
nid.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP; // 让哪些成员有效
nid.uCallbackMessage=UM_SHOWTASK; //自定义的消息名称
nid.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
strcpy(nid.szTip,"电子教室"); //信息提示条为“计划任务提醒”
Shell_NotifyIcon(NIM_ADD,&nid);//在托盘区添加图标
// ShowWindow(SW_HIDE);//隐藏主窗口
}
在OnCreate中调用函数 toTray()
③自定义消息:#define UM_SHOWTASK WM_USER+10
|----头文件申明 :afx_msg LRESULT OnShowTask(wParam,lParam);
|----源文件消息映射 :ON_MESSAGE(UM_SHOWTASK,OnShowTask)
`----消息处理函数 :LRESULT CMainFrame::OnShowTask(WPARAM wParam,LPARAM lParam)
LRESULT CMainFrame::OnShowTask(WPARAM wParam,LPARAM lParam){
//wParam接收的是图标的ID,而lParam接收的是鼠标的行为
if(wParam!=IDR_MAINFRAME)return 1;
switch(lParam){
case WM_RBUTTONUP://右键起来时弹出快捷菜单,这里只有“显示/关闭”
{
LPPOINT lpoint=new tagPOINT;
::GetCursorPos(lpoint);//得到鼠标位置
CMenu menu,*lpSubMenuTray;
menu.LoadMenu(IDR_RCLICK_MENU);//声明一个弹出式菜单
lpSubMenuTray = menu.GetSubMenu(0);//确定弹出式菜单的位置
lpSubMenuTray->TrackPopupMenu(TPM_LEFTALIGN,lpoint->x,lpoint->y,this); //资源回收
HMENU hmenu=menu.Detach();
menu.DestroyMenu();
delete lpoint;
}
break;
case WM_LBUTTONDBLCLK: {//双击左键的处理
if(m_isShow){
this->ShowWindow(SW_HIDE);//简单的显示主窗口
m_isShow = false;
}
else{
this->ShowWindow(SW_SHOW);//简单的隐藏主窗口
m_isShow = true;
}
}
break;
}
return 0;
}
⑤关闭时候销毁托盘: 重写虚函数
void CMainFrame::OnClose(){
::ShellNofityIcon(NIM_DELETE,&nid); // 销毁托盘
CFrameWnd::OnClose();
}
void CMainFrame::OnWindowShow(){ // 不是特别清楚
ShowWindow(SW_SHOW);
}
⑥最小化时候不在任务栏出现,只出现托盘:处理WM_SYSCOMMAND
afx_msg void CWnd::OnSysCommand(xx,xx)
// This method is called by the framework when the user selects a command
// from the Control menu, or when the user selects the Maximize or the Minimize button
void CMainFrame::OnSysCommand(UINT nID,LPARAM lParam){
f(SC_MINIMIZE = nID){
ShowWindow(SW_HIDE); // 隐藏主窗口,其实不是真正的最小化
}
CFrameWnd::OnSysCommand(nID,lParam);
}
注意:这里的SysCommand指的是 最小化,最大化等消息
7.7 右键菜单 : 区分资源ID和菜单项标志ID TrackPopupMenu()
①添加菜单资源 IDR_xx
②添加消息映射:选择WM_RBUTTONDOWN 或者WM_CONTEXMENU
③编写消息处理函数:
void CMenu2View::OnRButtonDown(UINT nFlags,CPoint point){
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu* pPopup = menu.GetSubMenu(0);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,point.x,point.y,
this);
CView::OnRButtonDown(nFlags,point);
}
坐标变换 ClientToScreen()
afx获取的坐标point是客户端坐标,TrackPopupMenu需要的是屏幕坐标
7.8 动态菜单;
|------AppendMenu 添加菜单
|------InsertMenu 插入菜单
|------CreatePopupMenu 创建弹出菜单
①OnCreate中:
CMenu menu;
menu.CreatePopupMenu(); // 创建一个弹出菜单
//GetMenu()->AppendMenu(MF_POPUP | MF_ENABLED,(UINT)menu.m_hWn,"lr");
GetMenu()->InsertMenu(4,MF_BYPOSITION | MY_POPUP,(UINT)m_hWnd,"徐老师");
menu.AppendMenu(MF_STRING,IDM_LL,"LL"); // MF == Menu Flag
menu.AppendMenu(MF_STRING,IDM_YY, "YY");
menu.Detach();
resource.h中:
#define IDM_LL 101 // 也可以自己添加一个菜单资源,然后使用这些菜单资源生成的IDM_XX
#define IDM_YY 102
注意:
①新添加的资源,菜单项,对话框控件,编译后,ID都会出现在resource.h中
②可以添加菜单资源,然后利用这些菜单资源ID,来处理相应用户消息。
②自定义消息映射
|-----头文件中申明:afx_msg void OnLL();
| afx_msg void OnYY();
|-----源文件中定义:ON_COMMAND(IDM_LL,OnLL)
| ON_COMMAND(IDM_YY,OnYY)
`-----源文件中定义函数:void CMainFrame::OnLL(){}
void CMainFrame::OnYY(){}
7.9 动态菜单中出现的几个问题: 通过下面一个电话本案例来展现
①DrawMenuBar(); // 插入菜单后,需要重绘
②GetMenu() // 获得菜单,this指向的对象必须在拥有菜单才有效
---------------------------------------------------------------
案例:view中输入姓名,电话号码,生成菜单;点击菜单,输出姓名电话
-----------------------------------------------------------------
用到的知识:
①回车ascii码:13=0xd 空格的ascii码:
②处理字符消息,最好使用WM_CHAR消息,可以直接得到nChar字符码
③menu是局部变量,如果不detach,程序会崩溃
//----------------------------------------------------------------
① 在view中添加OnChar消息处理器
void CMENU2View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if(0x0d == nChar){
CMenu menu;
menu.CreatePopupMenu();
//GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"通讯录");
// 崩溃的原因:
// ①局部变量,不Detach,程序会崩溃
// ②上面这句话无效:因为this指针指向的类view没有菜单,返回null指针,程序会崩溃
GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"通讯录");
GetParent()->DrawMenuBar(); // 插入菜单后,需要重绘
menu.Detach();
Invalidate(); // 引发窗口重绘
}else{
CClientDC dc(this);
m_str += nChar;
dc.TextOut(0,0,m_str);
}
CView::OnChar(nChar, nRepCnt, nFlags);
}//----------------------------------------------------------------
注意:
①调用Invalidate(),引发窗口重绘
②str.Find(" ");找到字符串中第一个空格的index
②解析m_str中的内容,根据内容,添加菜单项:
|---Resource-> insert menu
| |---1 右键--> classwizard
| |---删除弹出菜单test
| `---头文件和原件代码移动
`----Resource.h: #define IDM_HELLO #define IDM_PHONE1 #define IDM_PHONE2 ...
注意:上面的方法就是利用资源编辑工具和classwzard为动态菜单添加消息响应,
因为动态菜单项的ID需要自己分配,利用工具可以实现就像操纵静态菜单一样,知识资源ID一样
③在消息处理器重处理菜单消息
7.10 设计启动画面: project -> add to project -->> componets and controls (组件和控件)
7.11 工具栏图标
HICON hIcon = NULL;
CImageList imageList;
CSize imageSize(45,45);
//加载icon,创建icon列表,自定义大小---coldimage
imageList.Create(imageSize.cx,imageSize.cy, ILC_COLORDDB|ILC_MASK, 2, 1);
hIcon = (HICON)::LoadImage(NULL,"image\\ToolBarColdImage\\broadcast.ico",\
IMAGE_ICON,imageSize.cx,imageSize.cy,LR_LOADFROMFILE);
imageList.Add(hIcon);
hIcon = (HICON)::LoadImage(NULL,"image\\ToolBarColdImage\\stopbroadcast.ico",\
IMAGE_ICON,imageSize.cx,imageSize.cy,LR_LOADFROMFILE);
imageList.Add(hIcon);
m_wndToolBar.GetToolBarCtrl().SetImageList(&imageList);
imageList.Detach();
hIcon = NULL;
//加载icon,创建icon列表,自定义大小---hotimage
imageList.Create(imageSize.cx,imageSize.cy, ILC_COLORDDB|ILC_MASK, 2, 1);
hIcon = (HICON)::LoadImage(NULL,"image\\ToolBarHotImage\\broadcast.ico",\
IMAGE_ICON,imageSize.cx,imageSize.cy,LR_LOADFROMFILE);
imageList.Add(hIcon);\
hIcon = (HICON)::LoadImage(NULL,"image\\ToolBarHotImage\\stopbroadcast.ico",\
IMAGE_ICON,imageSize.cx,imageSize.cy,LR_LOADFROMFILE);
imageList.Add(hIcon);
m_wndToolBar.GetToolBarCtrl().SetHotImageList(&imageList);
imageList.Detach();
//设置工具栏按钮文字
m_wndToolBar.SetButtonText(m_wndToolBar.CommandToIndex(ID_BROADCAST_START),"开始广播");
m_wndToolBar.SetButtonText(m_wndToolBar.CommandToIndex(ID_BROADCAST_STOP),"停止广播");
//设置工具栏大小
CRect tbBtnRect;
m_wndToolBar.GetItemRect(0,&tbBtnRect);
m_wndToolBar.SetSizes(tbBtnRect.Size(),CSize(imageSize.cx,imageSize.cy));
八、 消息路由问题:
8.1 命令消息和通告消息的路由
①菜单命令消息的路由:
消息映射宏: WM_COMMAND(ID_MENU,OnMENU)
路由顺序:view---doc---frame---app
注意:一定不会交由CAboutDlg处理
②通告消息路由:
控件消息(对话框上的消息),通告消息:向父窗口通知事件发生,让父窗口处理
消息映射宏:WM_COMMAND
8.2 CMainFrame拦截住WM_COMMAND
不让view处理,由MainFrame自己处理,其他消息不拦截
命令消息的路由处理过程:
------------------------------------------------------------------------
AfxWndProc--AfxCallWndProc--WindowProc--OnWndMsg---OnCommand---OnCmdMsg
|
`----OnNotify ---OnCmdMsg
------------------------------------------------------------------------
实现方法:根据以上基本原理,我们可以在OnCommand中截获并处理WM_COMMAND
OnCommand是一个虚函数,可以在MainFrame中override该函数
①添加OnCommand虚函数:
OnCommand(wParam,lParam):
①wParam的低字节Wie消息uMsg, 高位是通告消息(如果来自空间消息)
LOWORD(wParam):得到wParam的低字节序
HIGHWORD(wParam):得到高字节
②lParam:如果来自控件,则是控件ID
GetActiveView():
①在主框架窗口中处理WM_COMMAND,在view中绘制图形,需要获得view对象
bool CMainFrame::OnCommand(WPARAM wParam,LPARAM lParam){
int id = LOWORD(wParam);
CMenu2View* pView = (CMenu2View*)GetActiveView();
if(id>IDM_PHONE1 && id
dc.TextOut(0,0,(pview->m_attr).Get(id-IDM_PHONE1));
return true; // 这里一定要返回,自己范围内的事情做完就返回
}
return CFrameWnd::OnCommand(wParam,lParam);
}
注意:①GetParent(): 视图中获得框架
②GetActiveView():框架中获得视图
8.3 对话框中处理WM_KEYDOWN(按键)消息: 孙鑫深入浅出
九、专题问题
9.1 对话框最大化、最小化按钮的出现:
int CXXXXDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
ModifyStyle(0, WS_MAXIMIZEBOX | WS_MINIMIZEBOX, SWP_FRAMECHANGED);
return 0;
}