Предотвращение прокрутки ListBox вверх при обновлении - PullRequest
4 голосов
/ 22 мая 2010

Я пытаюсь создать простой музыкальный проигрыватель со списком воспроизведения ListBox. При добавлении аудиофайлов в список воспроизведения он сначала заполняет ListBox именами файлов, а затем (в отдельном потоке) извлекает данные ID3 и перезаписывает имена файлов правильной информацией об исполнителе - названии (во многом аналогично Winamp).

Но пока обновляется ListBox, он не прокручивается, так как он всегда переходит наверх при каждом перезаписи элемента.

Есть ли способ предотвратить это?

EDIT:
Код:

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:
Намного проще тестовый пример:
Попробуйте прокрутить вниз, не выбирая элемент. Изменяющийся список будет снова прокручиваться вверх.

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

Ответы [ 2 ]

4 голосов
/ 23 мая 2010

Хороший код для репро, у меня не было проблем с диагностикой источника проблемы. Это особенность, а не ошибка. Нажмите клавишу со стрелкой вниз несколько раз, затем прокрутите список. Обратите внимание, что теперь он не возвращается назад.

Здесь происходит то, что список автоматически прокручивает элемент с фокусом, когда он обновляется. Это обычно желательное поведение, вы не можете отключить его. Обходные пути, такие как выбор элемента, который вы обновляете, не будут красивыми, когда вы обновляете список, как этот, он будет мерцать. Может быть, виртуальный режим, я не пробовал.

ListView не имеет такого поведения, рассмотрите возможность его использования. Используйте Вид = Список или Детали.

1 голос
/ 22 мая 2010

Возможно, было бы разумно отключить любой рисунок Listbox при выполнении обновлений.Используйте ListBox .BeginUpdate(), а затем обновите записи, когда закончите, вызовите ListBox .EndUpdate().Это должно гарантировать, что во время обновления не происходит никаких обновлений.

Вы также должны убедиться, что вы не обновляете ListBox из потока, так как перекрестная многопоточность запрещена, и во время выполнения появится сообщение с надписьючто произошло «исключение перекрестной потоков».

Используйте метод ListBox BeginInvoke(...), чтобы обойти проблему перекрестной потоковой обработки.См. здесь на MSDN о том, как это сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...