问题描述
我正在尝试使用 ListBox 播放列表构建一个简单的音乐播放器.将音频文件添加到播放列表时,它首先用文件名填充 ListBox,然后(在单独的线程上)提取 ID3 数据并用正确的艺术家 - 标题信息覆盖文件名(很像 Winamp).
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).
但是当 ListBox 被更新时,它是不可滚动的,因为它总是在每次覆盖时跳到顶部.
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?
代码:
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);
}
更简单的测试用例:
尝试向下滚动而不选择项目.变化中的 ListBox 将再次滚动到顶部.
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());
}
}
}
推荐答案
很好的重现代码,我可以毫无问题地诊断出问题的根源.这是一个功能,而不是一个错误.按几次向下箭头键,然后滚动列表.请注意,它现在不会跳回.
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 没有这种行为,请考虑改用它.使用 View = List 或 Details.
ListView doesn't have this behavior, consider using it instead. Use View = List or Details.
这篇关于更新时防止列表框滚动到顶部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!