Delphi练习之简单实现记事本功能

Posted ganders

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Delphi练习之简单实现记事本功能相关的知识,希望对你有一定的参考价值。

      主要是使用Delphi创建一个窗口程序,实现的功能就是记事本最基本的打开和保存功能,其实是很简单就能实现的,因为原理很简单,前提是控件的属性方法要知道是哪一个,但是RAD官方帮助文档做的实在是太烂,粗略不说有的甚至都直接不写,所以中间遇到了很多问题,在此总结一下。

      实现的过程就分为两部分:界面布局Unit内编码.

界面布局,我们先按需求设计出合适的界面,下图是我设计的界面(其实就是仿记事本),具体步骤下面讲;

技术图片

如图所示,我向默认生成的 Form1窗体 中拖入了五个组件:

    1)Standard中的 TMainMenu,用于创建菜单;

我们对该控件进行编辑(右键->Menu Designer)然后在弹出的窗口中设置想要的选项(具体的就是:右键->Insert,然后修改 Caption、ShortCut、Name属性),如下图:

技术图片技术图片

注意:例如图中显示的“文件(F)”,需要将该控件的 Caption 改为“文件(&F)”,只有加了&,才能正确的显示,而且程序运行时默认快捷键是“Alt + F”(如果你将&后的字母设为A,默认快捷键就是“Alt + A”,以此类推)。当然,程序运行时字母下是不会有下划线的。

这里关于作者的属性CheckedBox属性是False,也就是没有打对勾,如图;

技术图片

    2)Standard中的 TMemo,用于文本区域;

这里主要修改 Align(布局属性,我想让编辑区填充整个窗口,设置如图所示),BorderStyle(TMemo区域边界属性,默认是单线条的边界,我不想要边界线,设置如图所示),Font(就是 TMemo内文本字体啥的,你也可以选择不改),ScollBasrs(编辑区的滚动条,我设置的是垂直滚动条,如下图)

技术图片技术图片

    3)Win32中的 TStatusBar,窗体底端的状态栏;

这个是记事本底端的状态条,可以分栏,不过我没有芬兰而且在里面输入的内容是“by Ganders”(因为比较自恋),不过文本内容的排列方式是从右向左,即 BiDiMode属性 设置为 bdRightToLeft;如下图

技术图片技术图片技术图片

    4)Dialogs中的 TOpenDialog,点击“打开”选项时弹出的窗体;

我们双击这个控件的时候,会弹出下图,这个控件是当我们单击菜单中的“打开”选项时调用的,它的作用只是单纯的弹出这个窗口而已;

技术图片

这里面我们要改动的地方是 Filter属性,就是文本过滤器,例如我们想要显示当前目录下的文本文档,即需要把过滤器设置为txt后缀的,如下图;

技术图片技术图片

    5)Dialogs中的 TSaveDialog,点击“另存为”选项时打开的窗体;

这个设置跟上面的一样(反正我写的是一样的),不过需要提醒的是:这个是我们点击“另存为”选项时弹出,而我们点击“保存”选项时,也不过是先进行判断然后决定是否调用“另存为”选项,而这个判断的原理就是确定是否保存过一次了,如果保存过一次,就直接覆盖了。

提醒:控件包括可视和不可视控件,区别就是在运行是能否看到,TMainMenu、TOpenDialog和TSaveDialog都是不可视控件(我们当然也希望它不可视)。

界面这部分就差不多了,然后我们要在下面填写各功能的代码了。

Unit内编码:

我先贴出各功能块之外的代码截图,后面再详解:

下图是自动生成的类成员还有各个功能的过程(注意:我说的是过程procedure;Pascal中例程包括过程procedure和函数function,区别就是函数有返回值,而过程没有;C语言中是利用函数前是否为void来判别是否有返回值,而且在C语言中只有函数这一种);

技术图片

下图中,resourcestring后声明的内容相当于const类型的字符串(反正我是这样理解的),var后的是cFileName(字符串变量,用于盛装文件名字,后面要统一调用它)和WhetherSaved(布尔型变量,判断文本是否被保存过一次);

技术图片



我们先写另存为功能(因为它不会调用其它功能);

我们双击”另存为“选项或者双击 OnClick事件(两者是一样的,效果都是程序运行时,单击该选项是做出相应反应);

技术图片或者技术图片

