Execute Task in Background in Wpf Application

Execute task in background in WPF application

With .NET 4.5 (or .NET 4.0 + Microsoft.Bcl.Async), the best way is to use Task-based API and async/await. It allows to use the convenient (pseudo-)sequential code workflow and have structured exception handling.

Example:

private async void Start(object sender, RoutedEventArgs e)
{
try
{
await Task.Run(() =>
{
int progress = 0;
for (; ; )
{
System.Threading.Thread.Sleep(1);
progress++;
Logger.Info(progress);
}
});
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

More reading:

How to execute task in the WPF background while able to provide report and allow cancellation?

Async in 4.5: Enabling Progress and Cancellation in Async APIs.

Async and Await.

Async/Await FAQ.

How to execute task in the wpf background while able to provide report and allow cancellation?

I thought I answered your question here. If you need more sample code on how to do this using Task Parallel Library, with CancellationTokenSource and IProgress<T>, here it is:

Action _cancelWork;

private async void StartButton_Click(object sender, RoutedEventArgs e)
{
this.StartButton.IsEnabled = false;
this.StopButton.IsEnabled = true;
try
{
var cancellationTokenSource = new CancellationTokenSource();

this._cancelWork = () =>
{
this.StopButton.IsEnabled = false;
cancellationTokenSource.Cancel();
};

var limit = 10;

var progressReport = new Progress<int>((i) =>
this.TextBox.Text = (100 * i / (limit-1)).ToString() + "%");

var token = cancellationTokenSource.Token;

await Task.Run(() =>
DoWork(limit, token, progressReport),
token);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
this.StartButton.IsEnabled = true;
this.StopButton.IsEnabled = false;
this._cancelWork = null;
}

private void StopButton_Click(object sender, RoutedEventArgs e)
{
this._cancelWork?.Invoke();
}

private int DoWork(
int limit,
CancellationToken token,
IProgress<int> progressReport)
{
var progress = 0;

for (int i = 0; i < limit; i++)
{
progressReport.Report(progress++);
Thread.Sleep(2000); // simulate a work item
token.ThrowIfCancellationRequested();
}
return limit;
}

Can I do a in-process background task in a WPF app in Desktop Bridge

You can indeed use an app service that runs in the same process as its host UWP app, but a WPF application has a different Application class than the Windows.UI.Xaml UWP Application class that has no OnBackgroundActivated method to be called when the app service is invoked.

So you should either use a background task that runs in a separate process or convert your application into a pure UWP app.

background task is blocking main thread in wpf .net core application

One way to do it is to wrap the task in an async event callback:


public partial class MainWindow : Window
{
// I mocked your missing types, replace with your own
class AssignTickets
{
public Task AssignTicketCreator() => Task.CompletedTask;
}

public class RunTicketTasks
{
public async Task RunBackgroundTasks()
{
AssignTickets assignTickets = new AssignTickets();
int x = 0;
while (true)
{
await assignTickets.AssignTicketCreator();
await Task.Delay(1000);

var isRunningOnDispatcher = Application.Current.Dispatcher ==
System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread);
Trace.WriteLine($"One second passed, running on dispatcher: {isRunningOnDispatcher}");

// exception will also get thrown on the main thread
if (x++ > 3) throw new Exception("Hello!");
}
}
}

public MainWindow()
{
InitializeComponent();

// event fires on UI thread!
this.Loaded += async (sender, args) =>
{
try
{
await new RunTicketTasks().RunBackgroundTasks();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
};
}
}

C# WPF Exiting application with running background workers which update dialog

I feel silly, must have been the end of the day on Friday....here was the problem

in BackgroundDialog:

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
}

Must have been a relic from before I found this solution. However, some cancellation is needed to prevent the user from closing the dialog from the taskbar. So I wrapped the cancel with the statement if (!_Release)

Operate Task in Background AND use Control Elements from another Class

Try something like this

public void DoSomething()
{
Task.Run(() =>
{
BackgroundProcess();
});
}

private void BackgroundProcess()
{
string ControlValue;
Application.Current.Dispatcher.Invoke(() =>
{
ControlValue = textBox.Text;
});
ControlValue += ControlValue;
Application.Current.Dispatcher.Invoke(() =>
{
textBox.Text = ControlValue;
});
}

What I'm doing is only accessing the control when I absolutely have to, and doing that in the dispatcher. Everything else is done in the background thread. As long as you're only doing a few entries for this, it shouldn't clog up the dispatcher.

I've got one place where I do this to log the progress of a process into a textbox using TextBox.AppendText and TextBox.ScrollToEnd, and that doesn't seem to affect the UI. So the key is to do the minimum amount of work necessary using controls. If you're waiting on input, then you should be using MVVM and DataBinding, so that you kick of the relevant parts of the process when the values change.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;

public class MVVMExampleViewModel : System.ComponentModel.INotifyPropertyChanged
{
private string _MajorChange;
public string MajorChange
{
get
{
return _MajorChange;
}
set
{
_MajorChange = value;
DoPropertyChanged("MajorChange");
-- Start process using value here
}
}

private void DoPropertyChanged(string propertyname)
{
PropertyChanged(me, New PropertyChangedEventArgs(propertyname));
}

public event PropertyChangedEventHandler PropertyChanged;
}

WPF Usage

<Grid DataContext="{Binding CurrentMVVMExample}" 
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />

</Grid.RowDefinitions>
<Label Content="{DynamicResource NewItemMessage}" Visibility="{Binding IsDetached, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}" Foreground="#FFBD0000" RenderTransform="{DynamicResource NewItemMessageLocation}"/>

<Label Content="Status" Grid.Column="0" Grid.Row="1" />
<TextBox Text="{Binding MajorChange, Delay=500, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1" Grid.Row="1" Width="{DynamicResource SectionFieldWidth}" />
</Grid>

How to use WPF Background Worker

  1. Add using
using System.ComponentModel;

  1. Declare Background Worker:
private readonly BackgroundWorker worker = new BackgroundWorker();

  1. Subscribe to events:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;

  1. Implement two methods:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
}

private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}

  1. Run worker async whenever your need.
