3.7.杀毒软件界面设计

从某种角色而言,软件界面比功能更为重要,当然这是针对用户的。界面是软件的门面,会带给用户第一使用感受,如果软件界面做的很糟糕,用户用过一次便不想再看,那么即便是软件功能再强大,用户也不会想使用。

在当下流行的各种开发工具中,都会提供各种功能控件,但这些控件风格都比较陈旧,按钮永远都是一个单色的小方块、文本框永远都有丑陋的下陷边框等等。当然是有第三方的界面控件可以使用,但其对开发环境的要求往往比较苛刻、会增加一定额外的代码量、控件内部功能对开发者不透明、会引起软件意外的错误、不能完全满足开发时随心所欲的需求等等。那么在这一章,我们将解决这一问题,介绍一种软件界面设计方面的技巧和相关的编程方法,提供给大家一种全新的界面设计思路。

3.7.1.漂亮界面的设计技巧:图片替换控件法
说到全新,其实也不能称的上是全新,只是一种技巧,只大多数人通常在不经意见忽视了它,没有发现它的价值。简单的说,就是大量使用图片来取代和装饰原来控件。

比如:去掉界面的边框,使用一张背景图来做为新的界面,在此图的基础上再次放置图片来替代界面固有的最大化、最小化、退出按钮。为了实现按钮在移动和按下的效果,可以做三组不同的图片,分别用来显示按钮在普通状态下的效果、鼠标移动到上面时的效果和鼠标按下后的效果。再在代码中图片感应到的事件函数中对图片做不同的显现控制、位置控件,这样就可以实现完美替换原有按钮的功能,又能达一美化界面的功效。

下面付上一段对界面中关闭按钮进行图片替代的编码方法:

首先在界面中放置三个图片,做了编码方法,使用数组的方式对其进行命名,分别为:ImageExit(0)、ImageExit(1)、ImageExit(2)

ImageExit(0)中贴入按钮普通状态下图片,ImageExit(1)中贴入鼠标移动到上面后的图片,ImageExit(2)中贴入按钮被按下后的状态图片。

然后进行编码:

在窗体加载函数中:
Private Sub Form_Load()
Dim i As Long
For i = 0 To 2
初始化关闭铵钮图片位置
With ImageExit(i)
.Left = 3480
.Top = 0
End With
Next

设置各图片的可见状态
ImageExit(0).Visible = True
ImageExit(1).Visible = False
ImageExit(2).Visible = False
End Sub

这样,窗体加载后,使三个图片的位置重叠在一起,而且控制隐藏掉两个图片,只显示一个普通状态下的图片。在用户看起来,就会只是一个按钮。

然后在其它图片事件中,再对按钮的显现做调整,以模拟正常关闭按钮对鼠标的响应:

鼠标在图片上移动时:
Private Sub ImageExit_MouseMove(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single)
ImageExit(0).Visible = False
ImageExit(1).Visible = True
ImageExit(2).Visible = False
End Sub

鼠标按下时:
Private Sub ImageExit_MouseDown(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single)
ImageExit(0).Visible = False
ImageExit(1).Visible = False
ImageExit(2).Visible = True
End Sub

鼠标抬起时,执行关闭操作
Private Sub ImageExit_MouseUp(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single)
Unload Me
End Sub

由于是文字介绍,这部分功能无法直接的效果显现给读者,需仔细思考加实践方可理解此法之妙、效果之好。

这里只是演示了关闭按钮的图片替代法,其它各种功能控件,也可以使用类似的方法进行美化、替换。

3.7.2.图片替换法的高级应用:界面换肤

因为图片用图片代替了控件,而使用的图片恰恰是可以控制的,那么在此技术的基础上,我们可以实现很多人梦寐以求的高级界面技术:界面换肤。

在图片替换法的基础上,界面换肤的实现非常简单:在上面的介绍中,关闭按钮的图片是事先贴好在图片中的,在此我们只需动态的替换图片中的图片源,即可实现界面换肤。同样以关闭按钮为例来演示:

在窗体加载函数中:
Private Sub Form_Load()
ImageExit(0).Picture = LoadPicture(App.Path & “\Skin_01\Exit0.bmp”)
ImageExit(1).Picture = LoadPicture(App.Path & “\Skin_01\Exit1.bmp”)
ImageExit(2).Picture = LoadPicture(App.Path & “\Skin_01\Exit2.bmp”)

