|
楼主 |
发表于 2018-7-14 20:54
|
显示全部楼层
7.高级:Listview动态加载查询数据的实现
在模糊查询数据时,随着每一次在TEXTBOX控件中的输入,数据都源源不断地的显示在Listview控件中,当我们输入的查询关键字还很少时,Listview显示的数据可能会非常的多,数千上万条是正常的。显示这么多数据,需要非常大的计算资源,而实际情况是,我们不可能从这成千上万的数据中去找我们想要的内容,我们会不断的输入可能的关键字信息,逐步缩小搜索结果,最后可能只会剩下几条数据供我们查看。如果我们在查询过程中,在TextBox控件的Change事件中,每次只输出20条或者50条数据,这样效率会明显地提高很多倍。
但,这也带来一个问题——如果我们只记得部分关键字,在我们停止输入关键字时,我们看到的查询结果是否已经完整?假设我们只呈现20条数据,查询速度是快了,但是我们也无法保证查询结果是完整的吧!有没有一种可能,我们每次都只呈现数据是20条,这样,在查询过程中,可以减少很多倍的输出量,如果用户确有需求,通过滚动鼠标中键,或者拖动滚动条,能逐步的把剩下的数据呈现出来?答案是肯定的。我们只要替换Listview的窗口函数,拦截系统发送过来的鼠标滚动消息和滚动条消息,就可以动态地加载数据,而不会担心数据遗漏。
在窗体初始时时,使用GetWindowLong获取Listview原有的窗口函数的地址,用SetWindowLong设置成我们自定义的窗口函数,初始化代码如下。
Private Sub UserForm_Initialize()
Dim i As Long, n As Long
arrData = Range("a1").CurrentRegion
If IsEmpty(arrData) Then Exit Sub
With ListView1
.Gridlines = True
.FullRowSelect = True
.LabelEdit = lvwManual
.SmallIcons = ImageList1
.View = lvwReport
.Font.Size = 12
For i = 1 To UBound(arrData, 2)
.ColumnHeaders.Add , , arrData(1, i), 100
Next
AddListItems ListView1, 2, 20 '初始化时加载20条数据,如有的话
LvmPreWndProc = GetWindowLong(.hwnd, GWL_WNDPROC)
SetWindowLong .hwnd, GWL_WNDPROC, AddressOf WndProc
End With
End Sub
自定义的窗口函数WndProc的代码在标准模块中,而不能在窗体中,因为Addressof运算符无法用于类模块中的函数。Addressof返回函数指针,它只对编译时地址确定的函数有效,而类成员函数编译时地址无法确定,而是由用户实例化时动态生成的,因此无法使用。自定义的窗口函数只拦截需要的消息,并做适当的处理,然后把消息还给原窗口函数,让其按原有方式处理消息。因为这些API函数的使用和理解都很简单,我就不一一解释了。
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function GetScrollPos Lib "user32" (ByVal hwnd As Long, ByVal nBar As Long) As Long
Public Declare Function GetScrollRange Lib "user32" (ByVal hwnd As Long, ByVal nBar As Long, lpMinPos As Long, lpMaxPos As Long) As Long
Public Const SB_VERT = 1
Public Const WM_VSCROLL = &H115
Public Const WM_MOUSEWHEEL = &H20A
Public Const GWL_WNDPROC = (-4)
Public LvmPreWndProc As Long
Public arrData, lngRowIndex As Long
Public Function WndProc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim lngMinPos As Long, lngMaxPos As Long
With UserForm1
Select Case Msg
Case WM_VSCROLL
GetScrollRange hwnd, SB_VERT, lngMinPos, lngMaxPos
If GetScrollPos(hwnd, SB_VERT) > lngMaxPos - 200 Then
If lngRowIndex <= UBound(arrData) Then
.AddListItems .ListView1, lngRowIndex, 1
End If
End If
Case WM_MOUSEWHEEL
If wParam = &HFF880000 Then
If lngRowIndex <= UBound(arrData) Then
.AddListItems .ListView1, lngRowIndex, 1
End If
End If
End Select
End With
WndProc = CallWindowProc(LvmPreWndProc, hwnd, Msg, wParam, lParam)
End Function
其中,WM_VSCROLL是拖动垂直滚动条消息,WM_MOUSEWHEEL是鼠标滚动建消息,参数wParam = &HFF880000是向下滚动。AddListItems是在窗体中定义的过程,用来往Listview中添加数据,接受3个参数,第一个参数是Listview控件,第二个参数是一个记录上次添加数据结束后数组中位置的长整数,传递这个参数时,数据就从这个位置开始加载,而不会重复加载前面已经加载过的数据,第三个参数是加载数据的条数,这里指定每接受到一次鼠标向下滚动键或拖动Listview垂直滚动条就加载一条数据,其代码如下:
Public Sub AddListItems(lv As ListView, ByVal lngIdx As Long, lngCount As Long)
Dim i As Long, j As Long, n As Long
Dim lstitem As ListItem, forecolor As Long
Dim strKey As String
If IsEmpty(arrData) Then Exit Sub
If lngIdx < LBound(arrData) Or lngIdx > UBound(arrData) Then Exit Sub
If lngCount < 1 Then lngCount = UBound(arrData) '小于1则加载全部
With lv
For i = lngIdx To UBound(arrData)
strKey = arrData(i, 3) & "/" & arrData(i, 4) & "/" & arrData(i, 5)
If InStr(strKey, UCase(TextBox1)) Then
n = n + 1
If n > lngCount Then Exit For
Set lstitem = .ListItems.Add
lstitem.Text = arrData(i, 1)
' forecolor = IIf(lstitem.Index Mod 2, vbRed, vbBlue)
' lstitem.forecolor = forecolor
For j = 2 To UBound(arrData, 2)
lstitem.SubItems(j - 1) = arrData(i, j)
' lstitem.ListSubItems(j - 1).forecolor = forecolor
Next
End If
Next
If i > UBound(arrData) Then lngRowIndex = i Else lngRowIndex = i + 1
End With
End Sub
在查询框的Change事件中的代码如下,每次只加载满足条件的20条数据,极大的减少了数据的输出量:
Private Sub TextBox1_Change()
ListView1.ListItems.Clear
AddListItems ListView1, 2, 20
End Sub
|
评分
-
2
查看全部评分
-
|