为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

MFC 对话框

2011-09-25 30页 doc 112KB 36阅读

用户头像

is_199877

暂无简介

举报
MFC 对话框孙鑫MFC教程笔记(对话框)2007-08-11 20:04 参考文献:左飞,万晋森,刘航. visual C++ 数字图像处理开发入门与编程实践. 北京:电子工业出版社,2008. (索书号:TP312C/1797) 孙鑫MFC教程笔记(对话框)2007-08-11 20:04 对话框 如何建立对话框 与 相应的程序相关联: 在MFC中,对资源的操作都是建立相关的类来完成的; 所以我门先NEW 一个 对话框资源 然后建立他的类与相应的应用程序相关联就可以; 步骤: 1 NEW 一个对话框资源 2 在资源上选择 Classwa...
MFC 对话框
孙鑫MFC笔记(对话框)2007-08-11 20:04 参考文献:左飞,万晋森,刘航. visual C++ 数字图像处理开发入门与编程实践. 北京:电子工业出版社,2008. (索号:TP312C/1797) 孙鑫MFC教程笔记(对话框)2007-08-11 20:04 对话框 如何建立对话框 与 相应的程序相关联: 在MFC中,对资源的操作都是建立相关的类来完成的; 所以我门先NEW 一个 对话框资源 然后建立他的类与相应的应用程序相关联就可以; 步骤: 1 NEW 一个对话框资源 2 在资源上选择 Classwarrzd上创建一个新的类 (或在资源上双击左键) 3 起名字 !(注意文件名 将会去掉C 例如 我门的名字叫 CDaiDlg那么文件名是 DaiDlg); 对话框的俩种类型: 1 模态对话框(特性:当建立模态对话框时,将不允许用户点击对话框以外的控件后应用程序本身,也就是说当模态对话框建立时 应用程序停止运行 而只运行对话框,所以模态对话框是可以使用 局部变量的定义方法的! ) 模态对话框是比较方便的一种对话框~建立函数: int CDialog::DoMadol() //注意他是CDialog类成员 也就是说想使用先建立一个 CDialog 对象吧 如果我门想要在View类中 建立一个 CDialog对象不要忘了 要在View的Cpp文件中包含 CDialog.h 2 非模态对话框(特性与模态的相反) 使用成员函数 BOOL CDialog::Create( UINT nIDTemplate, CWnd* pParentWnd = NULL ); nIDTemplate 为对话框的ID号 pParentWnd 拥有这个对话框的 对象指针,如果 是NULL 为默认的主应用窗口 也就是 Frame应用窗口 注意如果我门在使用Create创建 非模态对话框时 要调用一个函数来 显示 这个对话框 CWnd::ShowWindow(SW_SHOW);不仅这样 非模态对话框时 不能是局部变量! 完整代码: CDialog dlg; dlg.Create(ID,this); dlg.ShowWindow(SW_SHOW): 对ShowWindow的发现: ShowWindow是CWnd的成员函数 是改变窗口的显示属性(包括Hide Show等) ShowWindow这一函数是改变属性 同样在创建一个窗口时(窗口包括很多,CButton也是一个窗口!)可以直接给他赋予属性 WS_VISIBLE (可见的)等属性 也可以解决改变窗口之后 更新窗口的问题 显示出新窗口的问题~! 对于俩种对话框的销毁; 模态对话框中OnOK的成员函数中有对模态对话框的 EndDialog();来销毁 而非模态的对话框时 OnOK不能销毁对话框 而我门需要对OnOK函数进行覆盖 调用DestryWindow; 做一个对话框的小程序,在对话框上动态增加一个按钮: 在这要强调一下 动态的增加某种资源 是需要把 资源对象 声明成类的成员 这样才可以(不然被析构了 建立了也就没有效果了) 而资源对象是对象 资源是资源 只有吧资源和对象联系起来 这个的东西才有实际的意义,我门才能对他进行操作, 而把资源和对象联系起来有俩个函数 LoadMenu 和 CreateMenu (当然有些类是不存在 Load函数的 例:Button类 ) Load函数 是将一个已经被我门在 资源视图中创建好的 东西 与对象联系 而Create函数是直接创立一个新的东西 (而直接创立时我门还需要给他一些这样东西的属性 所以使用Create函数时 总是件另人讨厌的事情~) 更重要的事情是:一个对象只能与一个资源相联系 如果你想把这个已与资源联系的对象与另一个资源联系起来 那必须先调用DestoryWindow()这个资源 然后在做新的联系; 静态文本框的操作: 刚刚建立的静态文本框 是不可以做消息响应的(一般也不需要这么去做) 而我门想要去用静态文本框的消息 就要改边文本框的 ID (因为一般不给文本框添加 消息响应 文本框的的ID都是一样的) 并且要把属性->样式(Styles)->通告(Notify)打上对号!! 几个有用的函数: 获得窗口的文本 CWnd::int GetWindowText(CString &str); CWnd::int GetWindowText( LPTSTR lpszStringBuf, int nMaxCount ) const; 参数str是放置 文本的对象! lpszStringBuf是一个字符串的指针,nMaxCount 到大的最大字符数 同样使用 SetWindowText(CString str) SetWindowText(LPCTSTR lpszString); 来设置文本 获得对话框上控件的指针(控件包括了Button,静态文本等等!) CWnd::CWnd* GetDlgItem( nID ) 使用ID号来获取控件的指针!从而进行操作! 从编辑框得到数据的几种操作 1 使用GetDlgItem( nID )->GetWindowText( )然后把字符转换为我门所要的东西(使用 atoi,itoa 的转换函数) 这几个函数所要注意的问题 使用函数前 一定要确定这个函数是对谁操作! 对谁操作要获取谁的指针!! GetWindowText(LPTSTR lpszStringBuf, int nMaxCount )这个函数 参数lpszStringBuf所要存储的指针 nMaxCount 是最大得到多少个字符~ 使用代码例子: char str[10]; GetDlgItem( nID )->GetWindowText(str,10 );//注意第一个参数使用的是指针 aoti itoa 函数介绍:(介绍前的感想,做ACM时 看到程序就要将程序分成一个一个功能函数,然后将功能函数做好,从来没有想过使用他人做好的函数,而现在学习了MFC这套教程,养成的第一个习惯就是寻找自己想要的函数 在MSDN中,然后再自己编写 算是一种偷懒的行为吧 ,估计在以后的编程中,也要养成 先寻找和想要的类模型差不多的类 然后在通过继承派生来得到想要的类模型,这可能就是代码重用的本质吧 ~ MS就是一大堆会偷懒的编程员 ~呵呵) double atof( const char *string ); int atoi( const char *string ); __int64 _atoi64( const char *string ); long atol( const char *string ); 这四个函数不用多说了,要注意的就是参数的问题,不要看参数名字叫 string 就是一个CString 成员 而是 char*(指针) 也就是数组的名字 返回型是相应转化的数据类型 ;(这是一个不错的函数) 不过要注意他是需要包含头文件 Routine Required Header Compatibility atof and ANSI, Win 95, Win NT atoi ANSI, Win 95,Win NT _atoi64 Win 95, Win NT atol ANSI, Win 95, Win NT 还可以反过来 itoa, ftoa等等做一个介绍 char *_itoa( int value, char *string, int radix ); value要转换的数 char *string 一个buffer 接受 转换后的字符串 int radix 十进制 啊 二进制 ...== (还有这里的 atoi 的 a 是ASCII的 A 这就解释了 为什么不是ctoi~) 2 使用函数GetDlgItemText( nID,LP... ),SetDlgItemText(nID,LP...) (这函数不仅长的象 GetDlgItem 和 GetWindowText 的混合体 功能更是相同...又个偷懒的杰作!) int GetDlgItemText( int nID, LPTSTR lpStr, int nMaxCount ) const; int GetDlgItemText( int nID, CString& rString ) const; void SetDlgItemText( int nID, LPCTSTR lpszString ); 使用也是一样的! 比那个能方便点~! 3 又一个混合体函数 GetDlgItemInt();; SetDlgItemInt()在2的基础上 来atoi都不用了! UINT CWnd:: GetDlgItemInt( int nID, BOOL* lpTrans = NULL, BOOL bSigned = TRUE ) const; nID ID号 lpTrans 如果NULL 那么不会给出错误的提示 bSigned 是否是有符号的数字(就是是否去转化符号“-”) TRUE就是有符号啦!! void SetDlgItemInt( int nID, UINT nValue, BOOL bSigned = TRUE ); nValue 是你想要在DlgItem输出的数字; 4 使用资源来关联变量的方式来控制数据交换(这才是重点,因为他不只提供了对数据错误的提示功能还增加了 控件与资源的联系 当然我门需要了解框架所带给我门的东西;) 在控件上使用ClassWarrzd 选择第2个选项卡!(Member Variables) 对相应的 ID 号进行资源与数据的联系: 观察一下ClassWarrzd给我门在代码里家了些什么: 1 在头文件中 加了 // Dialog Data //{{AFX_DATA(CTextDlg) enum { IDD = IDD_DIALOG1 }; int m_num1; int m_num2; int m_num3; //}}AFX_DATA 这是数据交换的宏 2 在构造函数里 给这几个 成员变量初始化: //{{AFX_DATA_INIT(CTextDlg) m_num1 = 0; m_num2 = 0; m_num3 = 0; //}}AFX_DATA_INIT 3 在void CTextDlg::DoDataExchange(CDataExchange* pDX)函数中增加了 数据交换声明 { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CTextDlg) DDX_Text(pDX, IDC_EDIT1, m_num1); DDX_Text(pDX, IDC_EDIT2, m_num2); DDX_Text(pDX, IDC_EDIT3, m_num3); //}}AFX_DATA_MAP } 对与 void CTextDlg::DoDataExchange(CDataExchange* pDX) 的使用 用来数据交换!! 这个函数是被框架调用,用来交换 和 验证 数据的 函数!这个函数从来不被直接掉用 而是通过调用 UpdateData()函数来调用这个函数 来交换和验证数据的(这句的意思是 DoDataExchange 接口是不对我门开放的 我门想要掉用 DoDataExchange这个函数 必须用UpdateData()来间接调用) BOOL UpdateData( BOOL bSaveAndValidate = TRUE );介绍: 参数问题:bSaveAndValidate=TRUE 时 数据开始交换(也就是说我门对该变量的操作是生效的) 而当他是 FALSE 时 数据不再交换 而是初始化这个函数(也就是把这个变量的操作失效 不过可以在屏幕上进行显示!)通过改变这个参数的值 我门可以控制 数据的交换 或是 数据的显示~这是很重要的;!错误的数字使得我门的操作成为无效;~ 还要说的一点是当创建的是模态对话框时(DoModal)时 系统会自动把这个参数变成 FALSE 也就是说我门初始时是不能改变变量的 例子代码: UpdateData(); m_num3=m_num1+m_num2; UpdateData(FALSE); //我门也可以 让 控件 关联一个 控件变量 然后在调用他的成员函数进行操作(教程里有讲 不过觉得有些多余,也许在其他类型的程序中 可能要有用,注意这种方法ClassWarrzd的使用方法) 函数 SendMessage( );(注意是SendMessage 而不是SetMessage)是一个发送消息的函数 使用....不详,查查SDK API什么的吧~ 功能很强大 static LRESULT SendMessage( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) 关于SendMessage()函数的说明 发送一个响应的消息message,然后用 wParam,lParam来接受我门想要的东西 (当然这个要取决于我门发的是什么消息 现在已知的消息是:WM_SETTEXT,WM_GETTEXT去MSDN 看看) 而 SendMessage 在API 和 MFC 中都有 要注意是谁调用他的原始问题; SendMessage()的变种 (向对话框中的子控件发送消息): LRESULT SendDlgItemMessage( int nID, UINT message, WPARAM wParam = 0, LPARAM lParam = 0 ); 用法基本相同 对于获取编辑框复选的操作: 向编辑框发送一个 EM_GETSEL (同样也是 EM_SETSEL) 要使用SendMessage()来发送 //还要注意的是 我门已经把复选设置好了有的时候也是看不见效果的,因为我门在操作时 点击了按钮或者其他的操作 使得焦点没有在 我门所设置那个Item上 所以我门是看不见结果的 我门还要增加一个操作 是用函数改变焦点:CWnd* CWnd::SetFocus( );没有参数 返回值是以前那个带有焦点的指针; (对与复选最常用的是复选所有的东西,这样我门要把 SendMessage 的 wParam =0,lParam =-1) 总结: 怎么样改变对话框的大小用来增加新的功能? 对话框的矩形坐标 可以利用一个 图形控件 和 SetWindowPos 来改变 先要知道对话框的矩形坐标 通过控件得到(GetWindowRect())矩形坐标 ...这样我门就 能知道 需要改变什么矩形坐标了 void GetWindowRect( LPRECT lpRect ) const; 函数 参数 lpRect 用来储存 矩形坐标 BOOL SetWindowPos( const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags ); 本来是用来改变Window Z-order 的(什么是 Z-order简单说就是显示窗口的顺序) 在 79 710 俩课中使用的SDK 函数 不再介绍(因为不明白!!) 使用default按钮的特性来增加对话框对 回车(0x0d)按键响应的操作 我门用一个设置输入焦点随回车变化的例子来说明; 因为在重载OnOK函数时 OnOK还是会调用基类的 OnOK函数(OnOK函数会自动关闭对话框) 而我门不希望我门在输入回车时关闭对话框 所以我门将它转化为注释! 然后使用函数获取当前焦点的句并 Wnd* GetFocus( )来选择GetNextWindow(UINT nFlag = GW_HWNDNEXT ) 然后在使用SetFocus( ); 完整代码 :(在Dialog中的 OnOK 函数中) GetFocus()->GetNextWindow()->SetFocus(); 这个代码逻辑上是可以的,不过执行起来会发生错误,如果一直回车下去 回造成程序崩溃;(哪有那么都 GetNext呀?)我门要使用Tab功能的函数来完成这个功能 就不会出错了~ CWnd* GetNextDlgTabItem( CWnd* pWndCtl, BOOL bPrevious = FALSE ) const; pWndCtl开始时的窗口 bPrevious = FALSE 向下走; 如何查看 Tab 顺序:布局(layout)-> Tab顺序(Tab Order) OK按钮不管存在不存在 都会执行 OnOK函数 他的ID 号 是IDOK(不是ID_OK哦 ) 孙鑫MFC教程笔记(菜单.对话框)2007-08-11 20:03对话框 先要对消息的传送进行分析: 消息分为三类 消息,命令消息,通告消息; 标准消息:是除WM_COMMAND以外的所有以WM_开头的消息(WM_Char,WM_Move等) 命令消息:WM_COMMAND 包括了 来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。 通告消息:由控件产生的消息,例如,按钮的单击,列框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现 菜单名词说明: 一个菜单拦可以有若干个子菜单,一个子菜单又可以有若干个菜单项等。对菜单栏的子菜单由左至右建立从0开始的索引。对特定子菜单的菜单项由上至下建立了从0开始的索引。访问子菜单和菜单项均可以通过其索引或标识(如果有标识的话)进行。 菜单只有一个! 子菜单 ! 菜单项 !索引都是从0开始 //菜单的操作是在 框架类(Frame) 进行的 : 定位菜单的方法及有关函数: 第一步获得菜单: CWnd::CMenu* GetMenu( ) const; //指针 第二步获得子菜单: CMenu::CMenu* GetSubMenu( int nPos ) const; 参数 nPos 是子菜单的位置(索引)从0开始; 第三步对菜单项操作~ 可以一起使用 GetMenu()->GetSubMenu(0)->? 菜单的特殊操作 1 给菜单前打对号 (标记菜单项) 函数 UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck ); nIDCheckItem 是与 nCheck 相关的参数取值 nCheck 是 MF_BYCOMMAND or MF_BYPOSITION 和 MF_CHECKED or MF_UNCHECKED 的和 2 给菜单前添位图 (标记菜单的功能等) 函数 BOOL SetMenuItemBitmaps( UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked ); nPosition 与 nFlags 相关 nFlags 为 MF_BYCOMMAND 或 MF_BYPOSITION 注意CBitmap 是资源定义的应该是类变量 而不是 局部变量 CBitmap 13*13 才能符合标记大小 技巧:给出一个求 系统中定义大小的函数 int GetSystemMetrics(int nIndex ) nIndex 为系统的ID 查询MSDN (记得 GetTextMetrics 是文本信息 字体的平均宽度和高度的求法) CString :: Format("%d,%d")格式处理内容到 str字串中 和 C中的printf();用法差不多~! 3 标明菜单可用不可用(变灰+是否真的可点击来发送命令消息) 使用函数CMenu::UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable ); 同样参数 nIDEnableItem 与参数 nEnable 有关 nEnable MF_BYCOMMAND or MF_BYPOSITION 与 MF_DISABLIE or MF_ENABLE 也可以变为灰色 MF_GRAYED 需要注意的是 想让手动对菜单项进行操作 需要在(Frame)构造函数中将一个值( m_bAutoMenuEnable ) 初始化为 FALSE ! m_bAutoMenuEnable 提供了MFC自动识别是否菜单项可用(通过检测是否为MFC添加了 消息响应函数)!! 4 加载新的菜单 或 更换新的菜单 有关函数 SetMenu(CMenu *menu);返回值为 修改之前的菜单 ;注意是指针!! 忽忽 使用SetMenu(NULL); 就可以取消菜单哦~ 不要忘了 Menu 也是资源 需要定义在类成员中 不然要出错的!!! 使用Menu.LoadMenu( IDNo. ); MFC的菜单消息更新机制~(可以通过此来修改菜单是否可用等) 菜单的运行是一个快速循环的过程,依赖于CN_UPDATE_COMMAND_UI消息 谁捕获了CN_UPDATE_COMMAND_UI消息 MFC将会创建一个对象 CCmdUI (UI是用户接口的英文缩写 User-I****记不住了) CCmdUI 先与子菜单索引位 置0,菜单项索引位置0开始 循环一次向下推进一次(当然CPU的速度很快 几乎就是一瞬间的事情)而我门通过 对CN_UPDATE_COMMAND_UI消息的捕获 来控制菜单的属性; 实现的方法:通过手工或 Classwizard 来添加一个命令ID的响应 (不要使用原来用的COMMAND而使用UPDATE_COMMAND_UI) 利用所添加的函数 传进来的参数 pCmdUI(是指针)来进行属性的操作! //工具栏图标的索引记数顺序是:从做至右从0开始,分隔符也算索引号 //当工具栏与菜单ID一样时 就可以实现其快捷方式(点击后产生相同的消息映射) 提示 可以去看看 pCmdUI 中的成员! 增加右键弹出菜单的功能(俩种方法) 1 通过编译器来添加右键菜单 点击VC6.0的Project()->Add to Project(增加到工程)->Components and Controls..... 观察它为我门增加了什么进入代码:A,一个菜单资源;B,在派生View类中增加OnContextMenu()函数 (至于怎么对右键进行消息扑获 未研究清楚) 所以我门使用对右键的消息响应 在其中增加菜单就可以手动来加入菜单! 2 手动来加入右键POP菜单! 先添加一个对右键响应的消息处理 OnRButtonDown函数 在OnRButtonDown函数内 例子代码: CMenu menu; menu.LoadMenu(IDR_MENU1); CMenu *pPopup=menu.GetSubMenu(0); pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); 对函数 TrackPopupMenu()说明: CMenu::BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL ); 参数: nFlags 是指一个右键的菜单的弹出方式(包括他与鼠标的位置等,习惯参数为TPM_LEFTALIGN | TPM_RIGHTBUTTON 意思是 在鼠标右边 向下弹出!) x,y 是坐标点 (注意是 屏幕的坐标点而不是客户区的坐标 需要使用 void ClientToScreen( LPPOINT lpPoint ) const来转换成客户区! CWnd* pWnd 是指这个菜单的拥有者 使用 this 就可以 lpRect 是点击多大区域有效 (NULL) 对ClientToScreen()分析 是 CWnd 的成员函数 是将 现在的Client 坐标 转化为 Screen 坐标! 而 TrackPopupmenu()是使用 Screen 坐标的! 对 Popup 菜单的响应顺序 View -> Frame 技巧: 得到父窗口指针 使用函数 Cwnd::CWnd* GetParent( ) const; 有关于资源声明成类成员的的另一种发现:: 之所以声明资源为类成员 是因为当他是局部的变量时 在执行完有关函数 后,会被析构(释放空间) 而发现了一个大部分资源类都有的函数 Detach() 可以把资源与相关的句柄断开 使他不会发生析构 动态添加菜单等资源时,必须这 么使用 而不是声明资源为类的成员~;(那就不叫动态了!!) 菜单的动态操作: 有关函数: 在菜单尾部添加 CMenu::BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL ); CMenu::BOOL AppendMenu( UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp ); nFlags 是所要添加菜单的种类 nIDNewItem 将会根据 nFlags 的变化而变化 这是一个很复杂的函数 在教程里我门使用这个函数: nFlags 为一个Popup的弹出菜单 时 nIDNewItem 是弹出菜单的句柄 menu.m_hMenu 但是参数要求是一个 UINT 类型 我门需要强制转换一下(原理不祥!) CMenu::CreatePopupMenu() 创建一个空的弹出菜单 与 菜单资源相关连~ 在原有菜单后添加弹出(Popup)菜单的完整代码: CMenu menu; //先要创建一个新的资源菜单 然后使用这个资源加载在原来菜单的后面 menu.CreatePopupMenu(); GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"daigong"); // 这里因为是添加Popup菜单 // 当然指获取 菜单的指针就OK // 而不是子菜单 menu.Detach(); //使用了Detach 不让资源析构 在菜单间插入新的菜单: 函数:CMenu::BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL ); nFlags决定了nPosition 的取值(命令or索引号)也控制了插入的形式 AppendMenu()也好 InsertMenu()也好 都需要为新的菜单项给定一个 ID号 以便用来以后的消息响应! 还有就是对什么形式的菜单操作就获取什么菜单的对象或指针 (例:增加Popup菜单需要 GetMenu() 而 增加子菜单中的菜单项则需要 GetMenu()->GetSubMenu( nPos )来调用函数) 删除菜单 函数 CMenu::BOOL DeleteMenu( UINT nPosition, UINT nFlags ); 同上 不用在说了吧 T_T 手动添加消息响应函数(重要哦 可以不依赖编译器的方法!!!) 1 先要定义命令的ID 在FileView里头文件 Resource.h 中添加 例:#define IDM_DAIGONG 117 如果原来有ID 那么 就不需要添加了; 2 找到相应的头文件(通常是Frame类中因为对是对菜单进行消息响应么!) 找到(一般会在头文件的最后) protected: //{{AFX_MSG(CMainFrame) afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG DECLARE_MESSAGE_MAP() }; 来添加消息函数原形:afx_msg void OnDaigong(); 代码变为: protected: //{{AFX_MSG(CMainFrame) afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG afx_msg void OnDaigong(); //后来增加的代码 DECLARE_MESSAGE_MAP() }; 3 添加消息映射! 在代码区(.cpp文件)里 找到:(通常是在前面) BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code ! ON_WM_CREATE() //}}AFX_MSG_MAP END_MESSAGE_MAP() 添加形式: ON_COMMAND(ID号,命令响应函数名) 这里要说明很多问题!! 第1 第二个参数上命令响应函数的名字 是没有()的 第2 这个声明是结尾没有任何标点符号的(别以为我门写!!) 代码: BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code ! ON_WM_CREATE() //}}AFX_MSG_MAP ON_COMMAND(ID_EDIT_COPY,OnEDIT_COPY) //后来添加的! 可要没有标点哦! END_MESSAGE_MAP() 4 在CPP文件里写函数体 void CMainFrame::OnEDIT_COPY() { MessageBox("OK"); } 技巧:如果忘记了怎么写消息映射 可以使用ClassWzard 添加一个消息响应函数 用此来引导我门去写 消息响应 不过要注意一点!!::在CPP文件中消息映射放到宏外(注意一定要放到宏外面,因为CLASSWIZARD发现菜单删除了,同时要把其宏对里的消息映射也删除掉的)! 菜单实践: 要求制作一个 通讯录 在View处 添加人名 电话 并也空格为标实符隔开 例:“daigong 123456” 在菜单后动态添加一个菜单 Popup“通讯录” 拉开菜单 显示 daigong 点击后 在View上显示 人名和 电话号~ 注意: 我门的输入是在View中完成的 也就是说我门需要在View 中添加对 WM_CHAR 消息的OnChar函数对 其响应 而我门在OnChar函数体中对菜单栏的操作要注意是谁调用的函数 (在View中添加函数当然是View 的菜单栏操作),而我门都知道 View类中不存在 菜单 (只有Frame中才存在菜单嘛~)所以我门如果直接 添加函数操作不会发生 ,而需要使用函数 GetParent()来获取View的父窗口 (Frame)然后在进行操作 , 上面只是问题之一,我门以前的操作都是在 OnCreate中进行的 而现在我门把操作放在了 OnChar函数中 有很大的不同 由名字我门可以知道 OnCreate 函数是在窗口建立时我门所做的操作,也就是我门的操作 直接会在窗口上造成影响,改变窗口,而我门现在在OnChar中对其操作 ,操作结束后,不会看到反映( 这是因为Window程序的工作原理所制,Window程序采用窗口重绘 因为改变菜单后 窗口没有进行重绘我门是 看不到变化的 ,这里我门使用菜单栏重绘函数(应该会有全部的重绘函数 而我门在这里不使用的原因是怕 是在View的上的字符显示 消息 造成不必要的麻烦)CWnd::DrawMenuBar() (想一想这个函数应该是谁调用 就象我门在刚才所说的 如果直接使用这个函数,那么意味着我门是在用View类来调用这个函数,而View是不存在菜单的! 所以我门还是要使用函数 GetParent()来找出Frame指针 用它来调用DrawMenuBar():) 总之是一句话 在使用函数前 一定要清楚的判断下 究竟是谁调用函数才会用效果~~从而来控制我门想要的结果~! 忽忽 全部重绘函数入手! void CWnd:: Invalidate( BOOL bErase = TRUE ); 重绘窗口 这个函数原理是向系统发出一个WM_PAINT消息 那么系统将会调用一次 OnPaint()函数 来重新画客户区域~ 当bErase为 TRUE 时 什么都不保留 而 FALSE 时保留 介绍几个数组集合类 CStringArray,CStringArray,CDWordArray,CPtrArray,CStringArray,CUIntArray,CWordArray (Array的意思 对列 数组 ) CStringArray为详细介绍; 因为这几个类都是表示C 或 C++里的数组的概念(或者说是数据队列吧) 所以成员函数和用法都是一样的 MSDN 也没逐个给出每一个集合类解释(偷懒啊~) 这样我门会使用一 个类 那么都会了(在学C时 我门会了 int a[5];难道还要问 char a[5];怎么用吗 呵呵~) int CStringArray::Add( LPCTSTR newElement ); 增加一个元素到数组 CString CStringArray::GetAt( int nIndex ) const; 通过索引号来得到一个数组元素;nIndex索引 编写这个实践程序体会一:我门会在MFC(或是在其他面向对象语言中)使用很多的函数,而我门在使用函数时 通常不会去想,是谁调用这个函数 而这个函数又是对谁进行操作(MS是面向过程编程所带来的思想),虽然我 到现在还没有理解MFC的彻底架构思想 和 运行原理,不过在使用函数 问一下自己这个函数是哪个类的成员 会对谁进行操作(好比我门刚学习编程时会问自己 这个函数的参数是什么一样重要!) 分析消息映射的路由(路由....就是消息和数据所走的路线) 我门在前面使用MessageBox()函数来验证过了 消息处理时的路由: Frame->View 框架类先给视窗类处理 View 如果不能处理 那么 在个文档类处理 最后再回到 Frame(框架类处理) 如果我门想要直接在 框架类中对消息进行响应怎么办: 因为virtual BOOL Cwnd::OnCommand( WPARAM wParam, LPARAM lParam );(就是进行消息处理的那个函数) 是一个虚函数 也就是说我门在派生类中 覆盖(就是在写一个OnCommand函数的话)我门就可以让他直接在框架 类中对我门的命令进行响应(好象也可以改变他的消息路由 人为方式 可是没被证明!) 我门在Frame类中添加 虚函数,OnCommand( WPARAM wParam, LPARAM lParam ); 通过MSDN我门知道 第一个参数 的第一字节数 储存了 命令ID 这里有俩个函数 : WORD LOWORD( DWORD dwValue ); WORD HIWORD( DWORD dwValue );这俩个函数分别是取 低字节序 和 高字节序; (教程里叫这俩个函数为宏....郁闷中 ;他门和函数有什么区别么~?) 我门可以通过 解析 wParam 成 命令ID 来判断我门想要的操作 教程中所给出的一个东西:只细想一下,我门通过对函数所操作的对象判断 有时候需要父窗口指针; 这时我门会使用函数 GetParent() 得到父窗口~ 当我门在父窗口时使用子窗口的数据时我门该怎么办? 使用 GetChild() ? 我去MSDN 中查找了一下 发现只有一个这个函数,还是API的 也就是说 MFC 没有把 这个函数封装在任何一个类中, 那么我门就没有办法来获取子类的指针吗? NO!教程中给出了一个在Frame 中获取 View的指针的办法 GetActiveView();查看一下 MSDN 我发现还有GetActiveFrame();~明白了怎么使用~ 如果想要不让消息传递下去 一顶要return TRUE ; MS是如果 有TRUE就不向下传递 这就解释了为什么 只要View响应了 Frame就不响应了(MessageBox 那个例子) 孙鑫MFC教程笔记(文本处理)2007-08-11 20:02插入符的创建 void CreatSoildCaret(int 宽度,int 高度); 需要在 OnCreate消息中构造 算做一种资源吧~ 在 View 中 添加对 OnCreate 消息的响应函数 WM_CREATE 在其中构造 初始化为隐藏 需要调用 void ShowCaret(); void ShowCaret() 显示插入符 void HideCaret() 隐藏插入符 文本的信息(宽度 高度等) CDC::GetTextMetrics //是CDC的成员函数!! BOOL GetTextMetrics(LPTEXTMETRICS lpmetrics);//LP是指针的意思! 对 TEXTMETRIC 结构体的内容: typedef struct tagTEXTMETRIC { /* tm */ int tmHeight; //字体的高度 int tmAscent; //基线以上 int tmDescent; //基线以下 int tmInternalLeading; int tmExternalLeading; int tmAveCharWidth;//字体平均宽度 int tmMaxCharWidth; int tmWeight; BYTE tmItalic; BYTE tmUnderlined; BYTE tmStruckOut; BYTE tmFirstChar; BYTE tmLastChar; BYTE tmDefaultChar; BYTE tmBreakChar; BYTE tmPitchAndFamily; BYTE tmCharSet; int tmOverhang; int tmDigitizedAspectX; int tmDigitizedAspectY; } TEXTMETRIC; 一般使用参数 CreateSoildCaret(tm.tmAveCharWidth/8,tm.tmHeight) 完整代码 CClientDC dc(this); //注意 要使用当前的this指针 dc是 要 // 和当前对象有关系的! TEXTMETRIC tm; dc.GetTextMetrics(&tm)//参数是取地址的(要求为指针么!) CreateSolidCaret(tm.AveCharWidth/8,tm.tmHeight); 插入Bitmap 的插入符 使用 CreateCaret(CBitmap *pbitmap) 首先要构造个 Bitmap bitmap;(因为是资源 一定是全局变量或是类成员) 使用 BitMap 的成员函数 BOOL LoadBitmap( LPCTSTR lpszResourceName ); BOOL LoadBitmap( UINT nIDResource );//ID号!! 代码; bitmap.LoadBitmap(IDB_BITMAP1); CreateCaret(&bitmap); ShowCaret(); 有关于 Ondraw函数 窗口从无到有 后 大小改变时被 系统会发送WM_PAIN消息自动调用; 要填加对WM_PAIN的响应! (要是想要把文字一直保存在窗口上 要在 Ondraw函数上操作) 文本的输入 调用 CDC中的Textout成员函数 即可; BOOL TextOut( int x, int y, const CString& str ); 代码: CString str; str="******"; CDC dc; dc.TextOut(50,50,str); 有关插入符的移动(根据鼠标点击) 方法很简单 添加一个 对 WM_LBUTTENDOWN 的消息响应函数 保留点坐标 使用 成员函数 static void PASCAL SetCaretPos( POINT point ); 对插入符坐标设置; 注意移动后要清空str 和改变 point 对于删除的处理(删除的ASCII码"0x08") 原理是先将原来的字符用背景色显示 然后删除一个字符 在用正常颜色输出; 有关的函数: GetBkColor( ) 得到背景色 COLORREF GetBkColor( ) const; virtual COLORREF SetTextColor( COLORREF crColor ); 设置输出文字颜色 它的返回值是替换之前的 属性(颜色) 需要保留时保留 在此例中就需要保留 再说以下删除字符时注意的事情 因为CString 只对加法的几个运算符重载了 没有 减法所以我门不能象增加字符那样处理 减少字符;而是使用一个函数 CString Left( int nCount ) const; //是 CString的成员函数(需要使用CString来调用) nCount 从左边数前 nCount 个字符; 而我门想知道一个CString中有多少字符 使用函数 int GetLength( ) const; /*是 CString 的成员函数哦*/ 这就可以获得长度 -1 就是删除后长度 返回值是一个CString 保留了这些字符 有了这个函数 我门就可以使用如下操作: Str=Str.Left(Str.GetLength()-1); 现实删除的代码: CString str("daigong"); CClientDC dc; dc.TextOut(Point.x,Point.y,str);//首次输出 if(0x08==nchar) //nchar为消息响应函数传进来的参数 为输入的符号 { COLORREF cr; //用于保存原来的颜色 cr=dc.SetTextColor( dc.GetBkColor( ) ); dc.TextOut(Point.x,Point.y,str); str=str.left(str.Length()-1); dc.SetTextColor( cr ); dc.TextOut(Point.x,Point.y,str); } 对于回车键的处理(回车的ASCII"0x0d") 在OnChar消息响应函数里表达; 原理是 当判断输入为 0x0d 时 插入符移动到下一行的原始位置 str 清空 Point变化新的位置用来开始输出 (这里的Point变化要说一点 输出字符是从 Point点开始直接使用字符输入的所以Point并不是插入符所在的点 //插入符所 在的点用GetCarePos()来求 呵呵)。 str清空要使用到 CString 类中的成员函数 void Empty( ) 调用表达str.Empty(); Point移动就比较复杂,想知道所要移动的点位,先要知道字符的高度,想象一下移动 的原理!(y=y+字符的高度)而求字符的高度我门在创建插入符时已经知道了(字符的 高度是一种字体信息的而不是字符串信息,也就是说我门在CDC中会得到高度字符,而 不是在CString中得到,忽忽)CDC::BOOL GetTextMetrics( LPTEXTMETRIC lpMetrics ); 代码表达: TEXTMETRIC tm; dc.GetTextMetrics(&tm); if(0x0d==nchar) { str.Empty(); Point.y=point.y+tm.tmHeight } 明天在写关于插入符随着字符移动 忽忽 (关 CSize 与 TEXTMETRIC的 区别!!!) 使用CSize 和 Textmetrics 的不同情况 CSize 与 Textmetrics 是俩个不同的概念 CSize 是指 一个屏幕的显示区域 typedef struct tagSIZE { int cx; int cy; } SIZE;//也就是定义一个区域的大小 而TextMetrics 是指DC中 文字的显示的信息 (高度 平均宽度等) SIZE是返回一个坐标! CSize 可以通过 CDC::GetTextExtent(CString str) 来得到~ 插入符的随输入字符移动 插入符的移动是一个比较经典的判断是计算屏幕坐标 还是 字符传长度 使用文字信息TextMetrics 还是使用 CSize (当然是使用计算屏幕坐标和CSize区域) 有关的函数是:CDC::CSize GetTextExtent( LPCTSTR lpszString, int nCount ) const; CSize GetTextExtent( const CString& str ) const; 这个函数是CDC的成员函数 得所求字符串在该CD 下的屏幕显示大小 返回值为 CSize; 代码: CPoint CaretPoint=0; CaretPoint=NewPoint; CaretPoint+=dc.GetTextExtent(str).cx; SetCaretPos(CaretPoint); 有关于字体的选择 介绍一个类 CFont 类 派生于 CGdiObject 字体 定义后 初始化函数为 BOOL CreatePointFont( int nPointSize, LPCTSTR lpszFaceName, CDC* pDC = NULL ); nPointSize 字体高度 10:1 比例输入 见MSDN lpszFaceName 字体的名字; 把字体选进设备描述表(GDI)函数: dc.SelectObject(Font *font); 然后在使用 dc.TextOut(int x,int y,CString str); 就是新的字体了 画刷 画笔等 也都是这样被选入的! 字体颜色的变换 有关函数 COLORREF RGB( 0~255,0~255,0~255)三原色改变颜色 virtual CDC::COLORREF SetTextColor( COLORREF crColor ); 改变字体 返回值为原来颜色~ 举例 字体 随时间变化 (类似于卡拉OK歌词的显示) 我门所知道的CDC::TextOut(CString &str)是将字符一次输出 不能达到平滑输出 的效果; 而函数: virtual int DrawText( LPCTSTR lpszString, int nCount, LPRECT lpRect, UINT nFormat ); int Dr
/
本文档为【MFC 对话框】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索