孙鑫 XML培训视频 笔记 名称空间
孙鑫VC++讲座笔记-(6)菜单编程_

1,弹出菜单(Pop-up)是不能用来作命令响应的。
1,弹出菜单(Pop-up)是不能用来作命令响应的。
2,MFC中菜单项消息如果利用ClassWizard来对菜单项消息分别在上述四个类中进行响应,则菜单消息传递顺序:View类--Doc类--CMainFrame类--App类。
菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数。
具体:当点击一个菜单项的时候,最先接受到菜单项消息的是CMainFrame框架类,CMainFra me框架类将会把菜单项消息交给它的子窗口View类,由View类首先进行处理;如果View 类检测到没对该菜单项消息做响应,则View类把菜单项消息交由文档类Doc类进行处理;如果Doc类检测到Doc类中也没对该菜单项消息做响应,则Doc类又把该菜单项消息交还给View类,由View类再交还给CMainFrame类处理。
如果CMainFrame类查看到CMainFrame 类中也没对该消息做响应,则最终交给App类进行处理。
3,消息的分类:标准消息,命令消息,通告消息。
[标准消息]:除WM_COMMAND之外,所有以WM_开头的消息。
[命令消息]:来自菜单、加速键或工具栏按钮的消息。
这类消息都以WM_COMMAND呈现。
在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的w Param参数识别。
[通告消息]:由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。
这类消息也是以WM_COMMAND形式呈现。
说明:1)从CWnd派生的类,都可以接收到[标准消息]。
2)从CCmdTarget派生的类,都可以接收到[命令消息]和[通告消息]。
4,一个菜单拦可以有若干个子菜单,一个子菜单又可以有若干个菜单项等。
对菜单栏的子菜单由左至右建立从0开始的索引。
对特定子菜单的菜单项由上至下建立了从0开始的索引。
孙鑫老师笔记16课

