热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

windows开发记事本程序纪实(二)逻辑篇1

从本节开始介绍windows开发实现记事本程序的逻辑实现部分。本节的主要内容有以下3点:1.主窗口定义2.RichEdit控件的选用及初始化3.整个程序ICON的选择

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 
//////////////////////////////////////////////////////////////////////////
// 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 
//////////////////////////////////////////////////////////////////////////
// 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);
 }
return 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); // 获取窗口客户区大小
// 将RichEdit大小调整为客户区大小
 ::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);

这样,RichEdit也就可以跟着主窗口大小自由伸缩了。上述过程处理函数中,主要调用了三个基本的API接口。

1. GetClientRect,这个API用于获取客户区大小,RichEdit伸缩的大小就是这个大小值

2. GetDlgItem 获取窗口下某一个控件的句柄,例如GetDlgItem(hWnd, IDC_RICHEDIT),就可以获得主窗口下RichEdit控件对应的句柄。

3. MoveWindow。它的第一个参数就是需要进行位置大小变化的窗口句柄。我们这里将2中的RichEdit句柄传入,后面的参数分别是矩形区域的left点,top点,width值以及height值。最后一个参数用来表示大小改变后要不要重绘窗口。注意,这里选择了TRUE。如果选择FALSE,会出现以下这种情况:当将窗口变小后,在进行变大操作,RichEdit并没有立即适应变大后的区域,还是保留在原来变小的区域。效果如图5所示:

图5 MoveWindows中参数repaint设置为FALSE后潜在问题

基于此,我们在用MoveWindow改变窗口大小时,最好使得repaint为TRUE,保证实时改变。

2.3 主程序ICON设计

      在上述截图中可以看出,主界面的左上角ICON一直是windows自带的ICON。为了与windows自带记事本做到类似,直接到网上找了一个类似的JPG图标转为ICO,然后设置了程序的ICON。具体的图标设置方法请参考http://www.cnblogs.com/lhglihuagang/p/3927283.html

      在图标设置后,可以运行程序查看下最新的效果,如图6所示


图6 设置程序图标后的结果

      最后,对话框窗口的标题Dialog实在显得有些另类,这里根据windows自带记事本的标题“无标题 - 记事本”,将这个值进行了相应的修改,具体为IDD_MAIN-> properties  -> Caption –> 无标题 - 记事本最后,对话框.

3. 运行结果

在添加了CMainWnd以及RichEdit后,整个程序的运行后效果如下图7所示:


图7 本节程序改动后的效果

4. 结论

    1. 使用RichEdit控件时,需要手动加载riched20.dll,否则程序运行后没有任何界面效果

      2. RichEdit换行、滚动条、边框都可以通过properties中相应字段进行设置

      3. 需要在CMainWnd中添加WM_SIZE消息响应函数,保证RichEdit自由伸缩。

5. 参考链接

      [1] http://blog.csdn.net/dijkstar/article/details/7953816

    [2] http://www.cnblogs.com/lhglihuagang/p/3927283.html

    [3] http://msdn.microsoft.com/en-us/library/ms633534(VS.85).aspx

6. 说明

     这将是一个系列博文,后面会继续补充逻辑功能的开发的步骤。希望能与更多博友交流。 如果你觉得这篇文章还可以,请点赞,哈哈~~
      声明:未作说明,则本文为年糕原创。      注意:转载须保留全文,如需修改请 联系作者。


推荐阅读
  • 本文介绍了互联网思维中的三个段子,涵盖了餐饮行业、淘品牌和创业企业的案例。通过这些案例,探讨了互联网思维的九大分类和十九条法则。其中包括雕爷牛腩餐厅的成功经验,三只松鼠淘品牌的包装策略以及一家创业企业的销售额增长情况。这些案例展示了互联网思维在不同领域的应用和成功之道。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • Skywalking系列博客1安装单机版 Skywalking的快速安装方法
    本文介绍了如何快速安装单机版的Skywalking,包括下载、环境需求和端口检查等步骤。同时提供了百度盘下载地址和查询端口是否被占用的命令。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文介绍了求解gcdexgcd斐蜀定理的迭代法和递归法,并解释了exgcd的概念和应用。exgcd是指对于不完全为0的非负整数a和b,gcd(a,b)表示a和b的最大公约数,必然存在整数对x和y,使得gcd(a,b)=ax+by。此外,本文还给出了相应的代码示例。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • HTML5网页模板怎么加百度统计?
    本文介绍了如何在HTML5网页模板中加入百度统计,并对模板文件、css样式表、js插件库等内容进行了说明。同时还解答了关于HTML5网页模板的使用方法、表单提交、域名和空间的问题,并介绍了如何使用Visual Studio 2010创建HTML5模板。此外,还提到了使用Jquery编写美好的HTML5前端框架模板的方法,以及制作企业HTML5网站模板和支持HTML5的CMS。 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
author-avatar
小帅
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有