我们完成该操作时,会在Unit内自动生成框架代码,然后我们进行完善(我直接贴图吧,代码我也不知道咋调颜色);

技术图片

我声明了三个局部变量,其中两个Integer类型的变量(I用来存储文件名长度,J用于for计数和数组索引功能),一个Char型数组(该数组4个长度,是用来存储文件名后4个字符的);

这里用了with..do句型,因为begin和end之间的很多method是属于SaveDialog1的,例如FileName其实是SaveDialog1.FileName,利用with句型就是声明SaveDialog1的作用域,避免写的繁复。

在SaveDialog1.Execute()之前先对SaveDialog1.FileName赋值,即在弹出保存窗口前就要赋一个默认的名字,否则什么都不会显示,如图;

技术图片


mmo1.Lines.SaveToFile(FileName,TEncoding.UTF-8);
这一句是真正的保存操作,就是将mmo1中的内容以UTF-8编码方式写入以FileName命名的文件;事实上这个FileName是包含路径的,就是在保存窗口中选择的文件夹路径(我其实对这个很迷惑);

然后我们看打开窗口语句和保存操作之间的赋值、循环和if语句,我调用了ExtractFileName()这个函数处理FileName,使返还的是文本名字的字符串,而不包含路径(关于这个函数我看了帮助文档,但失望的是,并没有进行解释,而我选择用这个函数是因为其他前辈的博客中用到了这个,我就试了试,行得通,唉,RAD真的很让人失望);

当然,这里需要注意的就是SaveToFile()这个Method是个重载函数,可以只写第一个参数,不过可能会编码格式不统一,造成乱码的情况。


I := Length(ExtractFileName(FileName));
    for J := 0 to 3 do begin
      s[J] := ExtractFileName(FileName)[I-3+J];
    end;
    {judge FileName}
    if s[0] = ‘.‘ then
      if s[1] in [‘t‘,‘T‘] then
        if s[2] in [‘x‘,‘X‘] then
          if s[3] in [‘t‘,‘T‘] then begin
            {do nothing}
          end
          else begin
            FileName := FileName + ‘.txt‘;
          end;
    {judge FileName}

然后调用 Length()返还该字符串的长度值存储在局部变量I中;接着用了一个for循环语句,将文本名字的后4位字符复制到数组s中;通过判断名字的后4位是否为“.txt”,来决定是否添加后缀名。

ok,现在我来解释为什么要怎么做以及需要注意的事项。因为在保存窗口弹出来以后,我们可以选择是否使用默认名称,也可以选择重新命名;当我们重新命名时,一般都是写个名字,而不会主动去添加后缀“.txt”,可是这样会造成保存的文档是没有后缀名的,我们肯定不想这样,所以才会有语句“ FileName := FileName + ‘.txt‘; ”,但是这样又会有一个问题,就是它会给所有的名称后都加上后缀,如果我们重新命名时主动键入了后缀,那保存的文本就会有两个后缀了,这也是我们不想要的;所以我选择添加一个判断,当没有主动写后缀或者写的后缀不是“.txt”时给文件名后面添加后缀(当然,这里主动键入的后缀是不区分大小写的,例如主动键入了“.Txt”,也是符合的,是不会再被添加“.txt”后缀的,因为“.TXT”、“.tXt”、“txT”等后缀都是被识别为文本文档的)。

这里主要有两点需要注意:1)字符串后直接跟中括号,中括号内的数字为字符串内元素的索引,索引值是从1开始的,而不是从0开始;例如,S := ‘abcdef’ ,则S[1]是 a ,S[6]是 f ;(之所以可以不像C语言那样,我想是因为,如果C语言中数组索引从1开始时,那数组的声明方式和末尾字符的表达方式是混淆的,而Pascal中数组的声明方式和C语言不同);2)if语句中的判断条件,可以看到,我用了4次判断,而这实际上只需要把判断条件写一起,判断一次就行了,可是Pascal语言好像并不支持这种多个判断条件的操作(我也不太懂是为啥,如果你知道详细情况请务必告诉我 ,一次写一个判断条件实在是有点扯淡)。


然后下一句,

cFileName := ExtractFileName(FileName);

Caption := Format(‘%s - %s‘,[cFileName,sSuffix]);

使用cFileName这个字符串变量(变量就是盒子)来存储文件名字符串,然后Caption引用该变量,因为我需要将主窗体的标题修改成保存的文件的文件名,就是下图所示区域:

技术图片

这里我使用了Format()函数(它也是个重载函数,第三个参数好像是跟线程安全相关的,我们暂时不考虑),该函数会将第二个参数中的字符串或者数字以第一个参数的格式重新组合并且将组合后的字符串返回,也就是说此时 Caption 为 cFileName – sSuffix 字符串;


最后一句;

WhetherSaved := True;

这个变量表示文件是否被保存过一次,是个全局变量,菜单中的”保存“选项需要用它作为判断条件,下面会用到;

保存功能;

,如法炮制,我们完善保存功能的代码,如图;

技术图片

首先,我们判断文件是否被保存过一次,也就是判断全局变量 WhetherSaved是否为True(当然,这个变量在主窗口被创建的时候被我初始化为False了,后面会写到);

如果从未被保存过,那么就相当于点击了”另存为“选项;

如果被保存过,就会覆盖原文档,至于为什么又给主窗体的Caption再次赋值,等会儿再说。

关于作者功能;

然后是关于作者功能,如图;

技术图片

当我们点击菜单中的”关于作者“选项时,该选项前的复选框会打上对勾(这个我在设计窗体界面的时候设置为了False),而底端的状态栏也会显示(这个我在主窗体界面创建函数里初始化为False,你也可以像复选框一样,在设计窗体的时候就设置为False),再点击一次,复选框内就没有对勾了,状态栏也不会显示,如图;

技术图片 技术图片

打开功能;

下面是打开功能;(中间有空隙是因为一张图截不完,是两张拼接的)

技术图片

技术图片

点击”打开“选项的原理就是:先判断文本是否被修改过,如果没被修改过(刚打开程序时和刚保存完时);如果被修改了,弹出对话框,提示”文本被修改,是否保存?“,如果点击”是“,就交给保存功能处理,保存功能处理后,进行打开操作;如果点击”否”,直接进行打开操作;如果点击“取消”,什么都不做。

我在这里使用局部变量 I 来作为判断条件,ID_CANCLE对应数字2,只要在初始化赋值不给 I 赋值为 2就行(我赋值的是1);然后用 I 存储消息框的返回值;只有 I 为ID_CANCLE 时,才会什么都不做。

然后是弹出打开窗口,然后我们判断是否选中了文件,因为不指定文件就打开的话会有错误警报,所以我们要确定一下;然后成功打开文件之后,WhetherSaved也是要赋值为True,因为打开的文件肯定是已经存在的,也就是保存过一次的。

标题提示功能;

下面是一个简单的窗体标题提示功能(文本被编辑后如果没保存,标题前就会出现 *);

技术图片

当文本内的内容被修改时,主窗体的标题前就会出现一个”*“,如下图是文本未被修改和被修改时的标题;

技术图片  技术图片

点击保存后,”*“就会消失,直到文本再次被修改(这就是为什么在上文保存功能块中,我会给主窗体的Caption再次进行赋值)。

初始化和关闭窗口时对应事件;

最后是窗口创建时的初始化工作和意外关闭窗口时做出的反应,如图;

技术图片

第一个例程就是Form1窗体创建时我想要进行的初始化操作,前文也说了,WhetherSaved这个全局变量初始要赋值为False,表示文本从未被保存过;

第二个例程是我们点击关闭窗体按钮时会作出的反应,跟上文的点击”打开“选项作出的反应差不多,就是判断一下文本是否被修改过,如果被修改了,弹出对话框,点击“是”,交给保存功能处理,处理完之后就退出了;点击“否”,就直接退出了;点击“取消”,就会中断操作,窗口不会被关闭。


以上就是我在实现这个小程序的过程中遇到的问题,希望同样喜欢Delphi的同学可以和我交流,因为我是今年才入的坑,很多东西都不懂,Delphi现在很没落,教程也很少,而RAD文档做的一点都不详细,所以希望能在交流中得到更多的指导。

文中有错误的地方欢迎指正,需要源码的同学可以在评论中留下邮箱。

以上是关于Delphi练习之简单实现记事本功能的主要内容,如果未能解决你的问题,请参考以下文章

如何用delphi6编记事本的步骤

如何把不同的几个delphi程序合并为一个工具集?

简单的记事本功能实现代码--(流读取器)

delphi 窗体问题

记事本的简单功能制作

Delphi如何获得窗口的名字?