Lesson 16 线程同步与异步套接字1.事件对象事件对象同上一课中的互斥对象一样属于内核对象,它包含三个成员:使用读数,用于指明该事件是一个自动重置的还是人工重置的事件的布尔值,用于指明该事件处于已通知状态还是未通知状态的布尔值.当人工重置的事件对象得到通知时,等待该事件对象的所有纯种无变为可高度线程,而一个自动重置的事件对象得到通知时,等待该事件对象的线程中人有一个变为可高度线程.所以一般使用线程同步时使用自动重置.创建事件对象:HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全选项,默认为NULLBOOL bManualReset, // reset type,TRUE(人工),FALSE(自动)BOOL bInitialState, // initial state,TRUE(有信号状态)LPCTSTR lpName // object name.事件对象名);BOOL SetEvent(HANDLE hEvent);把指定的事件对象设置为有信号状态BOOL ReSetEvent(HANDLE hEvent);把指定的事件对象设置为无信号状态BOOL CloseHandle( HANDLE hObject ); // handle to object关闭事件对象DWORD WaitForSingleObject(//请求内核对象,一旦得到事件对象,就进入代码中HANDLE hHandle, // handle to objectDWORD dwMilliseconds // time-out interval);以下是一个模拟火车站售票的多线程程序(使用事件对象实现线程同步)#include <windows.h>//加入头文件,Window API库#include <iostream.h>//C++标准输入输出库int tickets = 100;//共享的资源,火车票HANDLE g_hEvent;//全局的事件对象句柄//线程处理函数原型声明DWORD WINAPI Thread1Proc(LPVOID lpParameter // thread data);DWORD WINAPI Thread2Proc(LPVOID lpParameter // thread data);void main(){// g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);//创建一个人工重置的匿名事件对象,当调用SetEvent时所有的线程都可以执行,不能实现同步// SetEvent(g_hEvent);//将事件对象设置为有信号状态g_hEvent = CreateEvent(NULL, FALSE, FALSE, "tickets");//创建一个自动重置的有名事件对象,当调用SetEvent时只有一个线程可以执行SetEvent(g_hEvent);//可以通过创建有名的事件对象来实现只有一个程序实例运行if (g_hEvent)//有值{if (ERROR_ALREADY_EXISTS == GetLastError())//以事件对象存在为条件实现只有一个实例运行限制,因为事件对象是内核对象,由操作系统管理,因此可以在多个线程间访问{cout << "only one instance can run!" << endl;return;}}HANDLE hThread1;HANDLE hThread2;hThread1 = CreateThread(NULL, 0, Thread1Proc, NULL, 0, NULL);hThread2 = CreateThread(NULL, 0, Thread2Proc, NULL, 0, NULL);CloseHandle(hThread1);//释放线程句柄CloseHandle(hThread2);Sleep(4000);CloseHandle(g_hEvent);//注意最后释放事件对象句柄,在MFC中在类的析构函数中完成}DWORD WINAPI Thread1Proc(LPVOID lpParameter // thread data){//其中的SetEvent函数应该在两个判断中都调用,以防止因条件不满足而造成对象不能被设置为有信息状态while(TRUE){WaitForSingleObject(g_hEvent, INFINITE);//无限期等待事件对象为有信号状态if (tickets > 0)//进入保护代码{cout << "Thread1 is selling tickets : " << tickets-- << endl;SetEvent(g_hEvent);}Else//如果票已经售完,退出循环{break;SetEvent(g_hEvent);}}return 0;}DWORD WINAPI Thread2Proc(LPVOID lpParameter // thread data){while(TRUE){WaitForSingleObject(g_hEvent, INFINITE);//等待事件对象,如果对象为有信号状态,可以请求该对象资源,并将其设置为综上:为实现线程间的同步,不应该使用人工重置的事件对象,而应该使用自动重置的事件对象2.关键代码段(临界区)工作在用户方式下,它是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权,通常把多线程访问同一种资源的那部分代码当作关键代码段.VOID InitializeCriticalSection(//初始化代码段LPCRITICAL_SECTION lpCriticalSection //[out] critical section,使用之前要构造);VOID EnterCriticalSection(//进入关键代码段(临界区)LPCRITICAL_SECTION lpCriticalSection // critical section);VOID LeaveCriticalSection(//离开关键代码段(临界区)LPCRITICAL_SECTION lpCriticalSection // critical section);VOID DeleteCriticalSection(//删除关键代码段(临界区)LPCRITICAL_SECTION lpCriticalSection // critical section);种方法比较简单!但缺点是如果使用了多少关键代码码,容易赞成线程的死锁(使用两个或以上的临界区对象或互斥对象,造成线程1拥有了临界区对象A,等待临界区对象B的拥有权,线程2拥有了临界区对象B,等待临界区对象A 的拥有权,形成死锁,程序无法执行下去!3.互斥对象,事件对象,关键代码段的比较⏹互斥对象和事件对象都属于内核对象,利用内核对象进行线程同步时,较慢,但利用互斥对象和事件对象这俗人内核对象,可以在多个进程中的各个纯种间进行同步⏹关键代码段工作在用户方式下,同步速度快,但很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值4.基于消息的异步套接字编程Windows套接字在两种模式下执行I/O操作:阻塞模式和非阻塞模式.在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回(也就是不地将控制权交还给程序),例如,程序中调用了recvfrom函数后,如果这时网络上没有数据传送过来,该函数就会阻塞程序的执行,从而导致调用线程暂停运行,但不会阻塞主线程运行.在非阻塞模式下,Winsock函数无论如何都会立即返回,在该函数执行的操作完成之后,系统会采用某种方式将操作结果通知给调用线程,后者根据通知信息可以判断该操作是否正常完成.Windows Sockets采用了基于消息的异步存取策略以支持Windows的消息驱动机制,Windows Sockets的异步选择函数WSAAsyncSelect提供了消息机制的网络事件选择,当使用它登录的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,指示发生的网络事件,以及与该事件相关的一些信息.因此可针对不同的网络事件进行登录,一旦有数据到来,就会触发这个事件,操作系统就会通过一个消息来通知调用线程,后者就可以在相应的消息响应函数中接收这个数据.因为是在该数据到来之后,操作系统发出的通知,所以这时肯定能够接收这个数据.异步套接字能够有效的提高应用程序的性能.一些主要函数<1>//为指定的套接字请求基于Windows消息的网络事件通知.自动设置为非阻塞模式int WSAAsyncSelect(SOCKET s, //标识请求网络事件通知的套接字描述符HWND hWnd, //标识一个网络事件发生时接收消息的窗口的句柄unsigned int wMsg, //指定网络事件发生时窗口将接收到的消息,(自定义消息)long lEvent //指定网络事件类型,可以位或操作组合使用);<2> 获得系统中安装的网络协议的相关信息int WSAEnumProtocols(LPINT lpiProtocols,//[in]以NULL结尾的协议标识号数组.如果为NULL,返回可用信息LPWSAPROTOCOL_INFO lpProtocolBuffer,//[out]存放指定的完整信息ILPDWORD lpdwBufferLength//[in,out]输入时传递缓冲区长度,输出最小缓冲区长度);<3>初始化进程使用的WS2_32.DLLint WSAStartup(WORD wVersionRequested,//高位字节指定Winsock库的副版本,低位字节是主版本号LPWSADATA lpWSAData//[out]用来接收Windows Sockets实现细节);<4> 终止对套字库WS2_32.DLL的使用int WSACleanup (void);<5> Winsock库中的扩展函数WSASocket将创建套接字SOCKET WSASocket(int af,//地址簇标识int type,//socket类型SOCK_DGRAM为UDPint protocol,//协议簇LPWSAPROTOCOL_INFO lpProtocolInfo,//定义创建套接字的特性,如果为NULL,则//WinSock2.Dll使用前三个参数决定使用哪个服务提供者GROUP g,//保留DWORD dwFlags//指定套接字属性的描述,如果为WSA_FAG_OVERLAPPED则为一个重叠套接字,与文件中相似,);然后在套接字上调用WSASend, WSARecv,WSASendTo,WSARecvFrom,SWAIoctl 这些函数都会立即返回,这些操作完成后,操作系统会通过某种方式来通知调用线程,后者就可以根据通知信息判断操作是否完成<6> WSARecvFrom接收数据报类型的数据,并保存数据发送方的地址int WSARecvFrom(SOCKET s,//套接字描述符LPWSABUF lpBuffers,//指向WSABUF数据指针,一个成员缓冲区指针buf,另个长度DWORD dwBufferCount,//lpBuffers数组中WSABUF结构体的数上,一般为1LPDWORD lpNumberOfBytesRecvd,//[out]接收完成后数据字节数指针LPDWORD lpFlags,//[in/out]标志会影响函数行为,设置为0即可struct sockaddr FAR *lpFrom,//[out]可选,指向重叠操作完成后存放源地址的缓冲区LPINT lpFromlen,//[in/out]指定lpFrom缓冲区大小的指针LPWSAOVERLAPPED lpOverlapped,//指向重叠套接字指针,非重叠忽略LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine//一个指定接收完成时调用的完成全程指针(非重叠套接字的忽略0);如果创建是重叠套接字,最后两个参数值要设置,因为这时将会采用重叠I/O,函数会返回,当接收数据这一操作完成后,操作系统会调用lpCompletionRoutine参数指定的例程来通知调用线程,这个例程就是一个回调函数.<7>WSASendTo发送数据报类型的数据int WSASendTo(SOCKET s,//套接字描述符LPWSABUF lpBuffers,DWORD dwBufferCount,LPDWORD lpNumberOfBytesSent,DWORD dwFlags,//0即可const struct sockaddr FAR *lpTo,//可选指针,指向目标套接字的地址int iToLen,//lpTo中地址长度LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);5.一个网络聊天室程序的实现新建工程基于对话框,工程名为Chat,并添加一些控件主要两个编辑,IP控件和发送按钮[1]加载套接字库需要加载套接字库并进行版本协商,AfxSocketInit只能加载1.1版本的套接字库,本例使用WSAStartup加载系统安装可用版本,在CChatApp的initInstance函数加入并在stdafx.h文件中加入头文件#include <winsock2.h>[2]创建并初始化套接字在CChatDlg类增加一个SOCKET类型的成员变量,m_socket,高为私有,再添加一个BOOL类型的成员函数:InitSocket,初始化该类的套接字成员BOOL CChatDlg::InitSocket(){//使用扩展函数创建套接字m_socket = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, NULL, 0);if (INVALID_SOCKET == m_socket){MessageBox("创建套接字失败!");return FALSE;}//要绑定套按字的本地址和协议簇,端口号SOCKADDR_IN addrSock;addrSock.sin_addr.S_un.S_addr = htonl(ADDR_ANY);addrSock.sin_family = AF_INET;addrSock.sin_port = htons(6000);//绑定套接字到本地套按地址上if(SOCKET_ERROR == bind(m_socket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR))){ MessageBox("绑定失败!");return FALSE;}//调用WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ)为网络事件定义消息! //此时如果发生FD_READ网络事件,系统会发送UM_SOCK(自定义)消息给应用程序! //使用相应的消息响应函数来处理,程序并不会阻塞在这儿了!CChatDlg类的OnInitDialog函数中调用这个函数,完成套接字的初始化工作[3]实现接收端的功能在CChatDlg头文件中定义自定义的消息:UM_SOCK#define UM_SOCK WM_USER + 1在CChatDlg头文件中添加UM_SOCK响应函数原型声明protected:HICON m_hIcon;// 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//定义的消息要带参数,LPARAM中的低字节是保存网络事件(如FD_READ),//高字节保存错误信息,WPARAM保存是发生网络事件的SOCKET标识afx_msg void OnSock(WPARAM, LPARAM);//自定义消息的响应函数原型DECLARE_MESSAGE_MAP()在CChatDlg类的源文件中添加UM_SOCK消息映射BEGIN_MESSAGE_MAP(CChatDlg, CDialog)//{{AFX_MSG_MAP(CChatDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BTN_SEND, OnBtnSend)//}}AFX_MSG_MAPON_MESSAGE(UM_SOCK, OnSock)//消息与其响应函数的映射END_MESSAGE_MAP()消息响应函数的实现,因为同时可以请求多个网络事件如FD_READ或RDWRITE最好对所接受的消息进行判断后处理,本例中只有FD_READ,但仍判断处理,要注意是消息接收两个参数,低字节是保存网络事件(如FD_READ),高字节保存错误信息,WPARAM保存是发生网络事件的SOCKET标识.switch (LOBYTE(lParam)){case FD_READ://发生是网络读取事件WSABUF wsaBuf;char recvBuf[200];wsaBuf.buf = recvBuf;wsaBuf.len = 200;DWORD dwRead;DWORD dwFlag = 0;SOCKADDR_IN addrFrom;int len = sizeof(SOCKADDR);if(SOCKET_ERROR == WSARecvFrom(m_socket, &wsaBuf, 1, &dwRead, &dwFlag, (SOCKADDR*)&addrFrom, &len, NULL, NULL)){MessageBox("接收网络数据失败!");return;}CString strRecv;CString strTemp;strRecv.Format("%s 说: %s", inet_ntoa(addrFrom.sin_addr), recvBuf);GetDlgItemText(IDC_EDIT_RECV, strTemp);strRecv += "\r\n";strRecv += strTemp;[4]发送端按钮的实现[5]终止套接字库的使用为CChatApp类增加一个析构函数,主要是在此函数中调用WSACleanup函数,终止对套接字库的使用[6]在CChatDlg类中关闭套接字,添加一个析构函数,首先判断是否该套接字库有值,如果有的话关闭套接字4. 利用主机名实现网络访问struct hostent FAR *gethostbyname(const char FAR *name //从主机名中获取IP地址);Hostent结构体:struct hostent {char FAR * h_name;char FAR * FAR * h_aliases;short h_addrtype;short h_length;char FAR * FAR * h_addr_list;//空中止的IP地址列表,是一个char*字符数组,因为一个//主机可能有多个IP,选择第一个即可};由主机IP转换成主机名struct HOSTENT FAR * gethostbyaddr(const char FAR *addr,//指向网络字节序表示的IP地址指针int len,//地址长度,对于AF_INET必须为4int type//类型AF_INET);接收方部分代码可改为;。
孙鑫VC学习笔记第9课

