更新后阻止ListBox滚动到顶部

我正在尝试使用ListBox播放列表构建一个简单的音乐播放器。 将音频文件添加到播放列表时,它首先使用文件名填充ListBox,然后(在单独的线程上)提取ID3数据并使用正确的艺术家 – 标题信息覆盖文件名(非常类似于Winamp)。

但是当ListBox正在更新时,它是不可滚动的,因为它总是跳到每个项覆盖的顶部。

有什么办法可以防止这个?

编辑:
代码:

public Form1() { //Some initialization code omitted here BindingList trackList = new BindingList(); // The Playlist this.playlist = new System.Windows.Forms.ListBox(); this.playlist.Location = new System.Drawing.Point(12, 12); this.playlist.Name = "playlist"; this.playlist.Size = new System.Drawing.Size(229, 316); this.playlist.DataSource = trackList; } private void playlist_add_Click(object sender, EventArgs e) { //Initialize OpenFileDialog OpenFileDialog opd = new OpenFileDialog(); opd.Filter = "Music (*.WAV; *.MP3; *.FLAC)|*.WAV;*.MP3;*.FLAC|All files (*.*)|*.*"; opd.Title = "Select Music"; opd.Multiselect = true; //Open OpenFileDialog if (DialogResult.OK == opd.ShowDialog()) { //Add opened files to playlist for (int i = 0; opd.FileNames.Length > i; ++i) { if (File.Exists(opd.FileNames[i])) { trackList.Add(new TAG_INFO(opd.FileNames[i])); } } //Initialize BackgroundWorker BackgroundWorker _bw = new BackgroundWorker(); _bw.WorkerReportsProgress = true; _bw.DoWork += new DoWorkEventHandler(thread_trackparser_DoWork); _bw.ProgressChanged += new ProgressChangedEventHandler(_bw_ProgressChanged); //Start ID3 extraction _bw.RunWorkerAsync(); } } void thread_trackparser_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker _bw = sender as BackgroundWorker; for (int i = 0; i < trackList.Count; ++i) { //Pass extracted tag info to _bw_ProgressChanged for thread-safe playlist entry update _bw.ReportProgress(0,new object[2] {i, BassTags.BASS_TAG_GetFromFile(trackList[i].filename)}); } } void _bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { object[] unboxed = e.UserState as object[]; trackList[(int)unboxed[0]] = (unboxed[1] as TAG_INFO); } 

EDIT2:
更简单的测试用例:
尝试向下滚动而不选择项目。 更改ListBox将再次滚动到顶部。

 using System; using System.Windows.Forms; namespace WindowsFormsApplication1 { public class Form1 : Form { private System.ComponentModel.IContainer components = null; protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.listBox1 = new System.Windows.Forms.ListBox(); this.timer1 = new System.Windows.Forms.Timer(this.components); this.SuspendLayout(); // listBox1 this.listBox1.FormattingEnabled = true; this.listBox1.Location = new System.Drawing.Point(0, 0); this.listBox1.Name = "listBox1"; this.listBox1.Size = new System.Drawing.Size(200, 290); // timer1 this.timer1.Enabled = true; this.timer1.Tick += new System.EventHandler(this.timer1_Tick); // Form1 this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(200, 290); this.Controls.Add(this.listBox1); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false); } private System.Windows.Forms.ListBox listBox1; private System.Windows.Forms.Timer timer1; public Form1() { InitializeComponent(); for (int i = 0; i  44) tickCounter = 0; listBox1.Items[tickCounter] = ((int)listBox1.Items[tickCounter])+1; } } static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } } 

好的repro代码,我没有诊断问题的根源。 这是一个function,而不是一个bug。 按几次向下箭头键,然后滚动列表。 请注意,它现在不会跳回来。

这里发生的是列表框自动滚动项目,焦点在更新后重新进入视图。 这通常是理想的行为,你无法将其关闭。 解决方法就像选择你正在更新的项目一样,当你像这样更新列表时,它会变得很漂亮,它会闪烁得很厉害。 也许是虚拟模式,我没试过。

ListView没有此行为,请考虑使用它。 使用View = List或Details。

在进行更新时禁用任何绘图列表框可能是谨慎的。 使用ListBox .BeginUpdate()然后更新条目,完成后,调用ListBox .EndUpdate() 。 这应确保在更新期间不会刷新。

您还应该确保不会从线程刷新ListBox,因为交叉线程被禁止,并且运行时将使用消息说明发生了“交叉线程exception”。

使用ListBox的BeginInvoke(...)方法来解决交叉线程问题。 请参阅MSDN,了解如何完成此操作。