Will caliburn.micro do the right thing with async method on ViewModel? - wpf

As mentioned elsewhere, the new .NET async/await model propagates through layers of software like a virus. A recent async change has now bubbled up to my view model, and I am wondering if it is safe change declaration from public void DoStuff() to public async Task DoStuff() ?
Thanks!

The support of asynchronous programming model in Caliburn.Micro is pretty good now.
Few things you can do:
Use async/await in Action method. Be careful, as action methods are technically event handlers, you shoud do async void rather than async Task.
Asynchronous event handlers for Screen's events, like Activated, ViewLoaded and other.
Asynchronous overrides for Screen's methods: OnInitialize, OnActivate, ... You can override then as protected override async void OnInitialize(){} and inside you can await another task.
Convert Coroutines to Tasks. Use ExecuteAsync() extension method. Coroutines still have some advantages in some scenarios, like execution context.
IHandleWithTask<TMessage> - pretty handy...
There's a blog post desribing some use cases with few code snippets. And a GitHub repository with sample project I've used to play with async/await in Caliburn.

The answer is 'yes', starting with Caliburn.Micro 1.5.
See release announcement.

It's safe, but will break your existing global exception handling. After I did the refactoring, I didn't see any error dialogues anymore, to fix that, I had to subscribe to the Coroutine.Completed event:
Coroutine.Completed += (s, a) =>
{
//Do something here ...
};
You can do that in your App.xaml.cs file.
Example from my code on how I handle all possible errors raised in my app:
protected override void OnStartup(StartupEventArgs e)
{
SetupExceptionHandlers();
base.OnStartup(e);
}
private void SetupExceptionHandlers()
{
AppDomain.CurrentDomain.UnhandledException += (s, a) =>
{
HandleException((Exception)a.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException");
};
Current.DispatcherUnhandledException += (s, a) =>
{
HandleException(a.Exception, "Application.Current.DispatcherUnhandledException");
a.Handled = true;
};
TaskScheduler.UnobservedTaskException += (s, a) =>
{
Dispatcher.InvokeAsync(() => HandleException(a.Exception, "TaskScheduler.UnobservedTaskException"));
a.SetObserved();
};
Coroutine.Completed += (s, a) =>
{
if (a.Error != null)
{
HandleException(a.Error, "Coroutine.Completed");
}
};
}
private void HandleException(Exception exception, string source)
{
logger.Error(exception, "Unhandled exception occured (Source: {0})", source);
var msg = new ShowErrorDialogEvent(exception, exception.GetBaseException().Message);
eventAggregator.PublishOnUIThread(msg);
}
In-case you're wondering, the logger and eventAggregator variables are instantiated from the bootstrapper class in the OnStartup method before calling DisplayRootViewFor.

Marco Amendola, a project manager in the Caliburn.Micro project wrote an article that has this title: Coroutines are dead. Long live Coroutines. and he titled it this way because of the emergence of the async/wait programming model and if you read the article you will see that async/wait bring to life what Coroutines did in the past so i assume you could use them safely where you have used Coroutines before. i advise you to read the article.

Related

Setting Culture in .net5 WPF application

In a WPF application I try to set the culture in OnStartup.
protected override async void OnStartup(StartupEventArgs startupEventArgs)
{
base.OnStartup(startupEventArgs);
var a = new CultureInfo(ConfigurationManager.AppSettings["Language"]);
Thread.CurrentThread.CurrentCulture = a;
Thread.CurrentThread.CurrentUICulture = a;
CultureInfo.DefaultThreadCurrentCulture = a
CultureInfo.DefaultThreadCurrentUICulture = a;
CultureInfo.CurrentCulture = a;
}
If I start a method from the MainWindow with Click event or with ICommand then in the method the Thread.CurrentThread.CurrentUICulture will be always en-US, which is very strange (can sombody exaplain?). I can set again to the desired Culture but I have to do it in each called method one by one. Is there an alternative?
In .net4.7 there was a workaround but it does not work in .net5.
The reason for this behavior is how async methods are implemented. async methods have their own special execution context. This context has it's own CultureInfo, which is inherited from the non-async context that invokes the async method.
In your case, the async context's culture is inherited from the main thread, before the culture is changed.
What you can do is to implement the already suggested solution using the Dispatcher.InvokeAsync to postpone the CultureInfo configuration. This way the configuration is executed outside the async context:
Dispatcher.InvokeAsync(() => CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-EN"));
Since this can mess up your initialization routine, as the real context would be avaailable after the application is configured and the main Window displayed, you would prefer a different solution.
You can for example used an event based initialization routine, where you run low-level application configuration like culture configuration first and continue with the remaining initialization that involves asynchronous operations in an async context:
App.xaml.cs
// Event may be defined on a different class
private event EventHandler ConfigurationCompleted;
private void OnConfigurationCompleted() => this.ConfigurationCompleted?.Invoke(this, EventArgs.Empty);
protected override void OnStartup(StartupEventArgs startupEventArgs)
{
this.ConfigurationCompleted += ConfigureInAsyncContext;
// Do "low-level" application configuration. Code may be executed in a different class context
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-EN");
...
// Continue in async context
OnConfigurationCompleted();
}
private async void ConfigureInAsyncContext(object sender, EventArgs e)
{
// TODO::Execute async operations
new MainWindow().Show();
}
The key is to separate the non-async configuration from the async initialization.

Should we use anything Await/Async while using prism commands?

Until today, I was thinking that when you were binding to a DelegateCommand with an execute method being async, WPF was really doing this in an async manner.
But according to some tests I got, and this blog post: http://brianlagunas.com/prism-delegatecommand-fromasynchandler-is-obsolete/ it was never the case.
We recently started to implement an application, and we did start to use the await/async pattern. We were thinking this would be a nice way to use await/asyncand have a responsive app.
So my question is: Can we use(and most important, benefits) async/await in our commands handler?
By example:
public class SomeViewModel
{
public DelegateCommand SomeSpecificCommand { get; set; }
public SomeViewModel(){
SomeSpecificCommand = new DelegateCommand(DoSomeWorkAsync);
}
private async void DoSomeWorkAsync()
{
await DoSomeLongRunningJob();
//Update the GuiWithSomething
}
}
Technically this compiles, but:
My understanding is that XAML doesn't wait on the async return
If I use a CompositeCommand, the composite command doesn't wait on the result of the DelegateCommand
Can we use(and most important, benefits) async/await in our commands handler?
Yes, as long as your commands are fire-and-forget.
The problem is more in the testing than in production: how do you test a view model with a command that you have no idea of when it is completed?
One option is to "pollute" your view model with internal/public methods that are actually commands but expose the async nature, for your tests to await them.
Another one would be to reflect into the DelegateCommand instance to get hold of the async handler in the test.
Thirdly, you could provide your own command implementation, inspired by the DelegateCommand, but providing (test-)access to the async handler.

How to ensure wcf service client finishs his works in silverlight?

I use wcf service client to submit changes of data for a silverlight project. The correlative codes like this:
public class DispatcherCollection : UpdatableCollection<DocumentDispatcher>
{
public override void SubmitChanges()
{
DocumentServiceClient client = new DocumentServiceClient();
client.NewDocumentCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
client.UpdateColumnCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
client.RemoveDocumentCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
foreach (DocumentDispatcher d in this)
{
if (d.IsNew)
{
// d=>object[] data
client.NewDocumentAsync(data);
d.IsNew=false;
}
else
{
foreach (string propertyName in d.modifiedProperties)
{
client.UpdateColumnAsync(d.ID, GetPropertyValue(propertyName));
}
dd.ClearModifications();
}
}
foreach (DocumentDispatcher dd in removedItems)
{
client.RemoveDocumentAsync(dd.ID);
}
removedItems.Clear();
}
}
Class UpdatableCollection derives from ObserableCollection, and I implemtent logics in class DocumentDispatcher and UpdatableCollection to buffer the changes of data such as new created, property modified and removed. I use SubmitChanges method to submit all changes to server.
Now I am stuck:
1. I am at a loss when to close the client after a bunlde fo async calls. I don't know which callback is the last one.
2. What will happen when a user closes the IE immediately right after clicking the save button (it seems to be done because it runs async but in fact the updating threads are industriously running.)?
You can keep a counter or use an isbusy function to monitor the callbacks from your Async calls - to make sure they all finished.
If the user fires off a request to the WCF service, the WCF service will complete but there will be no call back - as the application will be closed.
I think that there is no wait handle for silverlight asynchornized call brings inconvenience. Here is my experence. I want to check and submit modifications of data which are not expicitly submitted when browser is closing. I have implemented codes in App_Exit like this:
private void Application_Exit(object sender, EventArgs e)
{
Document doc = EDPViewModel.CurrentViewModel.Document;
if (doc != null) new ServiceClient().SubmitChangesAsync(doc);
}
provided that in the SubmitChangesAsync method, not submitted modifications of doc are found out and submitted. Therefore, because of the asynchronized running features, while the service invoking is being sent, the application is yet immediately closed. And that will dispose related resouces of the application, including Service Invoking Tasks. So the codes above work not. I hope so eagerly that somewhere exists a mechanism, which can export a wait handle from silverlight asynchronized call, so that I can update the above codes whith this:
private void Application_Exit(object sender, EventArgs e)
{
Document doc = EDPViewModel.CurrentViewModel.Document;
if (doc != null)
{
Task t = new TaskFactory().StartNew(() => new ServiceClient().SubmitChangesAsync(doc));
t.Wait();
}
}
With wait operation I can really be sure that all modifications are really definitely submitted. So is there any similar pattern that can be used in silverlight?
It's for me a good news, as you put it, that calls could work like the mode "requesting and forgetting". So I needn' to worry too much about data losing during submitting.
To ensure all service calls are sent out before application is closed, I think, counter is a simple and effient idea. I will try to implement it in my project.
Thank you for your help!

Invoke or BeginInvoke cannot be called on a control until the window handle has been created

I have a SafeInvoke Control extension method similar to the one Greg D discusses here (minus the IsHandleCreated check).
I am calling it from a System.Windows.Forms.Form as follows:
public void Show(string text) {
label.SafeInvoke(()=>label.Text = text);
this.Show();
this.Refresh();
}
Sometimes (this call can come from a variety of threads) this results in the following error:
System.InvalidOperationException occurred
Message= "Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
Source= "System.Windows.Forms"
StackTrace:
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.Invoke(Delegate method)
at DriverInterface2.UI.WinForms.Dialogs.FormExtensions.SafeInvoke[T](T control, Action`1 action)
in C:\code\DriverInterface2\DriverInterface2.UI.WinForms\Dialogs\FormExtensions.cs:line 16
What is going on and how do I fix it? I know as much as it is not a problem of form creation, since sometimes it will work once and fail the next time so what could the problem be?
PS. I really really am awful at WinForms, does anyone know a good series of articles that explains the whole model and how to work with it?
It's possible that you're creating your controls on the wrong thread. Consider the following documentation from MSDN:
This means that InvokeRequired can
return false if Invoke is not required
(the call occurs on the same thread),
or if the control was created on a
different thread but the control's
handle has not yet been created.
In the case where the control's handle
has not yet been created, you should
not simply call properties, methods,
or events on the control. This might
cause the control's handle to be
created on the background thread,
isolating the control on a thread
without a message pump and making the
application unstable.
You can protect against this case by
also checking the value of
IsHandleCreated when InvokeRequired
returns false on a background thread.
If the control handle has not yet been
created, you must wait until it has
been created before calling Invoke or
BeginInvoke. Typically, this happens
only if a background thread is created
in the constructor of the primary form
for the application (as in
Application.Run(new MainForm()),
before the form has been shown or
Application.Run has been called.
Let's see what this means for you. (This would be easier to reason about if we saw your implementation of SafeInvoke also)
Assuming your implementation is identical to the referenced one with the exception of the check against IsHandleCreated, let's follow the logic:
public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous)
{
if (uiElement == null)
{
throw new ArgumentNullException("uiElement");
}
if (uiElement.InvokeRequired)
{
if (forceSynchronous)
{
uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
}
else
{
uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
}
}
else
{
if (uiElement.IsDisposed)
{
throw new ObjectDisposedException("Control is already disposed.");
}
updater();
}
}
Consider the case where we're calling SafeInvoke from the non-gui thread for a control whose handle has not been created.
uiElement is not null, so we check uiElement.InvokeRequired. Per the MSDN docs (bolded) InvokeRequired will return false because, even though it was created on a different thread, the handle hasn't been created! This sends us to the else condition where we check IsDisposed or immediately proceed to call the submitted action... from the background thread!
At this point, all bets are off re: that control because its handle has been created on a thread that doesn't have a message pump for it, as mentioned in the second paragraph. Perhaps this is the case you're encountering?
I found the InvokeRequired not reliable, so I simply use
if (!this.IsHandleCreated)
{
this.CreateHandle();
}
Here is my answer to a similar question:
I think (not yet entirely sure) that
this is because InvokeRequired will
always return false if the control has
not yet been loaded/shown. I have done
a workaround which seems to work for
the moment, which is to simple
reference the handle of the associated
control in its creator, like so:
var x = this.Handle;
(See
http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html)
The method in the post you link to calls Invoke/BeginInvoke before checking if the control's handle has been created in the case where it's being called from a thread that didn't create the control.
So you'll get the exception when your method is called from a thread other than the one that created the control. This can happen from remoting events or queued work user items...
EDIT
If you check InvokeRequired and HandleCreated before calling invoke you shouldn't get that exception.
If you're going to use a Control from another thread before showing or doing other things with the Control, consider forcing the creation of its handle within the constructor. This is done using the CreateHandle function.
In a multi-threaded project, where the "controller" logic isn't in a WinForm, this function is instrumental in Control constructors for avoiding this error.
Add this before you call method invoke:
while (!this.IsHandleCreated)
System.Threading.Thread.Sleep(100)
Reference the handle of the associated control in its creator, like so:
Note: Be wary of this solution.If a control has a handle it is much slower to do things like set the size and location of it. This makes InitializeComponent much slower. A better solution is to not background anything before the control has a handle.
var that = this; // this is a form
(new Thread(()=> {
var action= new Action(() => {
something
}));
if(!that.IsDisposed)
{
if(that.IsHandleCreated)
{
//if (that.InvokeRequired)
that.BeginInvoke(action);
//else
// action.Invoke();
}
else
that.HandleCreated+=(sender,event) => {
action.Invoke();
};
}
})).Start();
I had this problem with this kind of simple form:
public partial class MyForm : Form
{
public MyForm()
{
Load += new EventHandler(Form1_Load);
}
private void Form1_Load(Object sender, EventArgs e)
{
InitializeComponent();
}
internal void UpdateLabel(string s)
{
Invoke(new Action(() => { label1.Text = s; }));
}
}
Then for n other async threads I was using new MyForm().UpdateLabel(text) to try and call the UI thread, but the constructor gives no handle to the UI thread instance, so other threads get other instance handles, which are either Object reference not set to an instance of an object or Invoke or BeginInvoke cannot be called on a control until the window handle has been created. To solve this I used a static object to hold the UI handle:
public partial class MyForm : Form
{
private static MyForm _mf;
public MyForm()
{
Load += new EventHandler(Form1_Load);
}
private void Form1_Load(Object sender, EventArgs e)
{
InitializeComponent();
_mf = this;
}
internal void UpdateLabel(string s)
{
_mf.Invoke((MethodInvoker) delegate { _mf.label1.Text = s; });
}
}
I guess it's working fine, so far...
What about this :
public static bool SafeInvoke( this Control control, MethodInvoker method )
{
if( control != null && ! control.IsDisposed && control.IsHandleCreated && control.FindForm().IsHandleCreated )
{
if( control.InvokeRequired )
{
control.Invoke( method );
}
else
{
method();
}
return true;
}
else return false;
}

Winforms threading problem, second thread can't access 1st main forms controls

I have a winforms application, the issue has to do with threading.
Since I am calling 'MyCustomCode() which creates a new thread, and calls the method
'SomeMethod()' which then accesses MessageBox.Show(...).
The problem has to do with threading, since the newly created thread is trying to access
a control that was created on another thread.
I am getting the error:
Cross-thread operation not valid: Control 'TestForm' accessed from a thread other than the thread it was created on.
public TestForm()
{
InitializeComponent();
// custom code
//
MyCustomCode();
}
public void SomeMethod()
{
// ***** This causes an error ****
MessageBox.Show(this,
ex.Message,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
private void InitializeAutoUpdater()
{
// Seperate thread is spun to keep polling for updates
ThreadStart ts = new ThreadStart(SomeMethod);
pollThread = new Thread(ts);
pollThread.Start();
}
Update
If you look at this example http://www.codeproject.com/KB/cs/vanillaupdaterblock.aspx, the method CheckAndUpdate is calling MessageBox.Show(..) that is what my problem is. I would have thought that code was good to go!
Funny thing is that this code was working just fine on Friday???
You cannot acces UI elements from multiple threads.
One way to solve this is to call the Invoke method of a control with a delegate to the function wich use the UI elements (like the message box). Somethin like:
public delegate void InvokeDelegate();
public void SomeMethod()
{
button1.Invoke((InvokeDelegate)doUIStuff);
}
void doUIStuff()
{
MessageBox.Show(this,
ex.Message,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
to avoid cross-thread exceptions (InvalidOperationException), here is the code pattern:
protected delegate void someGuiFunctionDelegate(int iParam);
protected void someGuiFunction(int iParam)
{
if (this.InvokeRequired)
{
someGuiFunctionDelegate dlg = new
someGuiFunctionDelegate(this.someGuiFunction);
this.Invoke(dlg, new object[] { iParam });
return;
}
//do something with the GUI control here
}
i agree that this is annoying, but it is an artifact of the fact that windows GUI controls are not thread-safe. The exception can be turned off with a flag somewhere or other, but don't do that as it can lead to extremely hard to find bugs.
To keep things simple you can look into using the BackGroundWorker class. This class will provide a framework for to handle your threading and progress notification events. Your ui thread will handle the progress event and display the error message you pass back.
Use Control.BeginInvoke or Control.Invoke methods
OR
Use SynchronizationContext
I know this is an older post, but I recently found an elegant solution to this problem using generics and extension methods. This is a combination of the authors works and some comments.
A Generic Method for Cross-thread Winforms Access
http://www.codeproject.com/KB/cs/GenericCrossThread.aspx
public static void Manipulate<T>(this T control, Action<T> action) where T : Control
{
if (control.InvokeRequired)
{
control.Invoke(new Action<T, Action<T>>(Manipulate),
new object[] { control, action });
}
else
{ action(control); }
}
This can be called in the following manner, for simplicity I used a label.
someLabel.Manipulate(lbl => lbl.Text = "Something");
You should NOT use BeginInvoke, you should use Invoke, then once you grasp that, you can look into using BeginInvoke if really needed.
Check for InvokeRequired
I praticularly like a recursive call.
public delegate void InvokeDelegate(string errMessage);
public void SomeMethod()
{
doUIStuff("my error message");
}
void doUIStuff(string errMessage)
{
if (button1.InvokeRequired)
button1.Invoke((InvokeDelegate)doUIStuff(errMessage));
else
{
MessageBox.Show(this,
ex.Message,
errMessage,
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
}
'*******************************************************************
' Get a new processor and fire it off on a new thread.
'*******************************************************************
fpProc = New Processor(confTable, paramFile, keyCount)
AddHandler fpProc.LogEntry, AddressOf LogEntry_Handler
Dim myThread As System.Threading.Thread = New System.Threading.Thread(AddressOf fpProc.ProcessEntry)
myThread.Start()
Then in the parent app you have:
'*************************************************************************
' Sub: LogEntry_Handler()
' Author: Ron Savage
' Date: 08/29/2007
'
' This routine handles the LogEntry events raised by the Processor class
' running in a thread.
'*************************************************************************
Private Sub LogEntry_Handler(ByVal logLevel As Integer, ByVal logMsg As String) Handles fProc.LogEntry
writeLogMessage(logMsg);
End Sub
That's what I do.

Resources