worker.RunWorkerAsync();

  1. Track progress (optional, but often useful)

    a) subscribe to ProgressChanged event and use ReportProgress(Int32) in DoWork

    b) set worker.WorkerReportsProgress = true; (credits to @zagy)

How to repeatedly execute a method without affecting the GUI until application exits? C# WPF

Here is an example using the DispatchTimer, and async / await. This is the preferred timer for most WPF applications. You may need the other timers in some situations, but this one will handle the majority of WPF tasks.

The DispatchTimer allows the code to run on the UI thread, so you don't have to worry about manually dispatching the cross-thread operations.

The async / await calls allow the database operations (emulated here with a call to Task.Delay) to not block the UI. You should replace the Task.Delay with the appropriate async DB operations, like DbContext.SomeDbSet.FirstOrDefaultAsync() if you are using Entity Framework.

The XAML just displays a checkbox that allows the application to exit when checked, and an animated label, to prove that the UI thread isn't blocked.


BetterWindow.xaml.cs:

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;

namespace asyncTest
{
public partial class BetterWindow : Window
{
public BetterWindow()
{
InitializeComponent();

var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(10);
timer.Tick += Timer_Tick;
timer.Start();
}

private async void Timer_Tick(object sender, EventArgs e)
{
// "async void" is generally frowned upon, but it is acceptable for event handlers.
if (await ShouldExit())
{
Close();
}
}

private async Task<bool> ShouldExit()
{
Debug.WriteLine("Checking the DB");
//Simulate a long DB operation
await Task.Delay(TimeSpan.FromSeconds(5));
return chkAllowClose.IsChecked.GetValueOrDefault(false);
}
}
}

BetterWindow.xaml:

<Window x:Class="asyncTest.BetterWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:asyncTest"
mc:Ignorable="d"
Title="BetterWindow" Height="450" Width="800">
<DockPanel>
<CheckBox Content="Allow Close" Name="chkAllowClose" DockPanel.Dock="Top"></CheckBox>
<Grid>
<Label Content="The UI isn't locked!" RenderTransformOrigin="0.5, 0.5" Width="200" Height="200">
<Label.RenderTransform>
<RotateTransform x:Name="noFreeze" />
</Label.RenderTransform>
<Label.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Label.RenderTransform).(RotateTransform.Angle)"
To="-360" Duration="0:0:10" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Label.Triggers>
</Label>
</Grid>
</DockPanel>
</Window>

Running multiple background tasks in a WPF applicatiion - UI update stalling

Do not create thousands of tasks - this will cause immense performance problems.

Instead, use something like Parallel.For() to limit the number of tasks that run simultaneously; for example:

Parallel.For(1,
iterations + 1,
(index) =>
{
SampleTask(new SampleTaskParameterCollection { TaskId = index, Locker = locker, MinSleep = minSleep, MaxSleep = maxSleep });
});

Also if the UI updates take longer than the interval between the calls to BeginInvoke() then the invokes will begin to be queued up and things will get nasty.

To solve that, you could use a counter in your SampleTask() to only actually update the UI once every N calls (with a suitable value for N).

However, note that to avoid threading issues you'd have to use Interlocked.Increment() (or some other lock) when incrementing and checking the value of the counter. You'd also have to ensure that you updated the UI one last time when all the work is done.

How to open window or whatever while my method is doing something in background WPF

If your ExecuteLongMethod() must be executed on the dispatcher thread for some reason, you could create a new window that launches in a separate thread and display this one during the time it takes for the long-running method to complete. Please refer to the following link for more information.

Wait screen during rendering UIElement in WPF

The other option would otherwise be to execute the long-running method on a background thread and display the loading window on the main thread.

The recommended way to do this would be to use the task parallel library (TPL) and start a task: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx

if (e.Key == Key.Delete)
{
Window window = new Window();
window.Show();

Task.Factory.StartNew(() => ExecuteLongMethod())
.ContinueWith(task =>
{
window.Close();
},System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}


Related Topics



Leave a reply



Submit