在Delphi中巧改窗体文件实现控件数组化

在Delphi中巧改窗体文件实现控件数组化,第1张

  delphi 开发的应用中 每一个窗体都有一个对应的窗体文件( dfm) 用来记录该窗体的属性以及窗体上所有控件的属性 以便在窗体关闭后能准确地重新生成窗体 几乎所有的DELPHI参考书都没有提到过该文件的具体情况 偶尔提到 也都泛泛而谈 因为窗体文件是二进制文件 只有在DELPHI提供的编辑环境中才能看到它的本来面目 对其进行 *** 作可能会出现不可预知的错误 而且在大多数情况下 确实没有修改的必要 而本文谈到的和窗体文件密切相关

要利用窗体文件 首先必须了解该类型文件的结构 窗体文件的结构很简单 朋友们可以生成一个窗体 随便放上一些控件 存盘后打开Unit dfm文件 就可以看到窗体文件是由关键字"object"和"end"构成的代码段 基本结构如下

object  控件名 类名属性 =属性值属性 =属性值…end

并且支持嵌套 Delphi在记录控件属性时 只记录修改过的属性 举一个例子 比如对一个标签控件(label )的缺省描述如下

object Label : TLabelLeft = Top = Width = Height = Caption = &# Label &# End

记录的五个属性都是随开发者拖放的位置和顺序不同而变化的 其它属性由于没有修改过 都是缺省值 所以不必记录

窗体文件是有序的 它的有序性表现如下

object 窗体名 Tform窗体属性 =属性值窗体属性 =属性值 // 以下是TgraphControl类型的控件object 控件名 类名控件属性 =属性值控件属性 =属性值 endobject 控件名 类名控件属性 =属性值控件属性 =属性值 end // 以下是TwinControl类型的控件object 控件名 类名控件属性 =属性值控件属性 =属性值 endobject 控件名 类名控件属性 =属性值控件属性 =属性值 end // 以下是其它类型的控件object 控件名 类名控件属性 =属性值控件属性 =属性值 end end

在同一种类型的控件中 各控件排列的先后顺序和它被拖放到窗体上的先后顺序相同 这个顺序是可以人为修改的 我们正是通过修改这个顺序 来实现控件的数组化 下面将详细介绍

熟悉VB的朋友肯定知道在VB中可以通过控件拷贝实现控件的数组化 而DELPHI中则没有这种功能 Delphi中可以使用Components Controls两个控件数组在一定程度上模拟控件的数组化 比如

for I := to ControlCount do  if (Controls[I] is Tlabel) then(Controls[I] as Tlabel) Caption := &# Test&#

这段代码的功能是将窗体上所有Label的Caption属性设为&# Test&# 这是一种非常有用的方法 大家如果不太熟悉可以参考delphi帮助作进一步了解 这种方法有很多局限 最明显的是我们并不知道Controls[i]或Components[i]到底代表哪一个控件 只能用遍历的方法进行筛选 这不仅影响了程序执行的效率 也带来编程上的繁琐

其实 Controls和Components中控件的排列顺序和对应的窗体文件( dfm)中控件描述代码段的排列顺序是相同的 前面我们谈到窗体文件是可以进行适当修改的 也就是说 我们可以根据需要调整窗体文件中控件描述代码段的排列顺序 让Controls和Components这两个控件数组全在掌握之中 这样我们就能清楚知道Controls[I]或Components[I]具体代表的是哪一个控件 下面举例说明

比如 我们想让窗体Form 上的所有Tbutton灰化 最简单的方法是一句一句的编写代码

Button Enabled := FalseButton Enabled := False … …

如果Tbutton数量很多 代码就变得很冗长 于是我们采用一个循环来实现

for I := to ControlCount  do    if Controls[I] is Tbutton Then(Controls[I] as Tbutton) Enabled := False

lishixinzhi/Article/program/Delphi/201401/30261