OnCreate()中
SetClassLong(m_hWnd,GCL_HBRBACKGROUND,(LONG)GetStockObject(BLACK_BRUSH));
SetClassLong(m_hWnd,GCL_HCURSOR,(LONG)LoadCursor(NULL,IDC_HELP));
2.创建一个不断变化的图标。用定时器和SetClassLong完成
a.准备三个图标文件,放在RES文件夹,Insert->Resource-三个图标,
b.在CMainFrame中增加图标句柄数组,m_hIcons[3]
m_progress.Create(WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
rect,&m_wndStatusBar,123);
m_progress.SetPos(50);
}
最后在OnCreate中调用 PostMessage(UM_PROGRESS);//不能用SendMessage()
然后将其初始化
c.然后在定时器中实现
3.工具栏的编程
a.加入分隔符的方法,向右拖动即可;
b.删除按纽的方法,拖出即可。
4.创建一个新的工具栏的方法
a.插入一个工具栏,画出其图形。
b.在头文件中,定义CToolBar m_newToolBar
c.在MainFrm.cpp的OnCreate()中调用
GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str);
孙鑫老师VC++教学视频学习另一篇笔记

孙鑫老师VC++教学视频学习另一篇笔记<< 一:掌握C++>>1:struct 或者 class的定义完成后面一定不要忘记分号!2: 可以这么写:cout<class test{public:test(){cout<<"aaa"<using namespace std;class test{public:test(){cout<<"aaa"<>1:_tWinMain实际上是一个宏,其等于WinMain!2:_tWinMain()的定义位于文件appmodul.cpp中,而CWinapp的定义位于appcore.cpp中,AfxWinMain()在winmain.cpp当中.而负责注册窗口类的函数AfxEndDeferRegisterClass()位于wincore.cpp当中.至于窗口类的定义,在MFC 中,微软为我们定义了好几个默认的窗口类.3:所谓CWnd对象,只是在CWnd类中封装了创建窗口的过程然后保存了生成的窗口句柄而已,然后通过构造一个它的对象来启动那些过程,并不是说CWnd对象与窗口有什么固定的关系.又因为CMainFrame与CView都是从CWnd派生而来的,所以CMainFrame 与CView对象之于窗口的关系也差不多.也就是说,一个窗口关闭了,并不代表那个对象一定也销毁了.还可以从那个对象出发,再调用一遍其中的函数生成窗口,但是若是对象都销毁了,则窗口一定不会存在!CMainFrame对应窗口的标题栏及菜单栏为非客户区,工具栏及以下为客户区.而CView对象对应窗口的客户区为工具栏以下区域.4:具体追踪过程见TrackMfc 工程!5:为了修改程序的背景色,我在View类的PreCreateWindow()中新定义了一个窗口类如下:WNDCLASS wndcls;wndcls.cbClsExtra=0;wndcls.cbWndExtra=0;wndcls.hbrBackground=(HBRUSH)::CreateSolidBrush(RGB(2 08,221,238));wndcls.hCursor=::LoadCursor(NULL, IDC_ARROW);wndcls.hIcon=LoadIcon(::AfxGetInstanceHandle(),MAKEINT RESOURCE(IDI_MY ICON));wndcls.hInstance=AfxGetInstanceHandle();wndcls.lpfnWndProc=(WNDPROC)::DefWindowProc;wndcls.lpszClassName="wndcls";wndcls.lpszMenuName=NULL;wndcls.style=CS_HREDRAW | CS_VREDRAW;cs.lpszClass="wndcls";却发现一启动程序就弹出一个消息框说"建立空文档失败!".几番查找,才发现还要将自定义的窗口类进行注册才可以,在最后一句前面加上一句::RegisterClass(&wndcls);问题就解决了,背景色也变了,可是窗口图标还是没有变成我自画的IDI_MYICON,怎么回事呢?原来改变图标要在MainFrame类的PreCreateWindow()中进行,可是只改变个图标定义一个窗口类太过麻烦了,可以用下面的方法:cs.lpszClass=AfxRegisterWndClass(NULL,NULL,NULL,AfxGetApp()->LoadIcon (IDI_MYICON));使用了一个AfxRegisterWndClass()的函数.当然在View类中做一个大大的窗口类也是相当麻烦的,所以也可以使用AfxRegisterWndClass()函数!只用一句:cs.lpszClass=AfxRegisterWndClass(CS_HREDRAW |CS_VREDRAW,::LoadCursor(NULL,IDC_CROSS,(HBRUSH)::CreateSolidBrush(RGB(208,221,238)), 0);即可!做完上面这些,我通过import重新载入了一个ICON,在ResourceView窗口中直接删除了原本自己画的IDI_MYICON,然后将新载入的ICON改ID 为IDI_MYICON,然后重新编译,却得不到想要的效果,然后我在Build菜单下点击了Rebuild All,编译完成后新图标才显示出来,具体原因估计跟预编译有关!6:还有就是即使如上所示改变了窗口左上角的那个图标,可应用程序自身的图标却还是那个MFC标准图标,这时,你只需将你想要的图标放到工程的res文件夹下,交将之改名为标准MFC图标的名称,再做个Rebuild All就可以将之更改!之于上面的窗口类定义中菜单怎么可以定义为NULL,是因为由MFC产生的代码,菜单的创建是在App类的InitInstance()中在构造单文档模板的时候将菜单的资源标识传递过来,并进行相关的创建工作的!所以上面传递NULL并不会影响菜单的创建的. (相关更多知识见第八课的笔记)<< 三: GDI编程 >>1:如果你想画一个虚线线条,也就是说线形为PS_DASH,那么线宽必须是1才行.具有同样要求的还有PS_DOT...2:如何创建一个透明画刷CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NU LL_BRUSH));这就可以了,然后选入DC使用即可!3:关于画点,我们一般用SetPixel()函数,可是你会发现不管你把画笔的线宽设为几,画出来的点都是一个像素大小,但通常我们希望我们指定4的大小时可以画一个比原先大小大相应程度的点,这时,我想到的办法就是画点部分用画圆函数来实现,只要把填充画刷设置合理即可.不知还有没别的办法.<< 四:文本编程 >>1:创建普通插入符用CreateSolidCaret(),而创建位图插入符用CreateCaret()函数.2:路径层(Path)的概念!3:我试图写一个MyNotePad的小应用程序,已完成的任务有:设置自定义的图标,窗口背景,光标.在窗口显示插入符,并让插入符随鼠标的点击而显示在相应的位置,用TextOut完成串的输入,显示,并保存于一个CString对象中,可是我发现显示文本的背景色(默认是白色)与自定义的窗口背景色不一致,我先是用如下的代码:hdc.SetBkColor(hdc.GetBkColor));来设置文本背景色,可是背景色仍然是默认的白色,我不停地想是不是SetBkcolor()不可以用,但当时真是笨,明明用GetBkColor()取到的就是文本的背景色,你再设置回去,那不就相当于什么都没做嘛!当时脑子中充斥的想法是以为GetBkColor()取到的是窗口的背景色呢!呵呵,笨!另外,hdc.SetTextColor()可以设置文本的颜色.但还有一个问题:下面是我处理退格键的代码:void CMyNotePadView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {// TODO: Add your message handler code here and/or call defaultif(0x0d==nChar){// m_ptOrigin}else if(0x08==nChar){CClientDC hdc(this);hdc.SetBkColor(RGB(208,221,238));hdc.SetTextColor(RGB(208,221,238));hdc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);m_strLine=m_strLine.Left(m_strLine.GetLength()-1);}else{m_strLine+=nChar;}::InvalidateRect(m_hWnd,NULL,FALSE);CView::OnChar(nChar, nRepCnt, nFlags);}为了应付窗口切换时的重绘,我把输出工作放在了OnDraw()函数中了,别的都是正常的,可当退格的时候后面的那个残留的插入符没有消失,仍然保留着,我找了半天找不到原因,最后才发现原来调用::InvalidateRect()来启动OnDraw()时,第三个参数如果传FALSE,会导致原来无效区域的背景不刷新,然后我将之换成TRUE就OK了!4:至于类似卡拉OK字幕的平滑变色效果,可以用DrawT ext()来实现.<< 五:菜单编程 >>1:消息的分类及路由形态:(1)标准消息:除WM_COMMAND之外,所有以WM_开关的消息都是标准消息.(2)命令消息:来自菜单,加速键或工具栏按钮的消息.这类消息都是以WM_COMMAND呈现.在MFC 中,通过菜单项的标识ID来区分不同的命令消息,在SDK中,则是以wParam参数识别.(3)通告消息:由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生.这类消息也是以WM_COMMAND形式呈现.关于消息的接收:只要是从CWnd派生的类,都既可以接收标准消息,又可以接收命令消息以及通告消息.而其它从CCmdTarget派生的类,就只能接收命令消息与通告消息,而不能接收到标准消息.另外,从视频教材的第一个菜单响应例子的实验证明,一个WM_COMMAND消息,是首先被CMainFrame类接收到,但它先交由它的子窗口View类处理,如果View类没有相应的处理函数,就由View 类将此消息交由Doc类处理,如果Doc类也没有相应的处理函数,那么Doc类会将消息返还给View类,而View类再将消息返还给MainFrame类,这时才检查MainFrame类中有没有相应的消息处理函数,如果仍然没有的话,它就将消息上还给了App类处理(如果在这里仍没有处理函数,则回到CCmdTarget类,然后由FrameWork会将消息传递给::DefWindowProc()函数,注意这儿绕过了CWinThread,因为CWinThread并不参与消息路由!)并且一个消息一旦在某个类中被响应过,则不再接着传递!(关于这个看<<深入浅出MFC>>第9章的图9-4会很清楚!)而对于一个标准WM_消息,则由CMyView类先处理,如果没有处理函数,则上溯到CView,如果仍没有处理函数,则上溯到CWnd类,如果仍没有处理函数,则上溯到CCmdTarget了.这就是消息的路由过程!2:如何在菜单项上加上对号标记:在MainFrame类的OnCreate()中最后加入GetMenu()->GetSubMenu(3)->CheckMenuItem(0,MF_BYPO SITION | MF_CHECKED);另外,在响应刷新消息的函数中由MFC传入的那个CCmdUI*指针也可以用于此!3:如何在菜单项中设置默认菜单项:同样在MainFrame类的OnCreate()的最后加入GetMenu()->GetSubMenu(3)->SetDefaultItem(0,TRUE);即可.要注意,一个子菜单中只可以有一个默认(缺省)菜单项.以最后一个设置为默认的菜单项为准.4:如何设计图形标记菜单:同样在MainFrame类的OnCreate()的最后加入GetMenu()->GetSubMenu(3)->SetMenuItemBitmap(,,,);要注意,一个菜单的图形标记只能是13*13大小的位图,这个数据可以通过GetSystemMetrics(SH_CXMENUCHECK);或者GetSystemMetrics(SH_CYMENUCHECK);来获取!5:如何Enable或者Disable一个菜单项同样在MainFrame类的OnCreate()的最后加入GetMenu()->GetSubMenu(3)->EnableMenuItem(,);要注意,菜单项的Enable或者Disable功能的正常完成需要在MainFrame类的构造函数中将一个叫m_bAutoMenuEnable的成员变量设置为FALSE.但是如果做了m_bAutoMenuEnable=FALSE;后,会导致CMainFrame so no ON_UPDATE_COMMAND_UI or ON_COMMAND handlers are needed!6:如何让一个菜单显示或不显示:要让菜单不显示,在MainFrame类的OnCreate()的最后加入SetMenu(NULL);即可!要让菜单显示,可以先构造一个CMenu对象,再利用该对象的LoadMenu(ID of menu)函数将之加载,然后再用SetMenu(该menu 对象的指针)即可!要注意,你的CMenu对象必须是一个定义在CMainFrame类中的成员变量,不能是一个在OnCreate()函数中临时定义的一个变量,因为该函数结束时其中的成员变量要发生析构,那么你的菜单就要出问题.或者你也可以定义临时函数内的局部变量,然后在SetMenu()完了后用一个menu.Detach()成员函数将菜单与菜单对象的关系切断,这样也是可以的.7:MFC之于菜单项的命令更新机制:菜单项状态的维护是依赖于CN_UPDATE_COMMAND_UI消息,谁捕获CN_UPDATE_COMMAND_UI消息,MFC就在其中创建一个CCmdUI对象.我们可以通过手工或者利用ClassWizard在消息映射中添加ON_UPDATE_COMMAND_UI宏来捕获CN_UPDATE_COMMAND_UI消息.在后台所做的工作是:操作系统发出WM_INITMENUPOPUP消息,然后由MFC 的基类如CFrameWnd接管.它创建一个CCmdUI对象,并与第一个菜单项相关联,调用对象的一个成员函数DoUpdate().这个函数发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针.同一个CCmdUI对象就设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项.更新命令UI处理程序仅应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目.用我自己的话来讲就是:命令更新机制就是当无论何时要显示菜单项时,系统都会对CMainFrame类发出一个包含每个菜单项的UPDATE_COMMAND_UI消息,如果你在CMainFrame类中针对某个菜单项做了相应的消息处理函数,那么你可以在这个函数中检测程序中的某些条件,做出让某个菜单项显示或不显示或者变灰的决定!8:如何制作右键弹出菜单:方法一:在VC中,点菜单上的Project/Add To Project/Components and Controls...,在弹出的对话框中打开Visual C++ Components文件夹,再找到Pop-up Menu,选择Insert按钮,在随后的确认框中点确定,然后在随后的 Add pop-up menu to:下拉列表框中选择View类,资源ID可以不改,按OK,然后重新编译工程就可以看到结果了.方法二:其实在方法一中,MFC在后台所做的就是在View类中加入了一个函数:OnContextMenu()函数如下:void CMyNotePadView::OnContextMenu(CWnd*, CPoint point){// CG: This block was added by the Pop-up Menu component {if (point.x == -1 && point.y == -1){ //keystroke invocationCRect rect;GetClientRect(rect);ClientToScreen(rect);point = rect.TopLeft();point.Offset(5, 5);}CMenu menu;VERIFY(menu.LoadMenu(CG_IDR_POPUP_MY_NOTE_PAD_VI EW));CMenu* pPopup = menu.GetSubMenu(0);ASSERT(pPopup != NULL);CWnd* pWndPopupOwner = this;while (pWndPopupOwner->GetStyle() & WS_CHILD)pWndPopupOwner = pWndPopupOwner->GetParent();pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, pWndPopupOwner);}}这就使得我们也可以模仿以上行为做出自己的弹出菜单,首先在View类中添加对右键的响应函数,在其中也写如下代码:CMenu menu;menu.LoadMenu(IDR_MENU1);//这个ID所对应的菜单你要事先做好CMenu* pPopup=menu.GetSubMenu(0);pPopup->TrackPopupMenu(PM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);就可以了.但是现在的菜单显示的位置很离谱,原来显示弹出菜单用得是屏幕坐标,而你在View类中捕获的位置坐标是窗口坐标,这时就得用这个函数:ClientToScreen(point);在TrackPopupMenu()之前做个转换!这样就正常了.9:右键弹出菜单项的响应:在响应右键弹出菜单时,你可以在View类中响应,也可以在MainFrame类中响应,这主要取决于你在做pPopup->TrackPopupMenu()时的第四个参数,也就是该弹出菜单的父窗口.如果如上是this,则只能在View 类中响应,如果用GetParent()设其为MainFrame类,则既可以View类中响应,又可以在MainFrame类中响应.并且如果View类跟MainFrame类皆有响应函数,则优先响应View类,其实也就是说MainFrame中的响应函数将被忽略.所以为了方便,弹出菜单的父类就尽可能设为MainFrame类!10:如何在菜单栏上动态增加,插入或删除一个子菜单因为以前菜单栏上的子菜单都是在菜单编辑器中事先做好的,而现在要求在程序运行期间按需添加一个子菜单到指定位置.可以用如下代码段实现:CMenu menu;menu.CreatePopupMenu();GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMe nu,"动态菜单");menu.Detach();如上的代码会将新子菜单放在所有已有子菜单项的后面,如果要想在指定的位置插入子菜单,则应该用InsertMenu()而不是AppendMenu(),示例如下:CMenu menu;menu.CreatePopupMenu();GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMe nu,"动态菜单");menu.Detach();menu.CreatePopupMenu();GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"插入菜单");menu.Detach();要在新加入的子菜单下面显示菜单项的示例代码如下:CMenu menu;menu.CreatePopupMenu();GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMe nu,"动态菜单");menu.Detach();menu.CreatePopupMenu();GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"插入菜单");menu.AppendMenu(MF_STRING,111,"How");menu.AppendMenu(MF_STRING,112,"I");menu.AppendMenu(MF_STRING,113,"Feel?");menu.Detach();要在已有子菜单中添加菜单项同样可以用AppendMenu()或者InsertMenu()它们的区别自然是前者在最后放置,后者由你指定位置.示例代码如下:GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,1 14,"动态");GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,M F_BYCOMMAND |MF_STRING,115,"安岳");以上函数中若使用MF_SEPARATOR参数可以插入分割符!要删除一个子菜单或者菜单项可以使用DeleteMenu()函数.如果调用它的是菜单栏,则删除的是子菜单,如果调用它的是子菜单,则删除的是菜单项.至于这种动态菜单项的命令响应则不能再借助于ClassWizard啦,跟手动添加对其它消息的响应一样,需要三个步骤:一:添加响应函数声明在CMainFrame的构造函数中,紧随系统原有的afx_msg int OnCreate();之类的语句之后写上:afx_msg void OnHow();但注意不要写在括住系统自己的响应函数的//{{AFx_MSG...及//}}AFX_MSG之内了.二:添加消息映射在MainFram.cpp中找到以(CMainFrame,CFrameWnd)为参数的BEGIN_MESSAGE_MAP,END_MESSAGE_MAP宏对,在其中添加: ON_COMMAND(IDM_HOW,OnHow)注意这一句是宏语句,不要在其后面加分号.(也注意不要写在//{{AFx_MSG...及//}}AFX_MSG之内了.)因为这个宏中需要ID,所以自己还要手动的在Resource.h中加入:#Define IDM_HOW 111 一句.(111是AppendMenu()时指定的ID).三:完成消息响应函数在MainFrame.cpp文件后面写如下代码段:void CMainFrame::OnHow(){MessageBox("Hey man,what are you doing!");}即完成了此命令消息响应.11:在本课的最后,为了实现一个在CMainFrame中截获WM_COMMAND消息,而不让它下流到View 类中去的方法:通过重载CWnd::OnCommand()函数!还介绍了一个非常典型的头文件包含编译错误的解决方法,LOWORD()宏的使用!以及如何在CMainFrame类的成员函数中调用CView类中的成员变量的问题-GetActiveView()的使用!<< 六:对话框编程 >>1:分清模态,非模态,系统模态对话框!2:在View类的cpp中响应菜单消息,利用已有的对话框类来产生对话框别忘了在该cpp中包含相应定义对话框类的头文件.3:制作非模态对话框要使用成员变量或者堆上分配的对象,不能在函数内定义一个局部变量来用.示例代码如下:dlg.Create(IDD_MYDIALOG,this);dlg.ShowWindow(SW_SHOW);4:注意一个模态对话框当点击其中的OK按钮时这个对话框是被Destroy了的,而对于一个非模态对话框而言,它只是隐藏了,而并未被Destroy,所以如果你定义了一个非模态对话框,并使用了原有的OK按钮,你一定要重载其基类CDialog 的OnOK()函数,在其中自己调用DestroyWindow().详见MSDN中CDialog::OnOK()函数的讲解页!5:静态文本控件的消息响应要注意一两点:一:要为静态文本控件指定具体的ID,而不能是默认的IDC_STATIC;二: 要在静态文本控件的属性页中的Styles页中将Notify前面的勾打上,才能实现消息响应.下面是一段静态文本控件的操作代码:(设置静态文本的内容);DWORD elapsedTime;elapsedTime=GetTickCount()/1000; //get secondsUINT hours=elapsedTime/3600;UINT minutes=elapsedTime%3600/60;UINT seconds=elapsedTime%3600%60;char times[100];sprintf(times,"系统已运行%d小时%d分钟%d秒!",hours,minutes,seconds); GetDlgItem(IDC_TIME)->SetWindowT ext(times);是在对话框类的成员函数中所做的.6:获取控件文本的几种方法:一:GetDlgItem()->GetWindowText();二:GetDlgItemText();三:GetDlgItemInt();这个函数取到控件文本并将之转换成int返回给调用者.这对如使用EDIT控件获取整数非常方便,并且它可以有效处理有符号数.这个函数的调用稍有复杂,查阅MSDN!四:将控件与成员变量相关联,比如要从EDIT控件获取整数,可以直接将EDIT控件与int类型的变量相关联,它会直接获取整数,并自动做相关的输入数据的类型检测,当然也可以关联CEdit型变量,再间接获取数据,用这种方法时要注意UpdateData()函数的适时调用!如果是CEdit 控件,可以用CEdit中的GetWindowT ext()函数获取其内容!五:通过发送WM_GETTEXT或者WM_SETTEXT消息的方式获取: 分为三种情况:(1)使用合全局的::SendMessage(),如下:::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd,WM_GET TEXT,10,(LPARAM)ch1 );//ch1是一个字符数组或者是:::SendMessage(m_edit1.m_hWnd,WM_GETTEXT,10,(LPARAM )ch1); //这是已关联控件变量的情况下(2)使用CWnd的SendMessage(),如下:GetDlgItem(IDC_EDIT1)->SendMessage(WM_GETTEXT,10,(L PARAM)ch1);或者是:m_edit1.SendMessage(WM_GETTEXT,10,(LPARAM)ch1);(3):使用CWnd::SendDlgItemMessage()函数,它实际上相当于先用GetDlgItem()得到控件句柄,再SendMessage(),是一种组合的方便方式!如:SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch1 );在讲这个的时候,还讲到了可以获得一个控件中选中的内容的消息-EM_GETSEL以及可以设置哪些内容被选中的EM_SETSEL消息.使用方法见MSDN.如: SendDlgItemMessage(IDC_EDIT1,EM_SETSEL,1,3);m_edit1.SetFocus();//这句的意义是因为如果该控件不是当前的FOCUS所在,那么即使显示了选中也会不可见.7:实现对话框收缩与扩展功能的代码示例:void CTestDlg::OnButton1(){// TODO: Add your control notification handler code here CString str;GetDlgItemText(IDC_BUTTON1,str);static CRect rectLardge;static CRect rectSmall;if(rectLardge.IsRectNull()){CRect rectSeparator;GetWindowRect(&rectLardge);GetDlgItem(IDC_SEPARATOR)->GetWindowRect(&rectSepar ator);rectSmall.top=rectLardge.top;rectSmall.left=rectLardge.left;rectSmall.right=rectLardge.right;rectSmall.bottom=rectSeparator.bottom;}if(str=="收缩<<"){SetDlgItemT ext(IDC_BUTTON1,"扩展>>");SetWindowPos(NULL,0,0,rectSmall.Width(),rectSmall.Height( ),SWP_NOMOVE | SWP_NOZORDER);}else{SetDlgItemT ext(IDC_BUTTON1,"收缩<<");SetWindowPos(NULL,0,0,rectLardge.Width(),rectLardge.Heig ht(),SWP_NOMOVE | SWP_NOZORDER);}}8:这节课最后讲了如何实现回车时让输入焦点在各个控件之间遍历的方法,其中涉及到了默认键消息响应函数的操作,以及如何用SetWindowLong()来改变一个控件的窗口响应函数的方法.<< 七:对话框编程之二 >>1:要改变对话框内控件上的文本字体,要在对话框的属性对话框中设置,而不是在单个对话框的属性中设置!2:逃跑按钮的制作放置两个外观一样的按钮,初始化为隐藏其中的一个,在对话框类中响应MouseMove消息,一旦检测到鼠标位于某个当前正在显示的按钮上时,就隐藏之,将另一个按钮显示出来.这样给用户的感觉就是只有一个按钮,在躲藏你的鼠标! 而录像中的做法是新建一个继承自CButton的类,然后将两个按钮分别与该新类的一个对象所关联,然后由这个类实现对MouseMove的响应,并在每个对象中放置一个指向本类的公有成员指针变量,指向另一个对象,这样,在响应函数中很方便地实现了ShowWindow(SW_HIDE),以及ShowWindow(SW_SHOW)操作.至于那两个对象中指向对方的指针的初始化可以放在控件所在对话框类的构造函数中,也可以该对话框类中加入一个对WM_INITDIALOG的消息的响应函数,将初始化放置于其中!3:往对话框或其它地方插入组合框(combo box)资源时,初始放置时一定要将它拖大一些,要不然放置好以后其高度无法修改,而且下拉出的列表框部分无法显示出来,只能重新加入一个!4:关于属性表单对话框的制作,一:要插入属性页,并做好控件布局;二:要为每个属性页关联相应的继承自CPropertyPage类的自定义类.三:要在类视图中点根工程图标右键,加入一个类型为MFC Class的新类,其基类为CPropertySheet.四:在该类中分别加入每个属性页类的一个对象.(注意头文件的包含)五:在该类的两个构造函数中都通过AddPage()函数加载每个属性页对象.六:在某个菜单项的响应函数中定义一个该类的对象(其参数为属性表单的标题),然后调用该类的成员函数:DoModal()创建一个模态的表单,或者Create()创建一个非模态的表单.(注意头文件的包含);5:关于向导对话框的制作:向导对话框实际是属性表单的一种变体,只要在属性表单的DoModal()调用生成属性表单之前加上一句:propsheet.SetWizardMode();生成的就是向导对话框而不是表单了.如何去掉默认生成向导对话框第一页中的上一步按钮:在该页的类中增加一个虚函数OnSetActive(),在其中加入如下语句:((CPropertySheet*)GetParent())->SetWizardButtons(PSWIZB _NEXT);即可.如何去掉默认生成向导对话框最后一页的下一步按钮,并且加上结束按钮: 同上理: 在相应的类中增加虚函数OnSetActive(),加入如下语句:((CPropertySheet*)GetParent())->SetWizardButtons(PSWIZB _BACK | PSWIZB_FINISH);注意:如果你在第一页中将上一步按钮去掉了,则后面每一页的上一步按钮都没有了(我觉得这样不太合理)所以你要用同样的方法设置每一个属性页,在其中设置合适的按钮!6:本次课程中提到了用C语言函数memset初始化数组的一种方法很好,如: memset(m_bLike,0,sizeof(m_bLike));<<八:界面修改,工具栏,状态栏,启动画面的制作>>1:如何修改单文档应用程序的窗口标题,查阅MSDC文章:Changing the styles of a window created by MFC.要在MainFrame的PrecreatWindow()中加入如下代码:cs.style&=~FWS_ADDTOTITLE;cs.lpszName="This is a test!";可以先不要上一句试一试!另一种方法是:cs.style=WS_OVERLAPPEDWINDOW;再进行修改,也可以不修改,那么是去掉默认文档标题,而只显示原程序标题!另一类方法是在窗口创建后再修改,因为在OnCreate中,开始的这些代码: if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;if (!m_wndT oolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC)|| !m_wndT oolBar.LoadToolBar(IDR_MAINFRAME)){TRACE0("Failed to create toolbar\n");return -1; // fail to create}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}// TODO: Delete these three lines if you don't want the toolbar to// be dockablem_wndT oolBar.EnableDocking(CBRS_ALIGN_ANY);EnableDocking(CBRS_ALIGN_ANY);DockControlBar(&m_wndT oolBar);完成了窗口创建,工具栏,状态栏的创建等工作,可以在后面利用一个系统全局函数SetWindowLong()函数进行修改:加入代码为:SetWindowLong(m_hWnd,GWL_STYLE,WS_OVERLAPPEDWIN DOW);与此相对,还有一个GetWindowLong()函数可供使用!如下面代码去掉了窗口上的最大化按钮:SetWindowLong(m_hWnd,GWL_STYLE,GetWindowLong(m_ hWnd,GWL_STYLE) &~MAXIMIZEBOX);当然SetWindowLont()还可以做别的修改.与SetWindowLong()相类似的另一个系统全局函数为SetClassLong();2:如何完成一个动画图标其实就是准备好几个图标,在定时器消息响应中更改图标即可完成.第一步是准备好几个(如三个)图标.第二步是在MainFrame类中做三个图标类的相关对象的成员变量,或者是一个大小为3的HICON数组.第三步是在MainFrame类的OnCreate()函数中LoadIcon()进行对三个图标的加载.其中用到的实例句柄的获取有三种方法: 一:用全局函数AfxGetInstanceHandle()获取,二:先在MainFrame文件中用extern声明一下全局对象theApp,然后使用theApp.hInstance;三:使用全局函数AfxGetApp()获取全局对象theApp对象的指针,然后用AfxGetApp()->hInstance;第二个参数是一个字符指针,可我们只有图标的资源ID,所以要进行必要的转换:用MAKEINTRESOURCE宏!第四步是设置定时器,也在OnCreate()函数中定义:SetTimer(1,1000,NULL);第五步是在MainFrame中添加WM_TIMER消息响应,在其中加入代码:static int index=0;SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcons[index] );index=++index%3;3:在工具栏上新加一个按钮,要让它与前一个按钮之间有一个分隔符,只需要将它轻轻向一旁拖动一点点再放开即可,而要删除工具栏上的一个按钮,你只是选中它再按DEL键是完不成的,它只是将按钮上的图案删除,所以删除一个按钮要将它拖动到工具栏之外,再松手!4:如何创建一个工具栏在MSDN 的关于CT oolBar的讲解页有详细说明!一:在CMainFrame中加入一个CT oolBar类对象的成员变量,二:插入工具栏资源,三:在CMainFrame的OnCreate()中加入:if (!m_MyToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY |CBRS_SIZE_DYNAMIC)|| !m_MyToolBar.LoadToolBar(IDR_MYTOOLBAR)){TRACE0("Failed to create toolbar\n");return -1; // fail to create}m_MyToolBar.EnableDocking(CBRS_ALIGN_ANY);DockControlBar(&m_MyToolBar);各个函数调用及参数传递查看MSDN!5:如何让一个工具栏隐藏或显示:if(m_MyT oolBar.IsWindowVisible()){m_MyToolBar.ShowWindow(SW_HIDE);}else{m_MyToolBar.ShowWindow(SW_SHOW);}但这样做的结果是工具栏虽说隐藏了,但是工具条还在,所以还要在后面加上一句:ReCalcLayout();这样做还是有问题,如果工具栏没有停靠在边上而是一个单独的小窗口,那么只做上面的工作只使得工具栏上的按钮不见了,而那个小窗口还在,所以,还要调用一个函数:DockControlBar(&m_MyToolBar);经过上面这句,小窗口也如愿消失了,但问题还有一点,就是当用户将工具栏放置为一个小窗口时,再点击菜单,要让这个工具栏显示出来,当然我们应该将工具栏仍按用户先前的小窗口样式显示出来比较好,可是这次工具栏又自动停靠在客户区顶部了?这个功能如何实现呢?孙老师只是提示可以查MSDN中CToolBar的成员函数解决这个问题,并没细讲,所以我看了MSDN,发现有两个函数:CToolBar::IsFloating()利用这个函数可以判断一个工具栏是否处于浮动状态,另一个是CFrameWnd::FloatControlBar()这个函数可以让一个控制栏处于浮动状态,然后我在CMainFrame中加入了一个BOOL型的成员变量,在每次判断工具栏是否可见时用来记录工具栏是否处于浮动状态,然后在重新生成工具栏时根据它的置决定是否将工具栏设为浮动状态,但是第二个函数好像不太好使,所以我又换用了SetWindowPos()成员函数,可是也不能将它放置为一个独立的小窗口.显示和隐藏工具栏的第二种方法:用一个函数:CFrameWnd::ShowControlBar(),因为这个函数的固有特性,上面是if...else...判断就可以简化为一句代码:ShowControlBar(&m_MyToolBar,!m_MyToolBar.IsWindowVis ible(),FALSE); 并且我惊讶的发现,用这个函数时,上面提到的浮动工具栏让它在恢复的时候仍回复为浮动的问题自动解决了!哈哈,好.6:状态栏相关编程因为MFC自动生成的系统已经包含了一个状态栏,所以我们暂时仅限于已有状态栏的修改,而不是另外生成一个状态栏.状态栏最左边的那一长条,就是经常显示一些提示字符串的那部分叫做提示行,而右侧那三个小窗口是用来指示CapsLock,ScrollLock,NumLock开关的状态,称为状态指示器.状态栏跟工具栏一样,也是在CMainFrame类中定义并在OnCreate()中创建的. 下面的代码在状态指示器的最左边放置了两个小窗口,并在第一个小窗口中放置了一个时钟:同样的CMainFrame的OnCreate()中,CTime tm=CTime::GetCurrentTime();CString strTime=tm.Format("%H:%M:%S");CClientDC dc(this);CSize sz=dc.GetTextExtent(strTime);m_wndStatusBar.SetPaneInfo(1,IDS_TIMER,SBPS_NORMAL,s z.cx);//调整窗口大小。
孙鑫的教学笔记

老孙的教学笔记(转)第一课1.MFC生成的C++源文件中都有StdAfx.h,此文件包含了常用的AFX函数的声明,其中有afxwin.h,此文件包含了CRECT,CPoint,CWnd等许多类及其方法的声明。
2.Project->Setting->Debug可以加入命令行参数。
3.在SDK中要加入"windows.h"和stdio.h。
因为LoadCursor,MessageBox等函数的声明在这个文件中。
4.创建一个完整的窗口的四个步骤SDK,1设计窗口类,2注册窗口类,3创建窗口,4显示窗口5.函数名可以代表函数代码的首地址,即可作为函数指针。
6.要查看VC数据类型,可以在MSDN中输入“BOOL”然后选择“DATA TYPE”。
7.atof将字符串转化为float,atoi将字符串转化为int型。
8.所有从CWnd类派生的类都有m_hWnd句柄。
9.变量的生命周期:可以认为出了包含它的大括号,这个变量的生命周期结束。
所以全局变量的声明位于所有大括号之外。
但是用new声明的变量和用static声明的变量除外。
10.SDK示范程序,见下面。
11.sprintf格式化字符,其头文件为stdio.h,在MFC中格式化字符用CString.Format12.GetDC()与ReleaseDC()要成对使用,否则会内存泄漏。
同样,BeginPaint()与EndPaint()。
13.GetStockObject()得到画笔、画刷、字体、调色板的句柄,使用时必须用类型转换。
14.什么时候用NULL,什么时候用0.答,对指针赋值时用NULL,对变量赋值时用0.15.什么是野指针?答:将指针指向的变量的内存释放后,此指针即变成野指针!如何避免野指针?答:将此指针指向NULL即可。
p=NULL;16.SDK代码流程:#i nclude "windows.h"//包含头文件LoadCursor,TextOut等函数#i nclude "stdio.h"//包含sprintf,printf等函数LRESULT CALLBACK MyProc(...);//声明回调函数int WINAPI WinMain(){WNDCLASS wndcls;//设计窗口类wndcls.hcursor=LoadCursor();//初始化....RegisterClass(&wndcls);//注册窗口类hwnd=CreateWindow(...);//创建窗口ShowWindow(..);//显示窗口UpdateWindow(..);MSG msg;//定义消息结构体while(GetMessage(...))//消息循环{...}return 0;}LRESULT CALLBACK MyProc(...)//实现回调函数{switch(uMsg){case WM_CHAR:break;...}}第2课1.定义结构体和类时别忘记在最后加入";"号!例如Class Point{int x;int y;};2.#i nclude <xxx.h>与#i nclude "xxx.h"的区别:<>不查找运行时目录,""查找运行时目录!3.类的定义中,如果未指明成员类型,则缺省为private.而结构体中则缺省为public.4.引用:引用经常用在函数的传参上。
孙鑫老师笔记第一课

Lesson1: Windows程序运行原理及程序编写流程窗口产生过程,句柄原理,消息队列,回调函数,窗口关闭与应用程序退出的工作关系,使用VC++的若干小技巧,stdcall与Lessonecl调用规范的比较,初学者常犯错误及注意事项。
1.Windows API与Win32 SDK操作系统提供了各种方便开发Windows应用程序的编程接口,所的函数都在Windows。
h头文件中声明。
Win32 SDK(Software Development Kit): 即Windows 32位平台下的软件开发包,包括API函数,帮助文档,微软提供的一些辅助开发工具。
2.窗口与句柄窗口是是屏幕上一块矩形区域,是Windows应用程序与用户进行交互的接口。
窗口分为客户区和非客户区。
在Windows应用程序中,窗口是通过窗口句柄(HWND)来标识的,要对某个窗口进行操作,首先就要得到这个窗口的句柄。
其它各种资源(窗口,图标,光标等),系统在创建这些资源时会为它们分配内在,并返回标识这些资源的标识号,即句柄。
-->光标句柄(HCURSOR),图标句柄(HICON)。
3.消息与消息队列Windows程序设计是一种基于消息的事件驱动方式的程序设计模式。
消息:在Windows中由结构体MSG来表示,typedef struct tagMSG{HWND hwnd;//消息所属的窗口句柄UINT message;//消息本身标识符,由一数值表示,系统对消息定//义为WM_XXX宏(WM为Windows Message缩写)WPARAM wParam; //随消息的不同附加信息也不同LPARAM lParam; //消息的附加参数DWORD time; //消息投递的时间POINT pt; //鼠标当前位置}消息队列:每当一个Windows应用程序创建后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序一的窗口的消息,消息产生后被投递到消息队列中,应用程序通过一个消息循环不断的消息队列中取出消息进行响应。
孙鑫笔记2

4, PreCreateWindow()是个虚函数,如果子类有则调用子类的。
5, CreateWindowEx()函数参数与CREATESTRUCT结构体成员完全一致,CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。
对于MFC程序,MainFrame,View,ToolBar,Controlbar等都是窗口,所以下面的窗口注册与创建、显示等要反复调用多次,一次对应一个窗口
(1) 注册窗口类
AfxEndDeferRegisterClass
(2) 创建窗口
CMainFrame::PreCreateWindow()//反复调用一次是给我们修改窗口属性的机会
一、C语言程序执行步骤
在C语言中,大约的步骤如下:
1, 全局变量内存分配 要是初始化)
打开一个MFC APPWizard(exe)工程,跟踪其执行步骤,可以发现,是以下顺序:
1) CXXApp中的全局变量定义
1) 设计一个窗口类
2) 注册窗口类
3) 创建窗口
4) 显示及更新窗口
5) 消息循环
补充2:其他需要注意的几点
1, 每一个MFC程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。在WIN32程序当中,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于MFC应用程序中,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。
6,注意两个函数。
孙鑫老师笔记18课

