c# - 防止列表框在更新时滚动到顶部

标签 c# winforms listbox

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

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

有什么办法可以防止这种情况?

编辑:
代码:

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);
}

编辑 2:
更简单的测试用例:
尝试向下滚动而不选择项目。变化的 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 < 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());
        }
    }
}

最佳答案

很好的重现代码,我可以毫不费力地诊断出问题的根源。这是一个功能,而不是错误。按几次向下箭头键,然后滚动列表。请注意,它现在不会跳回。

这里发生的事情是列表框在更新时自动将具有焦点的项目滚动回 View 。这通常是理想的行为,您无法将其关闭。变通方法,例如选择您正在更新的项目,在您像这样更新列表时不会很漂亮,它会严重闪烁。可能是虚拟模式,我没试过。

ListView 没有这种行为,请考虑改用它。使用 View = 列表或详细信息。

关于c# - 防止列表框在更新时滚动到顶部,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2889172/

相关文章:

c# - 定义应使用多个节点创建哪个节点索引

c# - WCF 反序列化返回 0 个列表元素

c# - Linq orderby 没有按预期工作

c# - 向上或向下移动树中的节点

c# - 列表框的实时过滤

wpf - 删除列表框周围的多余空间

c# - 使用 TCP 协议(protocol)的套接字响应太慢(21 秒)

.net - 如何在表单及其嵌套面板中查找标签控件?

c# - 为什么调用 ClearSelected 并附加数据源后 SelectedIndex 可能为 0?

c# - 2 x 10 随机生成的数字具有相同的总和 C#