1. 主要内容
从本节开始介绍windows开发实现记事本程序的逻辑实现部分。本节的主要内容有以下3点:
1. 主窗口定义 —— 主要介绍记事本主界面窗口对应的窗口类及实现方案
2. RichEdit控件的选用及初始化 —— 记事本程序中编辑控件的选择及使用
3. 整个程序ICON的选择。—— 程序ICON设置
2. 实际开发
2.1 主窗口实现
在上一篇介绍界面的实现中只是给出了运行界面的效果,但是当时那个界面程序不能响应任何的windows消息,因为当时的窗口在创建时将窗口对应的过程处理函数设置为NULL。现在,我们需要将相应的过程处理函数添加上使得这个记事本应用程序可以响应我们发出的一系类操作指令。为此,本文在开发时,单独设计了一个用于保存主界面窗口的类CMainWnd。这个类定义了整个窗口的过程处理函数Main_Porc。在Main_Proc中可以对传入的任何消息进行处理(包括初始化窗口消息,窗口中其他控件的消息,关闭窗口消息等等)。以windows 自带记事本为例,如图1所示
图1 windows主窗口消息效应区域
如上图所示,在windows记事本主界面中,需要响应红色矩形区域内的菜单控件的各类消息、响应黄色矩形区域内系统按钮的相关消息,以及相应编辑控件Edit中的消息。对于主窗口中的各类控件的消息,windows会以WM_COMMAND消息进行传输,这也是整个程序的核心处理区域。系统按钮关闭的消息则是WM_CLOSE。窗口初始化消息WM_INITDIALOG则是构建对话框窗口前发出的初始化消息。为了能够响应上述各类消息,需要在CMainWnd中添加对于这几类消息的响应函数,因此整个CMainWnd的基本实现形式如下:
头文件声明:
/************************************************************************/ /* file : CMainWnd.h * author : Huagang Li * date : 2014-8-30 15:29:42 * blogs : http://www.cnblogs.com/lhglihuagang/ * tips : 主窗口实现类, 实现窗口的过程函数,消息响应函数等 */ /************************************************************************/ #ifndef _MAIN_WND_H #define _MAIN_WND_H #include <Windows.h> ////////////////////////////////////////////////////////////////////////// // CMainWnd 主窗口类,提供 class CMainWnd { public: static BOOL WINAPI Main_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static BOOL Main_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam); static void Main_OnCommand(HWND hWnd, int id, HWND hWndCtl, LPARAM lParam); static void Main_OnClose(HWND hWnd); private: static HWND hMainWnd; // 主窗口句柄 }; #endif
CMainWnd具体定义:
#include "MainWnd.h" include <WindowsX.h> ////////////////////////////////////////////////////////////////////////// // static data members HWND CMainWnd::hMainWnd = NULL; ////////////////////////////////////////////////////////////////////////// // static function members // 主窗口的过程函数,根据消息类型处理各类消息 BOOL WINAPI CMainWnd::Main_Proc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch (uMsg) { HANDLE_MSG(hWnd, WM_INITDIALOG, Main_OnInitDialog); HANDLE_MSG(hWnd, WM_COMMAND, Main_OnCommand); HANDLE_MSG(hWnd, WM_CLOSE, Main_OnClose); } </span><span style="color: #0000ff">return</span><span style="color: #000000"> FALSE; } BOOL CMainWnd::Main_OnInitDialog( HWND hWnd, HWND hWndFocus, LPARAM lParam ) { return TRUE; } // id为具体空间的ID号,可以在resource中定义有意义的控件ID,如“打开文件”可以设置 // 为ID_FILE_OPEN void CMainWnd::Main_OnCommand( HWND hWnd, int id, HWND hWndCtl, LPARAM lParam ) { switch (id) { // } } void CMainWnd::Main_OnClose( HWND hWnd ) { ::EndDialog(hWnd, NULL); }
在定义了CMainWnd后,在main函数处的DialogBox处添加主窗口的过程处理函数
::DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, CMainWnd::Main_Proc);
完成上述步骤运行后,就可以看到启动后的主界面可以响应窗口上系统按钮“关闭”,但是对于菜单控件的消息,因为消息响应此还是函数中数什么都没有做,因此还是不会进行任何处理。
2.2 RichEdit控件的选用
对于一个记事本程序来说,主界面上核心区域还是编辑区域。但是当前记事本程序中还没有选择任何编辑控件。通过观察现有windows控件列表可以看出,适合编辑控件的有Edit Control以及Rich Edit2.0 Control。对于这两种编辑控件,Edit Control较为简单,但是响应的功能也较少。Rich Edit2.0 Control实现起来较为复杂,但是对应的功能也多了不少(例如可以改变字体颜色,字号等等)。本文希望能够实现一个功能较强的记事本,因此选择了Rich Edit2.0 Control进行后续开发。插入了Rich Edit2.0 Control后,主界面窗口对应的资源视图如图2所示:
图2 IDD_MAIN中插入Rich Edit2.0 Control
上述步骤运行后,本以为可以看到带有编辑界面的记事本程序,可是实际上程序运行后没有任何效果,甚至主界面都不能正常启动了。百度后发现,对于richedit启动失败的方法都是针对MFC程序来说的,需要添加初始化函数AfxInitRichEdit2。但是现在使用windows API 开发,并没有AfxInitRichEdit2这个函数,只能另寻他路了。终于在一篇博文中http://blog.csdn.net/dijkstar/article/details/7953816提到,原来上面那个初始化函数中主要是加载RichEdit依赖的dll,那么整个问题就豁然开朗了,我们只需要在主窗口启动前手动的载入这个dll就可以了。因此在主函数的DialogBox前添加了依据载入dll的操作如下:
::LoadLibrary(T("riched20.dll"));
MAIN的properties中:
此时再运行程序时,可以正常启动记事本了,且能够在richedit中进行编辑,效果如图3所示:
图3 手动加载Riched20.dll后出现主界面窗口
在启动主界面后,可以正常进行编辑。貌似这个控件可以正常工作了。但在实际测试时,发现了以下几个问题:
1. 界面运行后RichEdit边框棱角过于分明
处理方法: Richedit控件的properties -> Boarder –> Flase
2.输入Enter 不能换行(手动输入时一直在同一行编辑)
处理方法: Richedit控件的properties -> Multiline–> True
Richedit控件的properties -> Want Return–> True
3. 没有滚动条(横向以及纵向的)
这个在主界面属性上,IDD
处理方法: IDD_MAIN-> properties -> Horizontal Scrollbar–> True
IDD_MAIN –> properties -> Vertical Scrollbar–> True
4. 不能随窗口大小伸缩
在对窗口进行伸缩时,RichEdit控件的大小还是保持原来的大小,如图4所示:
图4 主界面大小变化时RichEdit控件大小不变
这个问题其实很好理解,因为伸缩主界面窗口时,windows将发送WM_SIZE消息通知窗口。这个过程类似于windows对主界面窗口说“hi, 你的大小已经变了,你根据改变后的大小变一下”。现在我们的主窗口过程处理函数中并没有针对WM_SIZE消息对RichEdit进行特殊处理,因此主界面下面的RichEdit一直保持自己原来的大小,才会出现上面的情况。那么具体的解决方案为:在InitDialog中添加RichEdit大小自适应功能,同时针对WM_SIZE消息,添加Main_OnSize函数来处理这种独立的控件。具体的代码实现如下:
void CMainWnd::Main_OnSize( HWND hWnd, UINT state, int cx, int cy ) { RECT stRect; ::GetClientRect(hWnd, &stRect); // 获取窗口客户区大小 </span><span style="color: #008000">//</span><span style="color: #008000"> 将RichEdit大小调整为客户区大小</span> ::MoveWindow(::GetDlgItem(hWnd, IDC_RICHEDIT), stRect.left, stRect.top, stRect.right-stRect.left, stRect.bottom-stRect.top, TRUE); }
这里只是在CMainWnd中添加了对于WM_SIZE的消息响应函数,要让RichEdit响应这个消息,还需要在Main_Proc中添加相应过程
HANDLE_MSG(hWnd, WM_SIZE, Main_OnSize);