当前位置:编程学习 > VB >>

更新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 ,  控件
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,