我想在数据网格中显示临时文件,因此这是一个长期的过程,我在C#.net WPF应用程序中使用后台工作程序。
我的代码是
private System.ComponentModel.BackgroundWorker _background = new System.ComponentModel.BackgroundWorker();
private void button1_Click(object sender, RoutedEventArgs e)
{
_background.RunWorkerAsync();
}
public MainWindow()
{
InitializeComponent();
this._background.DoWork += new DoWorkEventHandler(_background_DoWork);
this._background.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(_background_RunWorkerCompleted);
this._background.WorkerReportsProgress = true;
_background.WorkerSupportsCancellation = true;
}
void _background_DoWork(object sender, DoWorkEventArgs e)
{
this.Dispatcher.Invoke((Action)(() =>
{
try
{
FileInfo[] files = new
DirectoryInfo(System.IO.Path.GetTempPath()).GetFiles();
foreach (FileInfo fi in files)
{
if (fi != null)
{
dataGrid1.Items.Add(fi);
}
}
}
catch { }
}));
}
void _background_RunWorkerCompleted(object sen, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Cancelled");
}
else if (e.Error != null)
{
MessageBox.Show("Exception Thrown");
}
}
所有代码都在运行但是在加载datagrid时它会挂起意味着我的UI在程序运行时没有响应。
在上述条件下顺利运行后台工作程序需要进行哪些修改?
除此之外,如果我想添加一个与此应用程序一起进展的ProgressBar,那么我必须做什么?
谢谢
答案 0 :(得分:3)
通过使用this.Dispatcher.Invoke
,您可以有效地将工作编组回UI线程。这没有任何意义:在执行此操作时,您正在阻止UI线程。
将工作分为两部分:
Dispatcher.Invoke
Dispatcher.Invoke
中完成,或者(更好)在RunWorkerCompleted
事件处理程序中完成。后台工作程序组件完全正确,因此您无需使用调度程序手动分派UI工作。例如,您可以将文件存储在填写DoWork
方法的字段中,并用于填充RunWorkerCompleted
事件中的数据网格:
FileInfo[] files;
void _background_DoWork(object sender, DoWorkEventArgs e)
{
files = new DirectoryInfo(System.IO.Path.GetTempPath()).GetFiles();
}
void _background_RunWorkerCompleted(object sen, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Cancelled");
}
else if (e.Error != null)
{
MessageBox.Show("Exception Thrown");
}
else
{
foreach (FileInfo fi in files)
{
dataGrid1.Items.Add(fi);
}
}
}
注意:如果您使用的是C#5,那么现在使用async/await
功能可以更轻松地使用它。所有你需要的是这样的:
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
try
{
var files = await Task.Run(() =>
new DirectoryInfo(System.IO.Path.GetTempPath()).GetFiles()
);
foreach (var f in files)
this.dataGrid1.Items.Add(f.Name);
}
catch (Exception e)
{
MessageBox.Show("Exception thrown!"); // do proper error handling here...
}
finally
{
button1.Enabled = true;
}
}
编译器负责所有剩下的事情。
答案 1 :(得分:0)
使用DirectoryInfo.EnumerateFiles。
这一行:
this.Dispatcher.Invoke
在主线程上同步执行代码,因此使用BackgroudWorker
这样的方式没有任何好处,因为DirectoryInfo.GetFiles
仅在枚举目录中的所有文件时返回。
另一方面,DirectoryInfo.EnumerateFiles
是懒惰的。你可以这样写:
void _background_DoWork(object sender, DoWorkEventArgs e)
{
var info = new DirectoryInfo(System.IO.Path.GetTempPath());
// now enumeration happens in background
foreach (var fi in info.EnumerateFiles())
{
// main thread in used only when there is next enumeration result available
Dispatcher.Invoke((Action)(() => dataGrid1.Items.Add(fi)));
}
}
答案 2 :(得分:0)
尝试从Dispatcher中取消此操作:
FileInfo[] files = new DirectoryInfo(System.IO.Path.GetTempPath()).GetFiles();
它应该只执行涉及UI访问或修改的小而快的操作。请看这个链接:http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
您可以通过BackgroundWorker完成繁重的工作,并使用Dispatcher更新dataGrid.Items集合。
尝试使用Dispatcher:
Dispatcher.BeginInvoke()
答案 3 :(得分:0)
您应该在RunWorkerComplete或ProgressChanged事件处理程序中更新UI。
尝试这样的事情:
public Program()
{
w = new BackgroundWorker();
w.DoWork += new DoWorkEventHandler(w_DoWork);
w.ProgressChanged += new ProgressChangedEventHandler(w_ProgressChanged);
w.RunWorkerCompleted += new RunWorkerCompletedEventHandler(w_RunWorkerCompleted);
w.WorkerReportsProgress = true;
w.WorkerSupportsCancellation = true;
w.RunWorkerAsync();
}
void w_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
FileInfo[] files = e.Result as FileInfo[];
foreach (FileInfo fi in files)
{
//dataGrid1.Items.Add(fi);
}
}
void w_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
FileInfo fi = e.UserState as FileInfo;
//dataGrid1.Items.Add(fi);
}
void w_DoWork(object sender, DoWorkEventArgs e)
{
var w = sender as BackgroundWorker;
FileInfo[] files = new DirectoryInfo(
Path.GetTempPath()).GetFiles();
// Using ProgressChanged
foreach (FileInfo fi in files)
{
w.ReportProgress(0, fi);
}
// Using RunWorkerCompleted
e.Result = files;
}
此外,在dowork中不需要try / catch,在runworkercomplete事件中会自动捕获异常并将其报告为错误。