Reputation: 813
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
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
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
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