Dim i As Long
For i = 0 To 2
    初始化关闭铵钮图片位置
    With ImageExit(i)
        .Left = 3480
        .Top = 0
    End With

Next

设置各图片的可见状态
ImageExit(0).Visible = True
ImageExit(1).Visible = False
ImageExit(2).Visible = False
End Sub

同样是在窗体加载函数中,只需新增三行代码,不严格的说,只需一行:

ImageExit(0).Picture = LoadPicture(App.Path & “\Skin_01\Exit0.bmp”)

即可实现肤换,其它图片对事件的响应代码不变,依然是使用上面的代码。

这行代码的功能是当窗体加载时,对图片控件中的图片进行加载,在这里是使用了文件:
App.Path & “\Skin_01\Exit0.bmp”,也就是软件目录中Skin_01目录下的Exit0.bmp文件,

要换肤时,只需更改此文件的路径即可,比如使用另一目录下的文件:
App.Path & “\Skin_02\Exit0.bmp”

然后显现在图片中的将会是另一张图,对用户而言,将会是另一个软件界面。

3.7.3.扫描自定义目录界面的实现

这个功能是杀毒软件所必备的,进行自定义扫描时,必须提供给用户选择不同驱动器、目录的选择权。实现这个功能,我们使用TreeView树型控件,但TreeView控件原本并没有这个功能,为了达到预期的需求,这里需要使用众多的技巧,这一部分功能代码用于演示使用编程技巧扩充控件原有功能的实现。

我们的预期目标是:在TreeView控件中,显示出计算机上所有的驱动器;在打开不同的驱动器及目录时,能够显示它的下级目录;在选中或取消选中某一节点时,控件能自动选中和取消选中该节点的子节点,但不影响父节点。界面效果如下:
病毒检测与杀毒技术大揭秘10-LMLPHP
假设已经在窗体中使用了TreeView控件,名称为:TreeViewScanTarget,功能实现代码如下:

Private Sub Form_Load()

窗体加载时,获得系统所有盘符并添加到控件中进行显示
TreeViewScanTarget.Nodes.Add , , "Lord", "我的电脑", 1
Dim sDrive As String
sDrive = String(256, Chr(0))
Dim sDriveID As String
Call GetLogicalDriveStrings(256, sDrive)
Dim i As Long
For i = 1 To 100 Step 4
sDriveID = Mid(sDrive, i, 3)
TreeViewScanTarget.Nodes.Add "Lord", tvwChild, sDriveID, Left(sDriveID, 2), 2
Next i

设置根节为选中状态
TreeViewScanTarget.Nodes(1).Checked = True

设置根节为展开状态
TreeViewScanTarget.Nodes(1).Expanded = True

设置子节选中状态
CheckChildNodes TreeViewScanTarget.Nodes(1)
CheckParentNodes TreeViewScanTarget.Nodes(1)

End Sub

Treeview控件单击事件处理函数
Private Sub TreeViewScanTarget_Click()
CheckChildNodes TreeViewScanTarget.SelectedItem
CheckParentNodes TreeViewScanTarget.SelectedItem
End Sub

Treeview控件节点单击事件处理函数
Private Sub TreeViewScanTarget_NodeClick(ByVal Node As MSComctlLib.Node)

转移Treeview中节点的焦点
CheckChildNodes TreeViewScanTarget.SelectedItem
CheckParentNodes TreeViewScanTarget.SelectedItem

End Sub

Treeview控件节点展开事件处理函数,功能是:获取节点所对应的子目录,增加子节点
Private Sub TreeViewScanTarget_Expand(ByVal Node As MSComctlLib.Node)

如果Treeview无内容则退出
If TreeViewScanTarget.Nodes.Count = 0 Then Exit Sub

Dim i As Long

如果选中的节点无子节点
If Node.Children = 0 Then
Call GetSubFolders(TreeViewScanTarget, Node)

如果有子节点
Else
Dim uNode As Node

节点的第一子节点
Set uNode = TreeViewScanTarget.Nodes(Node.Index).Child

遍历所有子节点
For i = 1 To Node.Children

DoEvents
如果子节点没有子节点,则取子节点的下级目录,增加子节点

If uNode.Children = 0 Then
Call GetSubFolders(TreeViewScanTarget, uNode)
End If
Set uNode = uNode.Next

Next
End If

End Sub

处理子节点选中状态
Private Function CheckChildNodes(ByVal Node As MSComctlLib.Node)
Dim i As Long
Dim uChildNode As Node

