更新treeview控件节点时的闪烁问题
设计要求树中节点内容是动态变化的。所以程序在Timer1中会用到以下两句:TreeView1.Nodes(i).Image = 1
TreeView1.Nodes(i).Text = "aaa"
每次更新都会引起整个控件闪烁一下,有什么办法只让该节点的内容刷新,而不让整个控件刷新,避免闪烁。
本想结合ValidateRect和InvalidateRect来做的,可是又没办法获得该节点的坐标。 --------------------编程问答-------------------- 你不是刷新一次,而是 n 多次吧。
在循环外将整个 TreeView 的刷新禁止,循环结束后再一次性刷新。 --------------------编程问答-------------------- 不是循环刷新的意思。而是动态变化的,节点的内容通过串口通信得到(随机变化的),如果节点的内容与接收到的内容,就得跟着变。 --------------------编程问答-------------------- 不是循环刷新的意思。而是动态变化的,节点的内容通过串口通信得到(随机变化的),如果节点的内容与接收到的内容不一样,就得跟着变。
--------------------编程问答-------------------- 将一定时间(比如1秒)内的变化缓存起来,一次性更新,这样就不会有闪烁了。 --------------------编程问答-------------------- 一秒更新一次,那还是要一秒种闪一次的呀?时间太长了又达不到设计要求了。
每次赋值,整个树都会先擦除,再重绘,从而闪烁。而我仅仅只是改变了一个节点的image和text属性,没必要整个树重绘吧,是我没用好treeview吗? --------------------编程问答-------------------- 这是我每秒对 10000 个节点进行更新,也没有闪烁
Private Sub Timer1_Timer()
Static b As Boolean
Dim nd As Node
b = Not b
For Each nd In TreeView1.Nodes
nd.Image = IIf(b, 1, 2)
nd.Text = Left$(nd.Text, 1) & IIf(b, "1", "0")
Next
End Sub
--------------------编程问答-------------------- 感谢Tiger_Zhao提供的代码,跟我差不多。
用你的代码试了,可是确实是闪的呀。你仔细感觉就能发现,它其实是先擦除再更新的。
可能所有节点都在变,你感觉不出闪的现象了。
我只需要更改节点1的状态,这样闪的状态会更明显,
用以下代码你试一下:
Private Sub Timer1_Timer() '1秒一次
Static b As Boolean
Dim nd As Node
b = Not b
Set nd = TreeView1.Nodes(1)
nd.Image = IIf(b, 1, 2)
nd.Text = left$(nd.Text, 1) & IIf(b, "1", "0")
End Sub
--------------------编程问答-------------------- treeview1.visible=false
treeview1.visible=true
--------------------编程问答-------------------- HOHO..你太注重细节了..我发个绘目录结果的代码..嘿嘿:
'第二次真正列一个只列了一个子目录的目录时会提示key的唯一性问题
'用它跳过这个跳(我们知道的正常的错):
On Error Resume Next
'将选定位置的图片加入Lst
Dim flecount As Long
Dim i As Long, j As Long, tmpEndN1 As Long, tmpEndN2 As Long
Dim TmpStr As String, DirGetMsg As String, DirNextDirMsg As String
Dim LstItm As ListItem, NowAddedDir() As String
'选定的每一个目录都要列举。定义动态数组,动态记录点过的目录,列过了就不列了。
tmpEndN1 = UBound(DirRecordCache())
ReDim Preserve NowAddedDir(0) As String '初始化监时数组。
For i = 0 To tmpEndN1
If InStr(DirRecordCache(i), TreeLst_NetPC.SelectedItem.Key) <> 0 Then Exit For
Next
If i > tmpEndN1 Then '一级缓存中没有访问痕迹
tmpEndN2 = UBound(DirRecord())
For i = tmpEndN2 To 0 Step -1
If InStr(DirRecord(i), TreeLst_NetPC.SelectedItem.Key) <> 0 Then Exit For
Next i
End If
If i < 0 Then '没有被访问记录,需要列举
'列举当前选中目录(盘符)下的一级子目录
'并记录在当选择目录
DirRecordCache(tmpEndN1) = TreeLst_NetPC.SelectedItem.Key
DirRecord(tmpEndN2) = TreeLst_NetPC.SelectedItem.Key
ReDim Preserve DirRecord(tmpEndN2 + 1) As String
If (tmpEndN1 + 1) <= 21 Then
ReDim Preserve DirRecordCache(tmpEndN1 + 1) As String
Else
ReDim Preserve DirRecordCache(0) As String
DirRecordCache(0) = ""
End If
'用dir()函数列出该机共享目录下的所有打印机录
DirGetMsg = Dir(CheckPath(TreeLst_NetPC.SelectedItem.Key), vbHidden + vbReadOnly + vbDirectory) ' 找寻第一项。
Do While DirGetMsg <> "" ' 开始循环。
' 跳过当前的目录及上层目录。
If DirGetMsg <> "." And DirGetMsg <> ".." Then
' 使用位比较来确定 MyName 代表一目录。
If (GetAttr(CheckPath(TreeLst_NetPC.SelectedItem.Key) & DirGetMsg) And vbDirectory) = vbDirectory Then
' 如果它是一个目录,将其名称显示出来。
For i = 0 To UBound(NowAddedDir())
If InStr(NowAddedDir(i), CheckPath(TreeLst_NetPC.SelectedItem.Key) & DirGetMsg) <> 0 Then
Exit For
End If
Next i
If i > UBound(NowAddedDir()) Then
' 如果它是一个目录,将其名称显示出来。
Me.TreeLst_NetPC.Nodes.Add TreeLst_NetPC.SelectedItem.Key, tvwChild, CheckPath(TreeLst_NetPC.SelectedItem.Key) & DirGetMsg, DirGetMsg
'让有下级目录的显示+号。列一个出来。省时间
DirNextDirMsg = Dir(CheckPath(CheckPath(TreeLst_NetPC.SelectedItem.Key) & DirGetMsg), vbHidden + vbReadOnly + vbDirectory)
ReDim Preserve NowAddedDir(j) As String
NowAddedDir(j) = CheckPath(TreeLst_NetPC.SelectedItem.Key) & DirGetMsg
j = j + 1
Do While DirNextDirMsg <> ""
If DirNextDirMsg <> "." And DirNextDirMsg <> ".." Then
' 使用位比较来确定 MyName 代表一目录。
If (GetAttr(CheckPath(CheckPath(TreeLst_NetPC.SelectedItem.Key) & DirGetMsg) & DirNextDirMsg) And vbDirectory) = vbDirectory Then
' 如果它是一个目录,将其名称显示出来。
Me.TreeLst_NetPC.Nodes.Add CheckPath(TreeLst_NetPC.SelectedItem.Key) & DirGetMsg, tvwChild, CheckPath(CheckPath(TreeLst_NetPC.SelectedItem.Key) & DirGetMsg) & DirNextDirMsg, DirNextDirMsg
DirGetMsg = Dir(CheckPath(TreeLst_NetPC.SelectedItem.Key), vbHidden + vbReadOnly + vbDirectory) ' 找寻第一项。
Exit Do
End If
End If
DirNextDirMsg = Dir ' 查找下一个目录。
If DirNextDirMsg = "" Then
DirGetMsg = Dir(CheckPath(TreeLst_NetPC.SelectedItem.Key), vbHidden + vbReadOnly + vbDirectory) ' 找寻第一项。
End If
Loop
End If
End If
End If
DirGetMsg = Dir ' 查找下一个目录。
Loop
End If
'Me.Text2.Text = CheckPath(Me.TreeLst_NetPC.SelectedItem.Key)
直接用会有好些问题..HOHO.. --------------------编程问答-------------------- ykwang,这样更闪,一关一开本身就是一个闪烁。
--------------------编程问答-------------------- rjzhangjun(RJ),你的程序不适合我,我不是要建树或是往树中添加节点。
树已建好,只是节点上显示的图像和文字要变化。
如果只是变化image,我改成这样倒是可以忍受:
Private Sub Timer1_Timer() '1秒一次
Static b As Boolean
Dim nd As Node
b = Not b
Set nd = TreeView1.Nodes(1)
nd.Image = IIf(b, 1, 2)
'nd.Text = left$(nd.Text, 1) & IIf(b, "1", "0")
ValidateRectBynum TreeView1.hWnd, 0
InvalidateRecBynum TreeView1.hWnd, 0, 0
UpdateWindow TreeView1.hwnd
End Sub
但如果变化的是Text属性,就不行了:
Private Sub Timer1_Timer() '1秒一次
Static b As Boolean
Dim nd As Node
b = Not b
Set nd = TreeView1.Nodes(1)
'nd.Image = IIf(b, 1, 2)
nd.Text = left$(nd.Text, 1) & IIf(b, "1", "0")
ValidateRectBynum TreeView1.hWnd, 0
InvalidateRecBynum TreeView1.hWnd, 0, 0
UpdateWindow TreeView1.hwnd
End Sub
为什么ValidateRectBynum会来不及屏蔽text属性的变化呢?
欢迎大家继续探讨。
--------------------编程问答-------------------- 经调试发现:
赋值image属性,会向控件发WM_PAINT消息(整棵树无效,都要重绘);如果无效区域只是图片范围就好了。但是无法知晓图片的坐标位置,上面的程序我把无效区域改成了整个显示区域,效果上已经好了不少。
赋值text属性,会向控件发WM_SETREDRAW消息。水平有限,这个苦思冥想都想不出解决办法了。 --------------------编程问答-------------------- 方法一: LockWindowUpdate treeview.hwnd/LockWindowUpdate 0, 可以减轻闪烁.
方法二: 在WM_PAINT消息内利用BeginPaint/EndPaint获得无效区域的hdc及rcRect,进行更新。(ValidateRect/InvalidateRect本身不能确定无效区域)
补充:VB , 控件