Crouzilles
Crouzilles

Reputation: 813

Cannot assign returned ListView from backgroundworker

I have a listview which so far has worked fine. The problem I encountered was that sometimes it would take a few seconds to populate as I was looking at adding many 1000s of items.

So I decided to populate the ListView with a BackgroundWorker, but the problem I encountered was that I could not use a control from the UI thread on the BackgroundWorker thread.

To solve that problem I wrote the code you see below. I know the returned ListView is full of items, but when I assign it to my ListView component in my form, nothing is displayed and I do not know why.

Please help me before I start slapping myself :)

public ListView listImages(string path, BackgroundWorker worker)
{
    ListView lv = new ListView();
    lv.Items.Clear();
    int count = 0;
    int total = 0;
    string[] extensions = new string[] { ".jpg", ".JPG", ".png", ".PNG", ".bmp", ".BMP" };
    lv.Columns.Add("Filename", 300);
    lv.Columns.Add("Date taken", 150);
    lv.Columns.Add("Size", 150);
    DirectoryInfo dir = new DirectoryInfo(path);
    IEnumerable<FileInfo> files = dir.EnumerateFiles("*.*", SearchOption.AllDirectories).Where(f => extensions.Contains(f.Extension));
    total = files.Count();
    foreach (FileInfo f in files)
    {
        DateTime dt = File.GetCreationTime(Path.Combine(f.Directory.ToString(), f.Name));
        string fsize = FormatBytes(f.Length);

        count++;
        ListViewItem lSingleItem = lv.Items.Add(f.Name);

        lSingleItem.SubItems.Add(dt.ToString("dd MMMM yyyy"));
        lSingleItem.SubItems.Add(fsize);
        lSingleItem.Tag = Path.Combine(f.Directory.ToString(), f.Name);


        worker.ReportProgress((count * 100) / total);
    }
    return lv;
}

private void buttonLeftBrowse_Click(object sender, EventArgs e)
{
    if (_bwListImages.IsBusy != true)
    {
        pgf = new ProgressBarForm();

        if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
        {
            string path = folderBrowserDialog.SelectedPath;
            this.textBoxLeftFolder.Text = path;
            object[] parameters = new object[] { path };

            _bwListImages.RunWorkerAsync(parameters);
            pgf.ShowDialog();
       }
    }
}

private void _bwListImages_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    object[] parameters = e.Argument as object[];
    resetViewsColor();
    disableButtons();

    e.Result = core.listImages((string)parameters[0], worker);

    checkButtons();
}

private void _bwListImages_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    pgf.progressBar.Value = e.ProgressPercentage;
}

private void _bwListImages_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    listViewLeft = (ListView)e.Result;
    pgf.Close();
}

Thank you Crouz

Upvotes: 0

Views: 216

Answers (3)

Panduranga Rao Sadhu
Panduranga Rao Sadhu

Reputation: 497

As Rotem pointed, you can do that by raising the event in the worker thread (Say AssignListview) and then you can do delegate the call to actual assignment by the following way this.BeginInvoke((MethodInvoker)delegate { ActualAssignmentFunction(); });

ActualAssignmentFunction(); gets executed in the UI thread.

Upvotes: 0

Arsen Mkrtchyan
Arsen Mkrtchyan

Reputation: 50712

what is listViewLeft ? I guess it is ListView? you should assign listViewLeft.ItemsSource in case of wpf or listViewLeft.DataSource in case of winform, but not ListView

Upvotes: 0

Rotem
Rotem

Reputation: 21917

I suspect the problem may be that your are creating the ListView in a background thread, making that the effective UI thread for the ListView.

Perhaps use the background thread to return only the list of items, and assign them to the ListView in the UI thread.

Your backgroundworker work method should return a List<string> of the filenames, and in the backgroundworker completed event you should use ListView.Items.Addrange to add the filenames to the ListView

Upvotes: 1

Related Questions