如果没有子节点,则退出
If Node.Children = 0 Then
Exit Function
End If

If Node.Checked = True Then

选中
Set uChildNode = Node.Child
For i = 1 To Node.Children
uChildNode.Checked = True
If uChildNode.Children <> 0 Then
CheckChildNodes uChildNode
End If
Set uChildNode = uChildNode.Next
Next

Else
Set uChildNode = Node.Child
For i = 1 To Node.Children
uChildNode.Checked = False
If uChildNode.Children <> 0 Then
CheckChildNodes uChildNode
End If
Set uChildNode = uChildNode.Next
Next
End If

End Function

处理父节点选中状态
Private Function CheckParentNodes(ByVal Node As MSComctlLib.Node)
Dim i As Long
Dim uParentNode As Node

同层node个数
Dim lSameLevelNodes As Long
Dim uSameLevelNode As Node

如果没有子节点,则退出
If Node.Root = Node Then
    Exit Function
End If

If Node.Checked = True Then
Set uParentNode = Node.Parent
uParentNode.Checked = True

Do While uParentNode <> Node.Root
Set uParentNode = uParentNode.Parent
uParentNode.Checked = True
Loop
Else


检查是否同级节点都是不选中状态
If Node.Parent.Children > 0 Then
lSameLevelNodes = Node.Parent.Children
Set uSameLevelNode = Node.Parent.Child
For i = 1 To lSameLevelNodes
If uSameLevelNode.Checked = True Then
Exit Function
End If
Set uSameLevelNode = uSameLevelNode.Next
Next
End If

不选中
Set uParentNode = Node.Parent
uParentNode.Checked = False

Do While uParentNode <> Node.Root
Set uParentNode = uParentNode.Parent
uParentNode.Checked = False
Loop
End If

End Function

Treeview控件中节点选中及反选事件
Private Sub TreeViewScanTarget_NodeCheck(ByVal Node As MSComctlLib.Node)

转移Treeview中节点焦点
TreeViewScanTarget.SelectedItem = Node

处理节点选中状态
CheckChildNodes Node
CheckParentNodes Node

End Sub

取得目录或驱动器下子目录,参数:uTreeView,放置目录的Treeview控件;uParentNode:Treeview选中节点,包含文件路径,本函数即取该目录下的子目录
Public Function GetSubFolders(uTreeView As Control, ByVal uParentNode As Node)

Dim sPath As String

检查最后字母是否是“\”,Treeview以"\"分隔每个节点,所以:“我的电脑”+“\”长度为5
If Right(uParentNode.FullPath, 1) <> "\" Then
sPath = Right(uParentNode.FullPath, Len(uParentNode.FullPath) - 5) & "\"
Else
sPath = Right(uParentNode.FullPath, Len(uParentNode.FullPath) - 5)
End If

Dim lRet As Long
Dim uWFD As WIN32_FIND_DATA

查找目录下文件
lRet = FindFirstFile(sPath & "*.*" & Chr(0), uWFD)
If lRet <> -1 Then

不为.或..且是目录
If (TrimNull(uWFD.cFileName) <> ".") And (TrimNull(uWFD.cFileName) <> "..") And (uWFD.dwFileAttributes And vbDirectory) Then

增加子节点,添加目录
Call uTreeView.Nodes.Add(uParentNode.Key, tvwChild, sPath & TrimNull(uWFD.cFileName), TrimNull(uWFD.cFileName), 7)
End If

While FindNextFile(lRet, uWFD)

不为.或..且是目录
If (TrimNull(uWFD.cFileName) <> ".") And (TrimNull(uWFD.cFileName) <> "..") And (uWFD.dwFileAttributes And vbDirectory) Then
增加子节点,添加目录

Call uTreeView.Nodes.Add(uParentNode.Key, tvwChild, sPath & TrimNull(uWFD.cFileName), TrimNull(uWFD.cFileName), 7)
End If
wend
End If
Call FindClose(lRet)

End Function

3.8.其它辅助功能设计

之前几章中,出理条理和易懂的目的,对各内容的介绍是以功能为模块进行分类单独讲述的,因此有许多本该穿插在其中功能未做到讲解。在本章中将对这些重要的未讲解到的功能再做单独介绍。

注:作者:wing qq:6465660
本书理论及功能、代码源于杀毒软件:“Ty2y杀毒软件”,作者授意,文章可自由转载,只需注明原出处即可,特此说明。

10-07 18:48