一、ListView使用简介
ListView控件是VB开发者非常喜爱的控件之一。作 为Windows95公共控件组(COMCTL32.OCX)的成员,它经常与经常与TreeView、ImageList等控 件联合使用。即用TreeView显示一个的树型结构,而用ListView显示选中的节点(Node)对 象的记录集。
这是笔者在开发财务软件项目中的<<凭证管理>> 模块的一个用户界面。屏幕左边是一个TreeView控件,用来显示会计凭证的类别;右边是一 个ListView,用来显示对应类别的凭证目录;上方是一个菜单条控件(MenuBar)和一个工具 条控件(ToolBar);下方是一个状态栏控件(StatusBar),用来显示凭证数个当前日期。
大家可以看到图中所示的界面非常类似于Window95/98 的资源浏览器,Windows的界面风格做为一种标准已为广大用户所接受。而Windows操作系 统的主要的优点就是为所有的应用程序提供了公用的界面。知道如何使用基于Windows的 应用程序的用户,很容易学会使用其他应用程序。
这种使用Windows95公共控件组合的方法能够达 到与Windows界面的一致性,所以在目前VB5.0应用程序的开发中经常使用。
二、填充大量结果集所遇到的问题
在实际应用开发中,经常用ListView填充一个数据库 结果集(Record set)的内容。即先写一段SQL查询语句,产生一个结果集,然后将结果集的每一 条记录用DO...LOOP循环语句中填到ListView中。
但是当结果集很大时(例如有5000条以上的记录) ,填充所需要的时间会很长。用户不得不等很长时间完成一个查询。所以在查询的过程中 必须允许用户按Escape键退出。具体做法是在DO...LOOP循环体中加一条DoEvents函数,并 写一段中断退出程序代码。
DoEvents函数的功能是:转让控制权,以便让操 作系统处理其它的事件。这样在长时间的查询过程中,如果用户按了Escape键,将退出循环 体,结束查询过程。
但是这样又会引发另外一个问题:由于DoEvents 可以让操作系统响应别的事件,循环体中填充每一条ListView项目(ListItem)的过程也会显示出来,所以在填充的过程中屏幕会不停的闪动,这种现象当然不能被用户所接受。如何 解决这个问题呢?
三、解决方案
用WindowsAPI函数可以解决这个问题。首先对几个 用到的API函数做一解释和说明。
1.GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
此函数的功能是获得一个指定对象窗口(Window) 的矩型框区域(rectangle)。
Hwnd为指定对象或窗体的句柄。LpRect为返回矩 型框的结构(必须定义为结构类型的变量)。
2.ValidateRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
此函数的功能是使指定的矩型区域生效。这样会 通知Windows不必对指定的区域进行重画(Redraw)。
3.InvalidateRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT, ByVal bErase As Long) As Long
此函数的功能是使指定的矩型区域无效。这样会 通知Windows要对指定的区域进行重画。
具体实现的步骤如下:
1.在填充结果集之前先用GetClientRect函数获 得ListView的显示区域。
2.在增加完一个显示项目(ListItem)后用ValidateRect 函数置这一区域为有效。这样Windows就不会显示每一条ListItem,屏幕闪动的现象就会消 失。
3.在填充结果集之后,用InvalidateRect函数置 这一区域为无效。这样Windows就会重画ListView的内容,结果集被完整的显示出来。
下面是笔者在项目开发中的一个程序实例。程序 名为FillListView。该程序将填写一个Access数据库(FISCAL.MDB)的凭证表(Table)的内 容到ListView中。
首先进入VB5.0,新建一个窗体(Form),名为Form1。
然后在Form中增加下列控件。
控 件 名 Name
ListView Lvw
Imagelist imlList
Command Button。 Command1
将ImageList控件中充填一个名为“item”的图象后 与ListView控件关联。
在<<工程>>菜单命令条中进入“引用”对话框,选 择“MicrosoftDAOObjectLibrary”
在Form的通用模块(Modle)中定义以下变量。
Private Type RECT ' 用 来 定 义 一 个 区 域 的 坐 标。
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
' - -
' Windows API 函 数 的 声 明。
Private Declare Function InvalidateRect Lib "user32"
(ByVal hwnd As Long, lpRect As RECT, ByVal bErase As Long) As Long
Private Declare Function ValidateRect Lib "user32"
(ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function GetClientRect Lib "user32"
(ByVal hwnd As Long, lpRect As RECT) As Long
Dim mbSearchCancel As Boolean
' 用 来 定 义 查 询 中 断 的 标 志。
' True 表 示 中 止 查 询;False 表 示 正 在 查 询。
将 该Form 的KeyPreview 属 性 设 为True, 以 控 制 窗 体 接 收 键 盘 事 件。
然 后 在Form 的KeyPress 事 件 中 写 下 列 代 码:
If KeyAscii = vbKeyEscape Then
mbSearchCancel = True
' 当 用 户 按Escape 键 时, 置mbSearchCancel 变 量 为True。
End If
' 表 示 结 束 查 询。
在Command Button 的 Click 事 件 中 调 用 填 充 子 程 序:Call FillListView。
子 程 序 的 代 码 为:
Private Sub FillListView()
'
Dim itmX As ListItem ' 定 义 一 个ListView 的 显 示 项 目。
Dim sSQL As String ' 查 询 字 串 变 量 。
'
Dim rc As RECT ' ListView 的 显 示 区 域。
Dim wrkJet As Workspace ' 数 据 库 工 作 空 间。
Dim dbFISCAL As Database ' 数 据 库 对 象。
Dim RS As Recordset ' 数 据 结 果 集。
On Error GoTo ErrFillListView
Screen.MousePointer = vbHourglass
lvw.ListItems.Clear: ' 清 除ListView 的 内 容。
'
' 定 义ListView 的 列 头 的 名 称。
With lvw.ColumnHeaders
.Add , , " 凭 证 编 号", 800
.Add , , " 凭 证 日 期", 1000
.Add , , " 凭 证 字 号", 1000
.Add , , " 凭 证 类 别", 800
.Add , , " 首 行 摘 要", 1440
.Add , , " 借 方 金 额 合 计", 1000, lvwColumnRight
End With
' - - -
' 产 生 查 询 语 句。
sSQL = "select voucher_id,voucher_number,voucher_date,voucher_type_shortname,"
sSQL=sSQL&"voucher_type_name,voucher_memo,voucher_amount from VOUCHER"
sSQL = sSQL & "order by voucher_number"
' ' - - -
' 打 开 一 个 数 据 库 结 果 集。
Set wrkJet = CreateWorkspace("NewJetWorkspace", "admin", "", dbUseJet)
Set dbFISCAL = wrkJet.OpenDatabase("FISCAL.mdb")
Set RS=. dbFISCAL .Open sSQL,dbOpenForwardOnly
'
' 获 得listview 的 显 示 区 域。
Call GetClientRect(lvw.hwnd, rc)
Do While Not RS.EOF()
DoEvents
If mbSearchCancel Then
' 中 断 退 出
RS.Close: Set RS = Nothing ' 关 闭、 清 除 结 果 集。
mbSearchCancel = False
Screen.MousePointer = vbDefault
' - -
' 刷 新ListView 的 内 容, 显 示 已 经 查 出 的 记 录 数。
Call InvalidateRect(lvw.hwnd, rc, True)
Exit Sub
End If
' - - -
' 增 加 一 个 显 示 项 目ListItem。
With lvw.ListItems
Set itmX = .Add(, , "" & RS!voucher_number, "item", "item")
' 凭 证 编 号
itmX.SubItems(1) = Format$("" & RS!voucher_date, "yyyy/mm/dd")
' 凭 证 日 期
itmX.SubItems(2) = "" & RS!voucher_type_shortname & "-" —
' 凭 证 字 号
& "" & RS!voucher_number
itmX.SubItems(3)="" & RS!voucher_type_name
' 凭 证 类 别
itmX.SubItems(4)=""&RS!voucher_memo
' 首 行 摘 要
itmX.SubItems(5)= Format$("" & RS!voucher_amount, "#,###.00")
' 借 方 合 计 金 额
itmX.Tag = "" & RS!voucher_id
End With
' - -
' 避 免 显 示 区 域 的 闪 动 现 象。
Call ValidateRect(lvw.hwnd, rc)
RS.MoveNext
Loop
'- 刷 新ListView 的 内 容。 显 示 所 有 查 出 的 记 录 数。
Call InvalidateRect(lvw.hwnd, rc, True)
' -
' 关 闭、 清 除 结 果 集。
RS.Close: Set RS = Nothing
creen.MousePointer = vbDefault
Exit Sub
ErrFillListView:
Screen.MousePointer = vbDefault
MsgBox Err & ":" & Error, vbInformation, Me.Caption
Exit Sub
End Sub
编 写 完 毕 后 按F5 执 行 该 程 序, 用 鼠 标 点 击CommandButton, 将 开 始 查 询 并 填 写 凭 证 的 内 容 到ListView 中 去。
关 于ListView 本 文 只 是 描 述 了 它 如 何 填 充 大 量 结 果 集 的 方 法, 它 还 有 很 多 特 性(property) 和 方 法(method), 利 用 它 们 可 以 达 到 更 完 美 的 显 示 效 果, 有 兴 趣 的 读 者 可 以 进 一 步 研 究。 不 管 是 开 发 什 么 样 的 应 用 程 序, 只 有 坚 持 面 向 用 户、 方 便 用 户 的 原 则, 这 样 的 软 件 才 具 有 强 大 的 生 命 力。
|