MFC学习之路


《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<m_ptrArray.GetSize();i++){
Graph* pg = (Graph*)m_ptr.GetAt(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<IDM_PHONE1+m_attr.GetSize()){
CClientDC dc(pView);
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;
}
 





转载于:https://my.oschina.net/u/1772925/blog/410340

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值