[英]Background worker does not work properly in WPF
i want to show Temporary files in a datagrid , hence it is a long term process i use background worker in my C# .net WPF application . 我想在datagrid中显示临时文件,因此这是一个长期的过程,我在C#.net WPF应用程序中使用后台工作程序。
my Code is 我的密码是
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");
}
}
All the code is running but it hangs when datagrid is loading means my UI does not response when program is running . 所有代码都在运行,但是在加载datagrid时挂起,这意味着我的UI在程序运行时没有响应。
What modification is needed to run background worker smoothly in the above condition ? 在上述条件下,需要什么修改才能使后台工作者顺利运行?
Beside it , if i want to add a ProgressBar which progressed along with this application then what i have to do ? 在它旁边,如果我想添加一个与此应用程序一起进度的ProgressBar,那我该怎么办?
Thank You 谢谢
By using this.Dispatcher.Invoke
, you are effectively marshalling the work back to the UI thread. 通过使用
this.Dispatcher.Invoke
,您可以有效地将工作编组回UI线程。 This makes no sense: you are blocking the UI thread while this action is being performed. 这没有任何意义:执行此操作时,您正在阻止UI线程。
Split the work in two parts: 将工作分为两部分:
Dispatcher.Invoke
Dispatcher.Invoke
外部完成 Dispatcher.Invoke
, or (better) in the RunWorkerCompleted
event handler. Dispatcher.Invoke
完成,或者(最好在RunWorkerCompleted
事件处理程序中)完成。 The background worker component is made exactly so that you don't need to dispatch UI work manually using the dispatcher. 后台工作程序组件是完全制成的,因此您不需要使用调度程序手动调度UI工作。 For example, you could store the files in a field which you fill in the
DoWork
method, and use to fill the datagrid in the RunWorkerCompleted
event: 例如,您可以将文件存储在填写
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);
}
}
}
Note: if you are using C# 5, you now have an even easier way, using the async/await
feature. 注意:如果您使用的是C#5,则现在可以使用
async/await
功能,甚至更简单。 All you need is something like this: 您需要的是这样的:
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;
}
}
All the rest of the cruft is taken care of by the compiler. 其余所有工作由编译器处理。
Use DirectoryInfo.EnumerateFiles . 使用DirectoryInfo.EnumerateFiles 。
This line: 这行:
this.Dispatcher.Invoke
executes code synchronously at main thread, so there are no benefits from using of BackgroudWorker
such a way, because DirectoryInfo.GetFiles
returns only when all files in directory are enumerated. 在主线程上同步执行代码,因此使用
BackgroudWorker
不会带来任何好处,因为DirectoryInfo.GetFiles
仅在枚举目录中的所有文件时才返回。
On the other hand, DirectoryInfo.EnumerateFiles
is lazy. 另一方面,
DirectoryInfo.EnumerateFiles
是惰性的。 You can write something like this: 您可以这样写:
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)));
}
}
Try to take this action off the Dispatcher: 尝试从分派器中采取以下措施:
FileInfo[] files = new DirectoryInfo(System.IO.Path.GetTempPath()).GetFiles();
It should only do small and quick operations that involve UI access or modification. 它只应执行涉及UI访问或修改的小型快速操作。 See this link: http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
请参阅此链接: http : //msdn.microsoft.com/en-us/magazine/cc163328.aspx
Your heavy work can be done by the BackgroundWorker and use the Dispatcher to update the dataGrid.Items collection. 繁重的工作可以由BackgroundWorker完成,并使用Dispatcher更新dataGrid.Items集合。
Try to use the Dispatcher using: 尝试通过以下方式使用分派器:
Dispatcher.BeginInvoke()
You should be updating the UI in either the RunWorkerComplete or ProgressChanged event handlers. 您应该在RunWorkerComplete或ProgressChanged事件处理程序中更新UI。
Try something like this: 尝试这样的事情:
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;
}
Also, there is no need for the try/catch in dowork, exceptions are automatically caught and reported as errors in the runworkercomplete event. 此外,无需在dowork中进行try / catch,在runworkercomplete事件中,异常会自动捕获并报告为错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.