Загрузка ListView и ImageList при использовании backgroundWorker
Загрузка списка изображений и просмотра списка, очевидно, заставляет пользовательский интерфейс немного зависать. Поэтому я хотел бы загрузить пользовательский интерфейс и сообщить пользователю, что мы работаем над вещами, и позволить backgroundWorker сделать это. Я попытался в formLoad вызвать "backgroundWorker1.RunWorkerAsync();"
Но я получил сообщение "Недопустимая операция между потоками: доступ к элементу управления listView1 осуществляется из потока, отличного от потока, в котором он был создан".
Я немного покопался и понял, что мне нужно немного изменить код. Итак, вот что у меня есть:
private void WatermarkPicker_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void GetImages()
{
DirectoryInfo dir = new DirectoryInfo(@"c:\pics");
this.listView1.View = View.LargeIcon;
this.imageList1.ImageSize = new Size(100, 100);
if (listView1.InvokeRequired)
{
listView1.Invoke(new MethodInvoker(
() => this.listView1.LargeImageList = this.imageList1));
}
else
{
this.listView1.LargeImageList = this.imageList1;
}
int j = 0;
foreach (FileInfo file in dir.GetFiles())
{
try
{
//this.imageList1.Images.Add(Image.FromFile(file.FullName));
imageList1.Images.Add(file.Name, Image.FromFile(file.FullName));
ListViewItem item = new ListViewItem(file.Name);
item.Tag = file.Name;
item.ImageIndex = j;
this.listView1.Items.Add(item);
j++;
}
catch
{
Console.WriteLine("This is not an image file");
}
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
GetCurrentLogos();
GetImages();
}
If (listView1.InvokeRequired) выполняется и выполняет listView1.invoke. Но ничего не отображается в listView в пользовательском интерфейсе. Никаких ошибок, хотя.
3 ответа
Вы должны переместить весь код, который пытается получить доступ к Listview И ImageList, за пределы события DoWork и использовать событие ProgressChanged, возникшее внутри кода DoWork.
Таким образом, код внутри события ProgressChanged выполняется в правильном потоке пользовательского интерфейса (при условии, что BackgroundWorker создан в потоке пользовательского интерфейса).
Например:
private void WatermarkPicker_Load(object sender, EventArgs e)
{
listView1.View = View.LargeIcon;
listView1.LargeImageList = imageList1;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Do not try to use in any way an object derived from Control
// like the ListView when you are inside the DoWork method.....
BackgroundWorker worker = sender as BackgroundWorker;
DirectoryInfo dir = new DirectoryInfo(@"c:\pics");
foreach (FileInfo file in dir.GetFiles())
{
// You can't use imageList here
// imageList1.Images.Add(file.Name, Image.FromFile(file.FullName));
// Raise the ProgressChanged event.
// The code there will execute in the UI Thread
worker.ReportProgress(1, file);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// This code executes in the UI thread, no problem to
// work with Controls like the ListView
try
{
FileInfo file = e.UserState as FileInfo;
imageList1.Images.Add(file.Name, Image.FromFile(file.FullName));
ListViewItem item = new ListViewItem(file.Name);
item.Tag = file.Name;
item.ImageIndex = imageList1.Images.Count - 1;
listView1.Items.Add(item);
}
catch(Exception ex)
{
....
}
}
Вы можете выполнять то, что вы хотите, в фоновом режиме, как вы должны делать в главном потоке. Но каждый раз, когда вы хотите получить доступ к компоненту пользовательского интерфейса, вы должны делать это в основном потоке (также называемом потоком пользовательского интерфейса). Синтаксис зависит от того, работаете ли вы под WPF, Windows Phone, Xamarin и т. Д.
С WPF у вас должно получиться что-то вроде этого:
Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));
Вы должны получить доступ ListView
а также ImageList
из потока пользовательского интерфейса не фонового рабочего потока.
Я предлагаю вам переместить логику инициализации в WatermarkPicker_Load
который работает в потоке пользовательского интерфейса, а затем изменить foreach
цикл для использования Control.Invoke
,
foreach (var file in dir.GetFiles())
{
var image = Image.FromFile(file.FullName);
var item = new ListViewItem(file.Name)
{
Tag = file.Name;
}
listView1.Invoke(() =>
{
imageList1.Images.Add(file.Name, image);
listView1.Items.Add(item);
});
}