Lesson18 Active控件容器和服务器程序容器应用程序时可以嵌入或链接对象的应用程序。
Word就是容器应用程序。
服务器应用程序是创建对象并且当对象被双击时,可以被启动的应用程序。
Excel 就是服务器应用程序。
ActiveX控件不能独立运行,它必须被嵌入容器应用程序中,和容器应用程序一起运行。
Dispatch maps调度映射,主要是MFC提供让外部应用程序可以访问控件的属性和方法Event maps事件映射,控件向包含它的容器发送事件通知接口是外部程序和控件进行通信的协议,可以把接口看作是函数的集合,外部程序通过借口提供的方法,去访问控件的属性和方法。
接口中所定义的所有函数都是纯虚函数regsvr32 ...注册控件 regsvr32 /u....卸载控件STDAPI DllRegisterServer(void)将控件信息写入注册表中STDAPI DllUnregisterServer(void)卸载注册信息。
制作一个时间控件,在void CClockCtrl::OnDraw(CDC* pdc, const CRect& rcBounds,const CRect& rcInvalid)中添加以下代码:CBrush brush(TranslateColor(GetBackColor()));pdc->FillRect(rcBounds,&brush);pdc->SetBkMode(TRANSPARENT);pdc->SetTextColor(TranslateColor(GetForeColor()));//为控件设置属性,必须在MFC ClassWizared中为控件添加属性,上面几 //行代码才有用CTime time=CTime::GetCurrentTime();CString str=time.Format("%H : %M : %S");pdc->TextOut(0,0,str);这样就能做出一个静态的时间控件,如果我们想使控件实时显示时间,需要添加两个消息响应函数 WM_CREATE,WM_TIMER.代码:int CClockCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct){if (COleControl::OnCreate(lpCreateStruct) == -1)return -1;// TODO: 在此添加您专用的创建代码SetTimer(1,1000,NULL);return 0;}void CClockCtrl::OnTimer(UINT nIDEvent){// TODO: 在此添加消息处理程序代码和/或调用默认值Invalidate(); //立即引起窗口重绘//也可以使用InvalidateControl(); //强制窗口重绘,效果相同COleControl::OnTimer(nIDEvent);}要修改控件的背景色前景色和字体颜色在OnDraw中添加CBrush brush(TranslateColor(GetBackColor()));pdc->FillRect(rcBounds,&brush);pdc->SetBkMode(TRANSPARENT);pdc->SetTextColor(TranslateColor(GetForeColor()));ActiveX控件的四种属性Stock:为每个控件提供的标准属性,如字体或颜色。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
XML名称空间一个XML文档中可以包含许多元素与属性,当我们使用其他人的XML文档,或者在文档中使用多个DTD文件时,就有可能碰到名称相同的元素,而这些名称相同的元素可能代表了完全不同的含义。
比如:title可以用于表示标题,也可以永远表示某个人的头衔;table 可以用于表示表格,也可以用于表示桌子。
当这些具有相同名称不同含义的元素混合到一个文档中时,就会造成理解和处理上的混乱。
为了解决这个问题,就要用到W3C发布的另一个推荐标准——XML名称空间。
/TR/REC-xml-names声明名称空间名称空间通过使用一系列的保留属性来声明,这种属性的名字必须是以xmlns或以xmlns:作为前缀。
与其他任何XML属性一样,这些属性可以直接或以缺省的方式给出。
声明有两种形式:第一种:<元素名xmlns:prefixname=”URI”>元素名是指你在哪一个元素上声明名称空间,在这个元素上声明的名称空间适用于声明它的元素和属性,以及该元素内容中的所有元素及其属性。
xmlns:prefixname作为该元素的属性名,属性的值是一个URI引用,是表示该名称空间的名称空间名字。
其中prefixname 给出名称空间前缀的名字,该前缀用于将元素及属性的名字与URI关联在一起。
要注意,在这样的声明中,名称空间的名字不能为空(“”)。
如果有两个URI,其组成字符完全相同,可以认为它们标识了同一个名称空间。
来自于XML名称空间的名字可以作为限定名(qualified names)出现,限定名包含了一个以冒号(:)分隔的名称空间前缀和一个本地部分(local part)。
映射到URI引用的名称空间前缀选择了一个名称空间。
<hr xmlns:hr=”/hr”><hr:employee>名称空间前缀可以是不包含冒号的任何合法的xml名称。
在声明名称空间时,有两个前缀是不允许使用的,它们是xml和xmlns。
xml前缀只能用于XML1.0规范中定义的xml:space和xml:lang属性,前缀xml被定义为与名称空间名字/XML/1998/namespace绑定。
前缀xmlns仅仅用于声明名称空间的绑定,它被定义为与名称空间名字/2000/xmlns绑定。
名称空间推荐标准中指出,在名称空间声明中,使用相对URI引用已经被废弃了(不赞成使用)。
第二种:<元素名xmlns =”URI”>这种声明形式没有给出名称空间的前缀名,URI所标识的是缺省的名称空间。
在这样的缺省声明中,属性值可以为空(“”)。
例子:<hr xmlns=”/hr”>表示声明了一个缺省的名称空间,hr元素及其内容中所有的没有前缀的元素都属于/hr所标识的名称空间,除非被hr元素内容中其他的缺省名称空间声明所覆盖。
在声明名称空间时,选择的URI不需要指向时间的内容,在URI所标识的位置上,可以不存在任何东西。
在名称空间声明中的URI,只是形式上的标识符,其唯一目的是提供一个唯一的名字。
名称空间在元素和属性中的运用元素中运用:<?xml version=”1.0” encoding=”gb2312”><books xmlns:people=”/people”><book><titile>jsp深入编程</title><author><people:name>张三</people:name><people:title>作家</people:title></author></book><book><titile>xml入门</title><author><people:name>李四</people:name><people:title>教师</people:title></author></book></books>进一步:<?xml version=”1.0” encoding=”gb2312”><bks:books xmlns:people=”/people”xmlns:bks=”/books”><bks:book><bks:titile>jsp深入编程<bks:title><bks:author><people:name>张三</people:name><people:title>作家</people:title></bks:author></bks:book><bks:book><bks:titile>xml入门</bks:title><bks:author><people:name>李四</people:name><people:title>教师</people:title></bks:author></bks:book></bks:books>或<?xml version=”1.0” encoding=”gb2312”><bks:books xmlns:bks=”/books”><bks:book><bks:titile>jsp深入编程<bks:title><bks:author xmlns:people=”/people”> <people:name>张三</people:name><people:title>作家</people:title></bks:author></bks:book><bks:book><bks:titile>xml入门</bks:title><bks:author xmlns:people=”/people”><people:name>李四</people:name><people:title>教师</people:title></bks:author></bks:book></bks:books>缺省的名称空间:在具有很多元素(这些元素都在同一个名称空间中)的文档中,给每个元素名称都添加一个前缀将是很繁琐的事情。
为此,可以使用没有前缀名的xmlns属性将缺省的名称空间附加给元素及其子元素,元素本身及其子元素都被认为是在缺省的名称空间中,除非它们有明确的前缀。
<?xml version=”1.0” encoding=”gb2312”><books xmlns =”/books”><book><titile>jsp深入编程<title><author xmlns:people=”/people”><people:name>张三</people:name><people:title>作家</people:title></author></book><book><titile>xml入门</title><author xmlns:people=”/people”><people:name>李四</people:name><people:title>教师</people:title></author></book></books>缺省的名称空间声明中的URI可以设为空字符串,这样的话,在它的声明范围内,没有前缀的元素将被认为不存在任何的名称空间中,这和没有声明缺省名称空间是一样的。
<?xml version=”1.0” encoding=”gb2312”><books xmlns =”/books”><book><titile>jsp深入编程<title><author xmlns=””><name>张三</name><title>作家</title></author></book><book><titile>xml入门</title><author xmlns=””><name>李四</name><title>教师</title></author></book></books>属性中运用:<?xml version=”1.0” encoding=”gb2312”><books xmlns:people=”/people”xmlns =”/books”><book><titile>jsp深入编程<title><author people:id_card=”CH-100-100”><name>张三</name><title>作家</title></author></book><book><titile>xml入门</title><author id_card=”CH-100-200”>//此次属于哪个空间?<name>李四</name><title>教师</title></author></book></books>一个属性想要在某个名称空间中,必须给改属性加上名称空间的前缀,没有前缀的属性不在任何的名称空间中(包括缺省的名称空间)。
即使拥有属性的元素在某个名称空间中,没有前缀的属性仍然不在该名称空间或任何其他的名称空间中。