发布日期:
FoxTalk 2.0
四月
来自
Craig Boyd
很棒的工具
Craig Boyd
(克雷格
.
博伊德)为
VFP
开发人员创作一些很棒的工具。这个月,
Doug Hennig
分析
Craig
加密函数库及分割条和进度条控件。
Craig Boyd
在
VFP
组别中最突出的一位新的微软最有价值专家。他有一个非常有趣的博客
(www.sweetpotatosoftware.com/SPSBlog),
,是
SednaX
的发起人和其中一位管理人员,
SednaX (
www.gotdotnet.com/codegallery/codegallery.aspx?id=0826d7a6-1dab-4a71-8e70-f2170c3c1661)
,是一个专注于扩展
VFP
的社会区网站,给
VFP
开发人员发布了许多很棒的程序。使我不变地惊讶的不但是他创作程序的数量并慷慨地向大家发表了,而且创造性地实现了他的想法。这篇文章标记我身为
FoxTalk
作家的
10
周年纪念,开始新的一个系列,看看
Craig
已经创作了的一些工具。
加密函数库
在我在
2006
年一月份
FoxTalk 2.0
的文章“基于角色安全,第二部份”我简要地讨论了
Craig
的
VFPEncryption.FLL
,一个向
VFP
开发人员提供了加密和解密函数的库文件。这个月,我们将更靠近一点的看它。你可以在
www.sweetpotatosoftware.com/files/vfpencryption.zip
下载这个库文件。(译者注:
VFPEncryption.FLL
是有两个版本的,
http://www.sweetpotatosoftware.com/files/vfpencryption.zip
是用
Visual Studio 2005
编译,需要
C++ 8.0
运行时
MSVCR80.DLL
,
http://www.sweetpotatosoftware.com/files/vfpencryption71.zip
是用
Visual Studio 2003
编译,需要
C++ 7.1
运行时
MSVCR71.DLL
,
VFP9
带有
MSVCR71.DLL
。)
VFPEncryption.FLL
是很简易使用的:
SET LIBRARY TO VFPEncryption.FLL
,和调用它的五个函数中的一个。
• Encrypt(cString, cKey[, nType[, nMode]])–
加密指定的字符串。我会在后面详述各个参数。
• Decrypt(cString, cKey[, nType[, nMode]])–
解密指定的字符串。
• EncryptFile(cSourceFileName, cTargetFileName, cKey[, nType[, nMode]])–
加密指定文件,创建目标文件的结果为函数返回值。(两个文件名要指完整的路径)
• DecryptFile(cSourceFileName, cTargetFileName, cKey[, nType[, nMode]])–
解密指定的文件到目标文件。
• Hash(cString[, nHashType])–
获得指定的字符串的
hash
值。
Hash
是单向加密程序,没有方法解密的。它是经常用于密码,储存初始密码的
hash
,然后当用户登录时,输入的密码的
hash
值和储存的
hash
值进行比较。
Hash
的值的好处是不同于其他(加密)机制,没有能够被泄密的私人密匙。
如下是参数的说明:
• cKey–
是用加密和解密的密匙。很明显,解密时,你必须提供用来加密字符串或文件相同的密匙。密匙根据不同的加密类型需要特定的长度。
• nType–
加密的类型。
VFPEncryption.FLL
支持
6
种加密类型。
0
是
AES128
,要求
16
个字符的密匙,
1
是
AES192,
要求
24
个字符的密匙,
2
(默认)
AES256
,要求
32
个字符的密匙,
4
是
Blowfish
,要求
56
个字符的密匙,
8
是
TEA
,要求语
16
个字符的密匙,或者
1024
是
RC4
,这种情况密匙可以任意长度。
• nMode–
加密的模式,指定
0
(默认)电子源码书(
ECB
)(网摘:密码块操作的一种模式,其主要特点为每一种可能存在的纯文本内容都拥有固定的密文与之相对应,而且反之亦然。换言之,相同的纯文本内容总会被转化成相同的密文),
1
是密码块连接
(CBC)
(网摘:密码块运算的一种模式,密码块中一系列的二进制代码利用整个数据块中的密钥加密成单元或数据块。密码块链接使用了广为人知的固定长度的初始微量),还有
2
是密文反馈
(CFB)
(网摘:一种密码块操作模式。与密码块链接模式相对,它对一定数位大小的密码块纯文本进行一次性加密,有时候需要对一些纯文本数据值进行逐一加密并传输,这时密文反馈就是一种有效的方法。)如果使用
RC4
加密,就会忽略该参数据的值。
• nHashType–
使用
hash
函数的类型,指定
1
是
SHA1
(也叫做
SHA160
),
2
是
SHA256
,
3
是
SHA384
,
4
(默认)是
SHA512
,
5
是
MD5
,
6
是
RIPEMD128
,或者
7
是
RIPEMD256
。
如果你不懂得不同的加密类型和模式,不用担心,我真的没有关于这一方面的一个线索,这是我喜欢
VFPEncryption.FLL
的主要原因。
TestVFPEncryption.PRG
是一个示范使用这个函数库的小程序。
set library to VFPEncryption.FLL
* Test encrypting and decrypting a string.
lcString = 'This is pretty cool stuff!'
lcKey = padr('This is a secret key phrase.', 32)
&& padded to the length needed by the default
&& encryption type
lcEncrypted = Encrypt(lcString, lcKey)
wait window lcEncrypted
wait window Decrypt(lcEncrypted, lcKey)
* Test encrypting and decrypting a file.
EncryptFile(fullpath('TestVFPEncryption.prg'), ;
fullpath('EncryptedFile.prg'), lcKey)
modify file EncryptedFile.prg
DecryptFile(fullpath('EncryptedFile.prg'), ;
fullpath('DecryptedFile.prg'), lcKey)
modify file DecryptedFile.prg
* Test hashing a string.
wait window Hash(lcString)
分割控件
分割条是很有趣的控件:它们本身真的没有可视的界面,却能让你们在两个或更多其他控件之间通过调节一个的尺寸,而改变其他控件的相关尺寸。分割条在
Windows
应用系统的许多地方里出现,在
Windows
的资源管理器,能使用分割条调节左右窗格的尺寸。分割条可以是水平的(左右调整对象)或垂直的(上下调整对象),和可以在同一个视窗(表单)使用分割条的两种类型。
在
1999
年
7
月
FoxTalk
我的专栏中,“
Splitting Up is Hard to Do
”,我介绍的一个
VFP
分割条类,
SFSplitter
,和具体的子类用于水平(
SFSplitterH
)和垂直(
SFSplitterV
)的分割。它们是相当的复杂类:它们自动地添加另外一个类,
SFSpliterCover
,到它们的父对象和使用
OLE
拖放实现分割条的移动。添加一个分割条到表单要拖放适当的分割条类到表单和设置相同的属性,表明了控件的名受移动的分割条影响。虽然这个类十分困难地去创建,但我已经成功在许多项目使用和不但心存在的复杂性。
去年夏天,
Craig
在他的博客中发布一个不同实现方法的分割条类,我必须承认我是怀疑的。在
Splitter.VCX
里仅仅只有一个单独的
Splitter
类,它只有一个属性用来设置垂直,指定分割条是否是垂直的或水平的
-
而且不是很多的代码。它是假设每个可视控制是左右(垂直分割的情况)或上下(水平分割的情况)的,当移动分割条时,分割条被影响,(这是)一个合理的假设,尽管你可以放置字符串“
DoN't_MoVe_SpLiT
”入
Tag
的任何控件的会被忽略。
我们去看看它是如何工作的,然后钻研它的代码。从
Craig
的网站下载
www.sweetpotatosoftware.com/files/splitter.zip
,解压到一个目录,并执行
do form splitter
。这是示例的表单,如图
1
所示,有三个编辑框和两个分割条,一个水平和另一个垂直。上下移动水平分割条和留意三个编辑框被适当地改变尺寸。左右移垂直分割条,下面两个编辑必然被调整了。
图
1
现在编辑这个表单,注意除了适当直改变尺寸和定位外,只是常规地改变两个分割条控件的属性分别是
Anchor
(因些它们响应表单的尺寸变化)和
Vertical
(指示如何分割的动作)。
MousePointer
属性设成“
9-Size WE
”,但在
Init
方法中,如果
Vertical
是
.f.
把它改为“
7-Size NS
”,因此,移动鼠标到控件上时将指针转换成适当的形状,明显使用户知道它是分割条控件。
MouseDown
事件记录鼠标当前的
X
或
Y
坐标(依赖
Vertical
的设置记录
X
还是
Y
)到
MouseDownAt
自定义属性。
MouseUp
和
MouseLeave
都设置这个属性为
0
。
实际的工作是在
MouseMove
和
Move
里,由于版面原因我不在这里展示这些代码,但会详细说明它们。
MouseMove
,当鼠标被移到在控件上面时激发,激发时确保鼠标左键是按下和
MouseDownAt
是大于
0
,如果鼠标在另一个控件上按下然后移到分割条的上面,这就要阻止任何事发生。如果鼠标移动(它当前的
X
或
Y
坐标不与
MouseDownAt
不相同),只要它不是太靠近表单的边缘时,程序就计算它被移动了多少,(是否太靠近表单的边缘)取决于自定义属性
MinimumSize
,
MouseMove
调用
Move
方法去做实正的移动和重置
MouseDownAt
到新的位置。
MinimumSize
默认是
40
所以控件不能变得如此小,使用你说见不到它们或使用它们,但有必要时可以把它设置一个不同的值。
Move
方法是做受分割条影响的而移动所有控件和分割条本身的卑劣(
dirty
)行为。它是从判断
MouseDownAt
不是
0
才开始,然后锁定屏幕,那么对象的移动在全部被移动完之前
,
不表现出来,计算鼠标移动的距离,和遍历它父容器中的控件,忽略任何
Tag
属性为“
DoN't_MoVe_SpLiT
”的控件,因为是非可视控件。代码首先保存控件的
Anchor
属性值后设为
0
,这是必需的否则这个控件在下次表单被改变尺寸时不能无完全显示。如果分割条是垂直的,控件是在分割条的左边或右边,如果是在分割条的右边,代码会调整控件宽度和
Left
属性。如果分割条是水平的,代码做控制它的上面和下面做类似处理。然后恢复控件的
Anchor
属性。在处理父容器的所有控件后,保存分割条的
Anchor
属性,然后把分割条移到它的新位置,再恢复它
Anchor
。最后解锁屏幕。
如果你想以编程方式移动分割条,设置
MouseDownAt
为一个非零值和调用
Move
方法。它能这样使用的,例如,恢复分割条到上次运行地表单的位置。例如,你可以在表单
Init
中用像一下面的代码,
lnTop
的值用作水平分割条的
Top
属性。
with This.Splitter
.MouseDownAt = .Top
.Move(.Left, lnTop, .Width, .Height)
endwith
它就是这样,正如我前面说的,在这类里没有太多的代码,它比几年前介绍的分割条类简单的多,和更容易使用。然而,我是个过分讲究的人,我决定创建它子类去改变它的一个处理。
Splitter
假设两边的控件最小尺寸相同。可是,有很多时候是一边必须比另一边大的,例如当在变得十分窄小的边上有一个列表框,但在另一边上的标签的容器和文本框,那样应该不是吧。
在
SFSplitter.VCX
的
SFSplitter
是分割条的子类,它带有两个新属性:
nMinimumSize1
,它包含了分割条左边或上面的控件的最小尺寸,和
nMinimumSize2
,它包含了分割条右边或下面的控件的最小尺寸。如果在它们默认值
0
离开它们时
Init
设置它们为适当的值。我然后覆盖
MouseMove
去使用这些属性并非
MinimumSize
。
Do form SFSplitter
,看我的子类是如何处理的。留意分割条在它们移动里不是对称,在边上的组件能够比另一个大,因为我设置了
nMinimumSize1
和
nMinimumSize2
为不同的值。这个表单在
Init
里也改变了水平分割条的初始位置用来展示如何工作的。
进度条控件
进度条控件有时会调用温度计控件,当运行时间长的时候给用户提供反馈运行情况。
Visual FoxPro
已经长期含了一个
ActiveX
控件,微软的
ProgressBar
控件,可以添加到一个表单来提供这类型的反馈。可是,这个控件呈现下降趋势,它不得不要发布和注册
MSCONCTL.OCX
。同时,因为它是的旧控件,你会在其他应用中见到它没有现代外观而象典型的进度条。
在发布分割条控件仅一个星期后,
Craig
发布了他的进度条控件,
ProgressBarEx.VCX
里的
ProgressBar
。它是百分这一百
VFP
代码,所以没有
ActiveX
发布的担心。你也可以在图
2
里看到,它看起来象一个标准的
Windows Xp
的进度条,完全由色彩的梯度和一个可选的百份比标签。
图
2
下载
www.sweetpotatosoftware.com/files/progressbarex.zip
获得
Craig
的进度条控件。运行(压缩包)中的
Example
表单,在微调器中输入一个百份数值,看一下它是如何工作的。这三条进度条展示了它能表现的不同方式。
让我们来看一下
Craig
是如何实现他的进度条的。在
ProgressBarEx.VCX
里有两个类:
PBBar
,它是表现在进度条中单独的色块,和
ProgressBar
进度条的本身。
ProgressBar
有几个决定它外观的属性:
• BarColor–
进度条的颜色,当选择
1
是红色,
2
(默认)是绿色,和平是蓝色。
• Bars–
进度条的色块数。数值越大,色块就越细。
• Min–
进度条的最小值,默认是
0
。
• Max–
进度条的最大值,默认是
100
。
• Percentage–
当前完成的百份比。
• ShowPercentage–
当是
.t.
时,在进度条中显示百份数。
• SolidBar–.t.
时显示实心,
.f.
时(默认)显示为一个个的色块。
• Value–
进度条的当前值,你不必要指定百份比数值。例如,如果你的程序是处理
1249
条记录,就设置
Max
为
1249
,
Value
就是正在处理的记录号。
ProgressBar
的
Init
方法实例化
Bars
属性指定数量的
PBBar
实例,但没有设置它们的
Visible
属性设为
.t.
,所以它们最初是没有显示的。如果
ShowPercentage
是
.t.
它也会添加一个标签到控件。
Value
属性有个
Assign
方法,用来计算有多少色块要显示,并设置那些色块对象的
Visible
属性为
.t.
,如果
ShowPercentage
是
.t.
也要更新标签的标题。
Percentage
属性有一个
Access
方法,根据
Min
,
Max
,和
Value
的值计算完成的百份比。
做出这个象
Winows XP
控件的进度条真不可思议的是
PBBar
类。它的
Init
接受逐个色块的颜色,宽度和高度三个参数(通过
ProgressBar
传递,当它实例化这些对象时)。为了显示一个色块,
Init
实例化了一系列
Line
对象,一个
Line
对像对应进度条的高度的一个像素。这些线用不同的颜色画,从中间的深色调到上下的较浅色调,做出一个过度的效果。同时,
PBBar
设置线的
DrawMode
属性为“
14-Merge Pen Not
”。这才让百份比的标签用正确的颜色显示出来,即使它部份地或者完全地覆盖了色块。
要使用
Craig
的
ProgressBar
类,简单地把它拖到表单上,根据需要设置属性,和在处理循环里设
Value
为一个合理值。然而,你可能想在与从开始处理过程的同一个表单中显示进度,并非使用单独的表单显示一些事情的进度。例如,我喜欢用一些类型表单的状态栏,它会在状态栏的一个面板中很好地显示进度条。我用的是
Rick Strahl
的
wwStatusBar
控件而不是
ActiveX
微软的
StatusBar
控件,同样原因,
Craig
的进度条比
ActiveX
控件更好的,看看
www.west-wind.com/presentations/wwstatusbar/wwstatusbar.asp
,这是描述这个类的文章和下载源代码的链接。
ProgressBar
有一个小小的问题,它要在
Init
中做所有的设置。这种方式的问题是它要求属性在属性窗口里设置好需要的值。如果你用编程方式实例化了这个类,
Init
事件在你有机会设置属性之前激发。所以,我创建了一个
ProgressBar
的子类,
SFProgressBar.VCX
中的
SFProgressBar
。我把
ProgressBar
的
Init
里的代码复制到一个叫做
SetupProgressBar
的新方法里,并在
Init
里加了一个注释,所以当这个类实例化时没有事情发生。因为我懒和更合适地避免必须要手动调用
SetupProgressBar
,所以我添加了一个叫
lSetup
的自定义属性,默认值为
.f.
,和在
SetupProgressBar
的结尾设它为
.t.
。然后,我用下面的代码覆盖
Value_Assign
。
lparameters tuNewVal
* Set up the progress bar if it hasn't been done.
if not This.lSetup
This.SetupProgressBar()
endif not This.lSetup
* If the value is 0, let's be invisible. Otherwise,
* ensure we can be seen.
This.Visible = tuNewVal <> 0
dodefault(tuNewVal)
这保证了
Value
第一次获得设置时,进度条被完全地设置。我确定当
Value
为
0
时进度条不可见,其他值才可见,所以这段代码是这样处理的。
我也建了一个
wwStatusBar
(
http://www.west-wind.com/presentations/wwStatusBar/wwStatusBar.zip
)的子类,在
SFStatusBar.VCX
里叫
SFStatusBar
。这个类的
Init
方法实例化
SFProgressBar
为表单的
ProgressBar
属性,增加它一个
Panel
自定义属性,用指明的进度条在状态的那个面板显示,和用了
BINDEVENT()
,所以当这个属性被改变时,新方法
PutProgressBarInPanel
被激发。我也加了调用
Resize
用来克服
wwStatusBar
的一个问题,当表单第一次显示时,由于它的
Resize
方法,它不能完整显示,这样设置是绑定到表单的
Resize
事件。如表单没有改变尺寸,状态栏适当地没被调整尺寸。
This.NewObject('ProgressBar', 'SFProgressBar', ;
'SFProgressBar.vcx')
addproperty(This.ProgressBar, 'Panel', 1)
bindevent(This.ProgressBar, 'Panel', This, ;
'PutProgressBarInPanel', 1)
dodefault()
This.Resize()
看在运行中的
SFStatusBar
,执行
SFProgress
表单我单击“
Start Process
”按钮,如图
3
所示,进度条显示在状态栏的第二个面板里。
图
3
这个表单在
Init
里有下面的代码,在状态栏里创建一对面板和把进度条放入面板
2
里。
with This.oStatus
.AddPanel('Ready', 300, .T., 0)
.AddPanel('', 200, .F., 1)
.RenderPanels()
.ProgressBar.Panel = 2
endwith
按钮的
Click
方法里用一个伪循环来示范这个进度条。
* Have status panel 1 indicate what we're doing.
Thisform.oStatus.UpdatePanel(1, 'Processing...')
* Perform a loop and show the progress.
for lnI = 1 to 100
Thisform.oStatus.ProgressBar.Value = lnI
inkey(0.02, 'H')
next lnI
* Reset the progress bar back to 0 and update status
* panel 1.
Thisform.oStatus.ProgressBar.Value = 0
Thisform.oStatus.UpdatePanel(1, 'Ready')
总结
这个月,我们看了
Craig Boyd
慷慨捐赠给
VFP
社区的三个工具:一个加密函数库及分割条和进度条控件。下个月,我们将看的控件是基于
VFP
的日历,滚动条和任务面板。