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 separate thread) extracts the ID3 data and overwrites the filenames with the correct Artist - Title information (much like Winamp).
But while the ListBox is being updated, it's unscrollable, as it always jumps to the top on every item overwrite.
Any way to prevent this?
EDIT:
The code:
public Form1()
{
//Some initialization code omitted here
BindingList<TAG_INFO> trackList = new BindingList<TAG_INFO>();
// 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:
Much simpler test case:
Try scrolling down without selecting an item. The changing ListBox will scroll to the top again.
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 < 45; i++)
listBox1.Items.Add(i);
}
int tickCounter = -1;
private void timer1_Tick(object sender, EventArgs e)
{
if (++tickCounter > 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());
}
}
}