I need load a overlay on a WPF wizardcontrol. I am using a busyIndicator tool from the wpf extended tooklit.
The code for async await works but the gui thread locks . I am trying add a please wait message when the await calls the function
private async void Button1_Click(object sender, RoutedEventArgs e)
{
BusyIndicator.IsBusy = true;
BusyIndicator.IsEnabled = true;
BusyIndicator.BusyContent = "Please wait while Site is provisioned";
await Task.Run(() =>
{
LongRunningFunction();
});
BusyIndicator.IsBusy=false;
}
The XAML for the BusyIndicator is as below.
<xctk:BusyIndicator x:Name="BusyIndicator" IsBusy="False" BusyContent="Please Wait">
</xctk:BusyIndicator>
The LonRunningFunction is a Webservice call which does not update the UI only returns a Bool value
public static bool LongRunningFunction(string URL)
{
bool IsPresent = CallWebservice()
return IsPresent;
}
Issue
1) The BusyIndicator does not seem to fire before the async call instead it seems to be fire when the LongRunning task completes
2) What is the correct process to call a gui overlay when async and await is used.
This is the way I tackled the problem with asynchronous calls.
Context:
Here I am using MvvM to show you good practice when working with WPF
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;
class VM
{
Dispatcher _dispatcher = Dispatcher.CurrentDispatcher;
//Time consuming operation
private void LongTask()
{
Thread.Sleep(5000);
//in here if you need to send something to the UI thread like an event use it like so:
_dispatcher.Invoke(new Action(() =>
{
//some code here to invoke an event
if (ComponentsLoaded != null)
ComponentsLoaded(this, new EventArgs { });
}));
}
private ICommand _command;
//This is the command to be used instead of click event handler
public ICommand Command
{
get { return _command; }
private set { _command = value; }
}
//method associated with ICommand
void commandMethod(object parameter)
{
Busy = true;
ThreadPool.QueueUserWorkItem(new WaitCallback(multiThreadTask));
Busy = false;
}
//the task to be started on another thread
void multiThreadTask(object parameter)
{
LongTask();
}
public event EventHandler ComponentsLoaded;
}
This is what I use when working with multiple threads in WPF.
You can still use this in the code-behind just instantiate the Dispatcher and you're good to go.
If you need any more info just let us know. HTH
Related
In my WPF application:
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.Diagnostics;
namespace CloudKey
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
AuthText.Visibility = Visibility.Hidden;
}
private async void button_Click(object sender, RoutedEventArgs e)
{
AuthText.Visibility = Visibility.Visible;
await Task.Run(() => Authenticate());
Task.Factory.StartNew(() => Authenticate());
Task.Run(() => Authenticate());
Authenticate();
}
void Authenticate()
{
//Do Stuff
}
}
}
No matter which way I try to call Authenticate with Tasks it just doesn't run. Am I using Task wrong?
Using await (and async) causes an exception to be thrown:
System.InvalidOperationException was unhandled
Message: An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll
Additional information: The calling thread cannot access this object because a different thread owns it.
Using just Task.Run or Task.Factory.StartNew causes the Authenticate method not to be run at all. If I add a breakpoint to the Authenticate method it isn't reached.
Just calling the method with Authenticate() runs the entire method without issue, but it freezes the UI making "AuthText.Visibility = Visibility.Visible;" useless.
To be honest, I really just want the UI to update with the message "Authenticating..." and THEN run everything in the method when I click the button. Is there perhaps an easier way to do that?
THIS IS THE WORKING CODE FOR REFERENCE:
using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.Diagnostics;
using System.Threading.Tasks;
namespace CloudKey
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
//private void PasswordBox_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { button_Click } }
AuthText.Visibility = Visibility.Hidden;
}
private void button_Click(object sender, RoutedEventArgs e) //ON CONTINUE BUTTON CLICK
{
AuthText.Visibility = Visibility.Visible;
Task.Run(() => Authenticate());
}
void Authenticate()
{
Dispatcher.Invoke(
() =>
{
//ALL MY CODE HERE;
});
}
}
}
The issue is that you're not waiting for the asynchronous task to complete, so it appears like "nothing happens" - when in fact something does happen. When you invoke either Task.Run or Task.Factory.StartNew you're essentially performaing a fire-and-forget, unless you correctly handle the Task.
private async void button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() => Authenticate()); // Stuff happens
}
void Authenticate()
{
// DO STUFF
}
In the example above adding the keyword async to the event handler allows the method to utilize the await keyword. The await keyword is where all the magic really occurs...but it will then work as you'd expect, i.e.; "Stuff happens".
When I do beat Stephen Cleary to these answers I usually point people to his blogs, this one in particular should help clarify this for you.
Note
Writing an async void is strongly discouraged! The only exception is in your example where you are applying it to an event handler. Finally, when using Task, and Task<T> with the async / await keywords - do so through the entire stack. I would change your Authenticate method to return a Task for example, such that it can be awaited. Try invoking Task.Run at the lowest level possible.
private async void button_Click(object sender, RoutedEventArgs e)
{
await Authenticate(); // Stuff happens
}
Task Authenticate()
{
return _authModule.Authenticate();
}
Update
Based on your comments, do the following:
private void button_Click(object sender, RoutedEventArgs e)
{
bool authenticated = false;
try
{
AuthText = "Authenticating...";
authenticated = Authenticate(); // Stuff happens
}
finally
{
AuthText = authenticated ? "Authenticated" : "Oops!";
}
}
bool Authenticate()
{
// Return if auth was successful
}
When you modify UI stuff in a new thread then you need to use Dispatcher.Invoke or you can use InvokeAsync
private void Button_Click( object sender, RoutedEventArgs e ) { Task.Run( () => Authenticate() ); }
public void Authenticate()
{
Dispatcher.Invoke(
() =>
{
ClickButton.Content = "Text Changed";
} );
}
By using Dispatcher You are telling WPF that run this code block on main thread which has access to update your GUI controls.
My design is illustrated by below example. Having a while true loop doing something and notifying by an event that it has done something to all subscribers. My application should not continue its execution before its done notifying all subscribers, where this works as long as someone do not put a async void on the callback.
If someone put a async void on the callback to await some task, then my loop can continue before the callback is completed. What other designs can I do to avoid this situation.
Its 3th party plugins that register themeself and subscribe to the event, so I have no control over if they put a async void. Understandable I cant do Task callbacks for the EventHandler, so what alternatives do I have with .net 4.5.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
public class Test
{
public event EventHandler Event;
public void DoneSomething()
{
if (Event != null)
Event(this,EventArgs.Empty);
}
}
class Program
{
static void Main(string[] args)
{
var test = new Test();
test.Event += test_Event;
test.Event +=test_Event2;
while(true)
{
test.DoneSomething();
Thread.Sleep(1000);
}
}
private static void test_Event2(object sender, EventArgs e)
{
Console.WriteLine("delegate 2");
}
static async void test_Event(object sender, EventArgs e)
{
Console.WriteLine("Del1gate 1");
await Task.Delay(5000);
Console.WriteLine("5000 ms later");
}
}
}
If someone put a async void on the callback to await some task, then my loop can continue before the callback is completed. What other designs can I do to avoid this situation.
There is really no way to avoid this. Even if you were to somehow "know" that the subscriber wasn't implemented via async/await, you still couldn't guarantee that the caller didn't build some form of asynchronous "operation" in place.
For example, a completely normal void method could put all of its work into a Task.Run call.
My application should not continue its execution before its done notifying all subscribers
Your current version does follow this contract. You're notifying the subscribers synchronously - if a subscriber does something asynchronously in response to that notification, that is something outside of your control.
Understandable I cant do Task callbacks for the EventHandler, so what alternatives do I have with .net 4.5.
Note that this is actually possible. For example, you can rewrite your above as:
public class Program
{
public static void Main()
{
var test = new Test();
test.Event += test_Event;
test.Event +=test_Event2;
test.DoneSomethingAsync().Wait();
}
}
public delegate Task CustomEvent(object sender, EventArgs e);
private static Task test_Event2(object sender, EventArgs e)
{
Console.WriteLine("delegate 2");
return Task.FromResult(false);
}
static async Task test_Event(object sender, EventArgs e)
{
Console.WriteLine("Del1gate 1");
await Task.Delay(5000);
Console.WriteLine("5000 ms later");
}
public class Test
{
public event CustomEvent Event;
public async Task DoneSomethingAsync()
{
var handler = this.Event;
if (handler != null)
{
var tasks = handler.GetInvocationList().Cast<CustomEvent>().Select(s => s(this, EventArgs.Empty));
await Task.WhenAll(tasks);
}
}
}
You can also rewrite this using event add/remove, as suggested by svick:
public class Test
{
private List<CustomEvent> events = new List<CustomEvent>();
public event CustomEvent Event
{
add { lock(events) events.Add(value); }
remove { lock(events) events.Remove(value); }
}
public async Task DoneSomething()
{
List<CustomEvent> handlers;
lock(events)
handlers = this.events.ToList(); // Cache this
var tasks = handlers.Select(s => s(this, EventArgs.Empty));
await Task.WhenAll(tasks);
}
}
My application should not continue its execution before its done notifying all subscribers, where this works as long as someone do not put a async void on the callback.
I have a blog entry on designing for async event handlers. It is possible to use Task-returning delegates or to wrap an existing SynchronizationContext within your own (which would allow you to detect and wait for async void handlers).
However, I recommend you use "deferrals", which are objects designed specifically to solve this problem for Windows Store applications. A simple DeferralManager is available in my AsyncEx library.
Your event args can define a GetDeferral method as such:
public class MyEventArgs : EventArgs
{
private readonly DeferralManager deferrals = new DeferralManager();
... // Your own constructors and properties.
public IDisposable GetDeferral()
{
return deferrals.GetDeferral();
}
internal Task WaitForDeferralsAsync()
{
return deferrals.SignalAndWaitAsync();
}
}
And you can raise an event and (asynchronously) wait for all asynchronous handlers to complete like this:
private Task RaiseMyEventAsync()
{
var handler = MyEvent;
if (handler == null)
return Task.FromResult<object>(null); // or TaskConstants.Completed
var args = new MyEventArgs(...);
handler(args);
return args.WaitForDeferralsAsync();
}
The benefit of the "deferral" pattern is that it is well-established in the Windows Store APIs, so it's likely to be recognized by end users.
I have the following code in a WinForms application with one button and one label:
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
await Run();
}
private async Task Run()
{
await Task.Run(async () => {
await File.AppendText("temp.dat").WriteAsync("a");
label1.Text = "test";
});
}
}
}
This is a simplified version of the real application I'm working on. I was under the impression that by using async/await in my Task.Run I could set the label1.Text property. However, when running this code I get the error that I'm not on the UI thread and I can't access the control.
Why can't I access the label control?
When you use Task.Run(), you're saing that you don't want the code to run on the current context, so that's exactly what happens.
But there is no need to use Task.Run() in your code. Correctly written async methods won't block the current thread, so you can use them from the UI thread directly. If you do that, await will make sure the method resumes back on the UI thread.
This means that if you write your code like this, it will work:
private async void button1_Click(object sender, EventArgs e)
{
await Run();
}
private async Task Run()
{
await File.AppendText("temp.dat").WriteAsync("a");
label1.Text = "test";
}
Try this:
replace
label1.Text = "test";
with
SetLabel1Text("test");
and add the following to your class:
private void SetLabel1Text(string text)
{
if (InvokeRequired)
{
Invoke((Action<string>)SetLabel1Text, text);
return;
}
label1.Text = text;
}
The InvokeRequired returns true if you are NOT on the UI thread. The Invoke() method takes the delegate and parameters, switches to the UI thread and then calls the method recursively. You return after the Invoke() call because the method has already been called recursively prior to the Invoke() returning. If you happen to be on the UI thread when the method is called, the InvokeRequired is false and the assignment is performed directly.
Try this
private async Task Run()
{
await Task.Run(async () => {
await File.AppendText("temp.dat").WriteAsync("a");
});
label1.Text = "test";
}
Or
private async Task Run()
{
await File.AppendText("temp.dat").WriteAsync("a");
label1.Text = "test";
}
Or
private async Task Run()
{
var task = Task.Run(async () => {
await File.AppendText("temp.dat").WriteAsync("a");
});
var continuation = task.ContinueWith(antecedent=> label1.Text = "test",TaskScheduler.FromCurrentSynchronizationContext());
await task;//I think await here is redundant
}
async/await doesn't guarantee that it will run in UI thread. await will capture the current SynchronizationContext and continues execution with the captured context once the task completed.
So in your case you have a nested await which is inside Task.Run hence second await will capture the context which is not going to be UiSynchronizationContext because it is being executed by WorkerThread from ThreadPool.
Does this answers your question?
Why do you use Task.Run? that start a new worker thread (cpu bound), and it causes your problem.
you should probably just do that:
private async Task Run()
{
await File.AppendText("temp.dat").WriteAsync("a");
label1.Text = "test";
}
await ensure you will continue on the same context except if you use .ConfigureAwait(false);
Because it's on a different thread and cross-thread calls aren't allowed.
You will need to pass on the "context" to the thread you are starting. See an example here: http://reedcopsey.com/2009/11/17/synchronizing-net-4-tasks-with-the-ui-thread/
I am going to give you my latest answer that I have given for async understanding.
The solution is as you know that when you are calling async method you need to run as a task.
Here is a quick console app code that you can use for your reference, it will make it easy for you to understand the concept.
using System;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
Console.WriteLine("Starting Send Mail Async Task");
Task task = new Task(SendMessage);
task.Start();
Console.WriteLine("Update Database");
UpdateDatabase();
while (true)
{
// dummy wait for background send mail.
if (task.Status == TaskStatus.RanToCompletion)
{
break;
}
}
}
public static async void SendMessage()
{
// Calls to TaskOfTResult_MethodAsync
Task<bool> returnedTaskTResult = MailSenderAsync();
bool result = await returnedTaskTResult;
if (result)
{
UpdateDatabase();
}
Console.WriteLine("Mail Sent!");
}
private static void UpdateDatabase()
{
for (var i = 1; i < 1000; i++) ;
Console.WriteLine("Database Updated!");
}
private static async Task<bool> MailSenderAsync()
{
Console.WriteLine("Send Mail Start.");
for (var i = 1; i < 1000000000; i++) ;
return true;
}
}
Here I am trying to initiate task called send mail. Interim I want to update database, while the background is performing send mail task.
Once the database update has happened, it is waiting for the send mail task to be completed. However, with this approach it is quite clear that I can run task at the background and still proceed with original (main) thread.
Is it possible to force the UI thread, to stop waiting for the task(s) to finish, update a UI control via Dispatcher, and then have the UI revert back to waiting for the task(s) to complete?
Ive just tried the following code, but its not working as it appears the
UpdatePB(int NewValue)
method is being executed by the non UI thread.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Threading;
using System.Windows.Threading;
namespace UpdateControlViaDispatcherUITaskWaitAll
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void UpdatePB(int NewValue)
{
pb1.Value = NewValue;
}
private void btn1_Click(object sender, EventArgs e)
{
Task tk = Task.Factory.StartNew(() =>
{
Worker();
});
tk.Wait();
}
public void Worker()
{
int currentValue = 0;
for (int i = 0; i < 100; i++)
{
currentValue = i;
Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
{
UpdatePB(currentValue);
}));
Thread.Sleep(1000);
}
}
}
}
Avoid blocking the UI thread:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Factory
.StartNew(this.Worker)
.ContinueWith(this.OnWorkerCompleted);
}
public void Worker()
{
Dispatcher.Invoke(new Action(() =>
{
btn1.IsEnabled = false;
}));
// your stuff here...
}
private void OnWorkerCompleted(Task obj)
{
Dispatcher.Invoke(new Action(() =>
{
btn1.IsEnabled = true;
}));
}
Try to minimize calls to Dispatcher and also try using BackgroundWorker which supports automatic syncronization between background thread and UI thread with ProgressChanged and RunWorkerComplete events.
WPF Dispatcher has task queue of DispatcherOperation, so when you call tk.Wait(); it blocks Dispatcher thread until tk finishes. You haven't ability to pause this waiting and resume again, but only cancel DispatcherOperation. But in your case I assume you better disable button (or whole window) and enable it when tk finishes. So you should consider asynchronous waiting for tk to complete.
I have the following classes:
using System;
using System.Windows.Forms;
namespace FastEyeControl
{
public partial class ConnectView : Form, IConnectView
{
private IConnectPresenter m_Presenter;
public ConnectView()
{
InitializeComponent();
m_Presenter = new ConnectPresenter(this);
}
public string Hostname
{
get
{
return m_Hostname.Text;
}
}
public int Port
{
get
{
return Convert.ToInt32(m_Port.Text);
}
}
public void ShowMessage(string message)
{
MessageBox.Show(message,
"Success",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
public void ShowError(string message)
{
MessageBox.Show(message,
"ERROR!",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
private void m_ConnectButton_Click(object sender, EventArgs e)
{
m_Presenter.ConnectButtonPressed();
}
}
}
The presenter class:
using System;
using System.Collections.Generic;
namespace FastEyeControl
{
public class ConnectPresenter : IConnectPresenter
{
private IConnectView m_View;
private IConnectModel m_Model;
public ConnectPresenter(IConnectView view)
{
m_View = view;
m_Model = FastEyeClient.Instance;
}
public void ConnectButtonPressed()
{
m_Model.Connect(m_View.Hostname, m_View.Port);
}
private void ConnectionComplete(object sender, ConnectionEventArgs e)
{
// Code here indicating whether connection was successful and informing the view.
// i.e...
if (e.IsConnected)
{
m_View.ShowMessage("Successfully connected.");
}
else
{
m_View.ShowError("Unable to connect.");
}
}
}
}
The model code runs in another thread. The problem is that when I call m_Model.Connect(), I'm calling code that's usually running in another thread within the main thread still (the UI thread). This is not a database connection. This is a TCP/IP connection to a server. If I set a variable within the model, then I am doing this from the UI thread which is not thread safe.
I know that with user controls, they have InvokeRequired and Invoke/BeginInvoke operations that will handle this situation. But that is for user controls only. I know you can't just interrupt another thread in the middle of its execution and tell it to call another method instead. I basically want the non-UI thread to call the Connect code somehow.
Just as a test, I tried using a delegate (fire off an event whenever I want to connect) and when I look in the debugger, the Connect code is still running in the UI thread.
I need a multi-threaded event queue essentially. What's the best way to achieve what I want to do here? Thanks!
public void ConnectButtonPressed()
{
var threadedTask = () => m_Model.Connect(m_View.Hostname, m_View.Port);
threadedTask.BeginInvoke(null,null);
}
This will, no question, use a background thread from the ThreadPool to do the work. Maybe you had tried to call the delegate directly, or called Invoke() on it; that will execute the delegate synchronously.
Now, BeginInvoke is simple to set up, but it has its limitations; you cannot cancel execution of the background thread, and if it throws an exception you cannot catch it in the invoking thread.
You can use BackgroundWorker.