使用 backgroundWorker 加载 ListView 和 ImageList
Loading ListView and ImageList while using a backgroundWorker
加载图像列表和列表视图显然会导致 UI 稍微挂起。所以我想加载 UI 并让用户知道我们正在处理事情并让 backgroundWorker 做这件事。我尝试在 formLoad 上调用 "backgroundWorker1.RunWorkerAsync();"
但是我得到了"Cross-thread operation not valid: Control 'listView1' accessed from a thread other than the thread it was created on."
我查了一下,发现我需要稍微更改一下代码。这就是我所拥有的:
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。但是 UI 中的 listView 中没有显示任何内容。不过没有错误。
您可以像在主线程中那样在后台工作程序中执行您想要的操作。但是每次要访问一个UI组件,都需要在主线程(也叫UI线程)下进行。语法取决于您是否在 WPF、Windows Phone、Xamarin 等下工作
使用 WPF,你应该有这样的东西:
Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));
您必须从UI线程而不是后台工作线程访问ListView
和ImageList
。
我建议您将初始化逻辑移动到 WatermarkPicker_Load
中,它在 UI 线程中运行,然后将 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);
});
}
您应该将所有尝试访问 Listview 和 ImageList 的代码移到 DoWork 事件之外,并使用在 DoWork 代码内部引发的 ProgressChanged 事件。
这样,ProgressChanged 事件中的代码在正确的 UI 线程中执行(假设 BackgroundWorker 是在 UI 线程中创建的)。
例如:
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)
{
....
}
}
加载图像列表和列表视图显然会导致 UI 稍微挂起。所以我想加载 UI 并让用户知道我们正在处理事情并让 backgroundWorker 做这件事。我尝试在 formLoad 上调用 "backgroundWorker1.RunWorkerAsync();"
但是我得到了"Cross-thread operation not valid: Control 'listView1' accessed from a thread other than the thread it was created on."
我查了一下,发现我需要稍微更改一下代码。这就是我所拥有的:
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。但是 UI 中的 listView 中没有显示任何内容。不过没有错误。
您可以像在主线程中那样在后台工作程序中执行您想要的操作。但是每次要访问一个UI组件,都需要在主线程(也叫UI线程)下进行。语法取决于您是否在 WPF、Windows Phone、Xamarin 等下工作
使用 WPF,你应该有这样的东西:
Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));
您必须从UI线程而不是后台工作线程访问ListView
和ImageList
。
我建议您将初始化逻辑移动到 WatermarkPicker_Load
中,它在 UI 线程中运行,然后将 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);
});
}
您应该将所有尝试访问 Listview 和 ImageList 的代码移到 DoWork 事件之外,并使用在 DoWork 代码内部引发的 ProgressChanged 事件。
这样,ProgressChanged 事件中的代码在正确的 UI 线程中执行(假设 BackgroundWorker 是在 UI 线程中创建的)。
例如:
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)
{
....
}
}