可视化控件(Visual Component)实际上就是一个类(class) 要编写一个类 可以直接在* pas文件中编写 但是要编写控件 则必须使用包(package) 从File菜单中选择New 新建一个Package 这就是存放和安装控件用的包 然后单击Package窗口中的Add按钮 添加一个元件(Unit)

在d出的对话框最上方选择New Component 因为一个控件的所有属性 方法 事件不可能都由自己编 所以就需要选择祖先类(或者叫做 父类 或 基类 ) 然后再在其上面添加自己的属性 方法 事件 在Ancestor type后的下拉框中选择所需的祖先类 由于编写可视化控件必须要画图 所以选择TGraphicControl作为祖先类 再在Class Name框中输入新控件(类)的名称 一般以 T 开头 Palette Page是用来选择新控件在Delphi的窗口中的控件页面名称 例如 Standard 这个可以自己取 在Unit File Name中添好新控件文件的路径及文件名 单击OK按钮 新的控件便加入了 现在可以为该控件编写代码了

下面以编写一个可以自定义图片的滚动条为例 说明编写可视化控件的方法 按照上面的方法 选择TGraphicControl为祖先类 新控件的名称是TPigHorizontalScroller (小猪水平滚动条) 选择好文件路径和文件名后 单击OK按钮 开始编写代码

每一个控件 都会被创建(Create)和删除(Destroy) 所以必须首先编写这两个过程 对于控件中的每一个过程 都必须在前面先定义 然后再在后面编写 定义的过程或属性有三种 (1)在private后定义的是属于控件内部使用的 使用该控件的人无法看到 (2)在protected后定义的一般是看不到的 只在别人使用该控件作为祖先类编写其它控件时才可见 (3)在public后定义的只允许别人在程序中调用 (4)在published后定义的可以在属性窗口(Object Inspector)中看到 由于创建和删除过程除了在编程过程中建立控件时自动执行外 还可能在程序运行过程中动态创建控件时被调用 所以把它定义在public后⑴ (该序号表示次步骤在所附源程序中的代码的位置 下同)现在也许还不知到应该在这两个过程中编写什么 如何去编 我们在下面将会讲到

我们首先为这个控件添加一些属性 我们定义一个Max属性用于设置或读取滚动条的最大值 因为在程序中一般不直接使用属性 所以要定义一个变量 和该属性对应起来 一边修改或读取其值 因为它只在控件内部使用 所以我们把它定义在private后⑵ (一般与属性相关联的变量都以 F 开头 例如FMax)定义好变量后 再定义属性 这个属性需要再Object Inspector窗口中可见 所以把它定义再published后⑶ 定义的语法是

property <属性名>:<类型>read <读取该属性时对应的变量>write <写入该属性时对应的变量或过程>

其它的变量和属性也类似的定义(例如Min最小值 Value当前值等) 下面我们定义几个属性和变量 用于设置滚动条的图片(因为图片变量比较特殊 所以单独讲一下) 我们把LeftButtonUpPicture(向左按钮图片) LeftButtonDownPicture(向左按钮按下图片)等定义为TBitmap类型(一定要定义相对应的变量)

大家一定注意到了 在所附的源程序中 定义这几个属性时 read后所指定的读取属性时对应的变量是F… 而write后指定的写入该属性时对应的不是一个变量 而是一个Set…之类的东西 这是一个自定义的过程 作为该功能的过程的定义为

procedure <过程名>(Value: <被设置的属性的值的类型>)

因为执行写入该类属性的时候需要做其它的事情 所以不能光用一个变量来处理 应该用一个过程来处理 这中过程一般定义在protected后 在该类过程中 使用一个在⑷处这样一个语句来给TBitmap类型的变量来赋值 这是由于该类型的变量不能直接赋值而采用的

定义完这些TBitmap类型的变量的属性后 上面讲的create过程和destroy过程中就需要编写代码了 因为TBitmap也是一个类 所以在create过程中必须创建⑸ 在destroy过程中必须释放掉(free)⑹ 这里⑺所指的inherited语句是用于指明该过程是从祖先类类中继承来的 (这个一定不能掉)

