Preventing ListBox scrolling to top when updated

前端 未结 3 1332
被撕碎了的回忆
被撕碎了的回忆 2020-12-21 02:05

I\'m trying to build a simple music player with a ListBox playlist. When adding audio files to the playlist, it first fills the ListBox with the filenames and then (on a sep

相关标签:
3条回答
  • 2020-12-21 02:22

    Nice repro code, I had no trouble diagnosing the source of the problem. It is a feature, not a bug. Press the arrow down key several times, then scroll the list. Note that it doesn't jump back now.

    What's going on here is that list box automatically scrolls the item with the focus back into view when it gets updated. This is normally desirable behavior, you cannot turn it off. Workarounds, like selecting the item you're updating, isn't going to be pretty when you update the list like this, it is going to flicker badly. Maybe virtual mode, I didn't try it.

    ListView doesn't have this behavior, consider using it instead. Use View = List or Details.

    0 讨论(0)
  • 2020-12-21 02:23

    It might be prudent to disable any painting of the Listbox when doing the updates. Use ListBox.BeginUpdate() and then update the entries, when done, invoke ListBox.EndUpdate(). This should ensure that no refreshes occurs during the update.

    You should also ensure that you do not refresh the ListBox from a thread either as cross-threading is dis-allowed, and the runtime will barf with a message saying that 'Cross Threading Exception' has occurred.

    Use the ListBox's BeginInvoke(...) method to get around the cross threading issue. See here on the MSDN on how this is done.

    0 讨论(0)
  • 2020-12-21 02:33

    Yes, .BeginUpdate() is a good solution:

    Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Timer1.Start()
    
    End Sub
    Dim ix As Integer
    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    
        ix = ListBox1.TopIndex
        Label1.Text = ListBox1.TopIndex     'get most top displayed in the listbox
        ListBox1.BeginUpdate()              'do not refresh during update
        ListBox1.Items.Clear()              'do updates
        For i = 1 To 100
            ListBox1.Items.Add(i.ToString & "." & Int((101) * Rnd()).ToString)
        Next
        ListBox1.TopIndex = ix              'set the top item to be displayed
        ListBox1.EndUpdate()                'refresh the listbox
    
    End Sub
    
    Private Sub VScrollBar1_Scroll(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles VScrollBar1.Scroll
        VScrollBar1.Maximum = ListBox1.Items.Count
        ListBox1.TopIndex = VScrollBar1.Value       ' link another scrollbar if you want
        Label2.Text = VScrollBar1.Value
    
    End Sub
    

    End Class

    The code speaks by himself. A timer is generating random number. The numbers are populated in the List Box. Before the update just read the current Top Index of the List Box. During the update switch off the rendering by Begin Update. Then restore the Top Index and call End Update.

    I put an extra Scroll Bar, linked to the List Box.

    0 讨论(0)
提交回复
热议问题