Reputation: 16764
I want to process items one by one, every item from a datagrid element.
I have a method which creates a background worker:
internal void Run(Action doWork, Action completed, Action loadingBeforeAction = null, Action loadingAfterAction = null)
{
using (BackgroundWorker worker = new BackgroundWorker())
{
worker.DoWork += (s, ev) =>
{
if (loadingBeforeAction != null)
{
_dispatcher.Invoke(loadingBeforeAction);
}
doWork();
};
worker.RunWorkerCompleted += (s, ev) =>
{
if (loadingAfterAction != null)
{
_dispatcher.Invoke(loadingAfterAction);
}
completed();
};
worker.RunWorkerAsync();
}
}
And now process the selected items from datagrid:
var folders = btn.Name.Equals("test")
? _model.Folders.ToArray()
: fileDataGrid.SelectedItems.Cast<FolderStatus>().ToArray();"
and
foreach (var folder in folders)
{
Run(() =>
{
Dispatcher.Invoke(()=> {_model.Message = $"This is message for {folder.Name}"});
// long operation here
}, () =>
{
// stuff
}, () =>
{
_model.IsBusy = true;
}, () =>
{
_model.IsBusy = false;
});
}
Seems that all items are processed simultaneously and process message flick from text to other depends on _model.Message
text.
How to process item one by one but without blocking the UI?
Upvotes: 1
Views: 118
Reputation: 15197
BackgroundWorker
is a legacy component from Windows Forms. You shouldn't use it with WPF applications. It will work, but there are better ways to implement what you want.
The easiest way is to use the TAP (Task-based Asynchronous Programming) pattern, supported by the async
and await
C# keywords.
If you're targeting at least the .NET Framework 4.5 or .NET Core, you have everything out-of-the box. For .NET Framework 4.0, there are NuGet packages to enable this functionality.
The implementation could look like this:
async void MyButtonClick(object sender, RoutedEventArgs e)
{
var btn = (Button)sender;
var folders = btn.Name.Equals("test")
? _model.Folders.ToArray()
: fileDataGrid.SelectedItems.Cast<FolderStatus>().ToArray();"
foreach (var folder in folders)
{
// This will be executed on the UI thread
_model.Message = $"This is message for {folder.Name}"};
_model.IsBusy = true;
// This will be executed on a worker thread due to Task.Run
await Task.Run(() => YourLongRunningOperation(folder));
await Task.Run(() => SomeOtherLongRunningOperation());
// This will be executed again on the UI thread
_model.IsBusy = false;
}
}
A couple of notes:
async void (object, EventArgs)
. However, async
methods should return a Task
or a Task<T>
. async void
is here to make the event handler async
. This is the only case when you should use async void
- UI event handlers. Note that an uncaught exception in an async void
UI event handler will crash your application.await Task.Run()
returns, the thread context will be restored to the original one. So, your _model.IsBusy = false
code will be executed on the UI thread again. If you don't need this, use await Task.Run().ConfigureAwait(false)
.Read more about the Task-based asynchronous pattern (TAP).
Here is a short but nice FAQ and tutorial too.
Upvotes: 1