因为我们编写的是可视化控件 所以必须在控件上画图 我们这个控件的祖先类TGraphicControl中封装有一个Canvas(画布)对象 我们可以直接使用它来画图 如果你对画布的使用还不太熟悉 最好去找一本书来看一看

下面要做的工作就是画图了 如何在控件上画图呢?祖先类TGraphicControl中有一个Paint事件 当控件需要重画时便会自动触发 我们现在要做的就是要为这个事件编写一段程序 首先在protected后定义一个Canvas对象 由于它是祖先类中已有的 所以不需要加任何说明⑻ 我们将使用这个对象来画图 接着 就要定义一个Paint过程 编写绘制控件的代码 先在public后定义Paint过程 由于它是由祖先类触发的 而不是由用户调用的 所以后面必须加上override 否则 该控件将会由于Paint过程永远不会被调用而不成为可视化控件⑼ 下面我们就来编写Paint过程的代码⑽

该文章所附的源程序的Paint过程中的T_Height等变量是用来保存滚动条中按钮 滑块等的大小的 这部分程序和普通的Application中的程序差别不大 大部分都是对画布进行 *** 作 相信大家一看就明白 值得注意的是下面对FAutoSize变量的判断⑾ FAutoSize是和该控件的属性AutoSize相关联的布尔型变量 是用来设置这个控件的大小是否随图片的大小而变化的 注意 在控件的代码中 一般都不直接调用属性 而是使用与其相对应的的变量

程序编到这里 就算是终于给自己的新控件做了一个外型了 不过它还不能滚动 现在我们来编写鼠标事件 让我们能够 *** 纵它 鼠标事件的过程的定义和Paint过程很相似 只是后面要加上参数说明⑿ 鼠标事件分为MouseDown MouseMove和MouseUp三个 在定义后面都要加上override 接下来在后面编写它的代码 注意 这里的鼠标事件是Mouse… 而不是通常的OnMouse… 可是在⒀处的定义是干什么用的呢?这里的事件定义 都是给用户使用的 也就是说 当使用该控件时 会在Object Inspector中的Event页面中显示出来

这些鼠标事件的代码也非常简单 判断鼠标的坐标 在画布上画出相应的图片等 并同时触发相应的事件 值得注意的是 在调用自定义事件时 都要先用⒁处的这样一个语句来判断用户是否已经为该事件编写代码 这一点非常重要 否则会调用出错

大家注意到了 刚才所调用的事件都是自定义的 定义的方法也很简单 和定义属性差不多 只是类型时TNotifyEvent罢了  

TNotifyEvent是默认事件 其定义为

TNotifyEvent = procedure(Sender: TObject)

如果你要定义另外形式的事件 就必须这样 先在type后编写

<事件类型名称>= procedure(<参?gt:<类型>)

例如

TCustomEvent = procedure(a: Integerb:String)

然后在public后定义

<事件名称>:<事件类型名称>

例如

AnEvent: TCustomEvent

看完这些 这整个程序你应该理解了吧 如果编译或运行出错 注意检查以下几点

(1)create和destroy过程中是否有inherited语句 (2)TBitmap类型的变量create和free了没有 (3)过程前有没有控件名 例如 TPigHorizontalScroller MoseMove

判断鼠标是否进入或离开控件的方法

定义如下的过程

procedure MouseEnter(var Msg: TMessage)message CM_MOUSEENTERprocedure MouseLeave(var Msg: TMessage)message CM_MOUSELEAVE

lishixinzhi/Article/program/Delphi/201311/8448

选择目录或文件路径 有多种方式,通常可以使用以下方法:

1、直接使用 WinApi 函数,如 SelectDirectory 等。

2、使用选择文件(路径)对话框控件,如 OpenDialog 等。

3、使用与 shell 相关的控件,如 ShellTreeView、ShellListView、TDirectoryOutLine 等。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/tougao/11825114.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-19
下一篇 2023-05-19

发表评论

登录后才能评论

评论列表(0条)

保存