Print Page | Close Window

DelegateCommand CanExecute and Async

Printed From: IdeaBlade
Category: Cocktail
Forum Name: Community Forum
Forum Discription: A professional application framework using Caliburn.Micro and DevForce
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=4178
Printed Date: 06-May-2024 at 4:03am


Topic: DelegateCommand CanExecute and Async
Posted By: LowOrbit
Subject: DelegateCommand CanExecute and Async
Date Posted: 14-Jun-2013 at 9:06am
Hello
 
I have a DeletegateCommand object for a button I my UI. In the CanExecute method of the delegate, I need to execute a query against the database to see if a user is allowed to press the button based on the context of the data they are on. The normal signature of the CanExecute is a Func<bool>.
 
I cannot figure out how to call an async method that returns a bool. In order to return a bool, the signature needs to be a Task<bool> but then it doesn't match the DelegateCommand signature for CanExecute.
 
Can you provide some guidance or an example on how to specifiy a method for CanExecute than can execute a async method that returns a bool?
 
Thanks!



Replies:
Posted By: LowOrbit
Date Posted: 14-Jun-2013 at 9:39am
In thinking about this more, this may be one of those cases where async is not appropriate. The DeletegateCommand's CanExecute is basically asking the question, "Can this method be executed?". If a method "could" be executed asyncronously, you have no idea on when the answer can be returned. This would have the effect of your button/menu/control being enabled/disabled at random times depending upon how long it takes for the async method to complete.
 
The effect of this could be confusing, for example, if a menu item suddenly disables itself a few seconds after being displayed.
 
So I guess I'm looking for a little guidance on how to handle this scenario? I am using Cocktail and the UnitOfWork stuff. All queries are executed asyncronously. How would I execute the query and get the answer back to the delegatecommand when forced to do queries asyncronously?
 
Thanks!


Posted By: mgood
Date Posted: 14-Jun-2013 at 1:34pm
That's what INotifyPropertyChanged is all about. You let the view know to refresh the binding once you you performed your async work.

Button states typically get initialized during VM initialization and may change in response to a user action. Both can be asynchronous. Enabling/Disabling should trigger a NotifyPropertyChanged in the Command so that the view refreshes the binding. 

You simply perform your asynchronous query in response to a user action or during VM initialization and once the result comes back you enable/disable the command. You would typically disable all commands in the ctor, so that the user can't click anything until the view model is properly initialized and the appropriate buttons light up. 

BTW, why are you using the DelegateCommand instead of Caliburn.Micro actions? 

The following VM is somewhat of a good representation of what I'm talking about. Notice how UpdateCommands() is called in the appropriate places after something happens in the application and then UpdateCommands fires NotifyProperty changes on the various CanXXX properties to refresh the binding and have the appropriate buttons light up. 

https://github.com/IdeaBlade/Cocktail/blob/master/Samples/TempHire/TempHire/ViewModels/StaffingResource/ResourceMgtViewModel.cs - https://github.com/IdeaBlade/Cocktail/blob/master/Samples/TempHire/TempHire/ViewModels/StaffingResource/ResourceMgtViewModel.cs

The CanXXX doesn't need to be asynchronous. What matters is that you trigger a NotifyPropertyChanged when necessary, so the view triggers a reevaluation of the appropriate CanXXX properties.


Posted By: LowOrbit
Date Posted: 14-Jun-2013 at 2:20pm
I started down the DelegateCommand route and have since changed to the Caliburn.Micro way.
 
But I still have the same problem. In the CanXX methods, I need to fetch data from the database via the UnitOfWork and return a boolean. The return value of these CanXX methods is a bool. I have to await the fetch from the database in order to return the proper value which means putting async on the method.
 
You are not allowed to have a method defined as      async bool. You have to change it to "async Task" or "async Task<xx>". The DeletegateCommand CanExecute cannot point to this kind of method (hence the original question). Will the Caliburn.Micro CanXX version of a method defined as "async Task<bool>" work properly?
 
Thanks
Robert


Posted By: mgood
Date Posted: 14-Jun-2013 at 2:29pm
CanXXX gets bound to the Enabled property on the corresponding XAML control. That can't be asynchronous. I think you've got something upside down here. There should not be a need to query in a CanXXX method at all. You should do this query in Initialize and/or in response to an action that might change the result of that query. Store the boolean result in a private field that is returned by CanXXX. After running the query you call NotifyOfPropertyChange(x => CanXXX) to have the view reevaluate the binding. 


Posted By: LowOrbit
Date Posted: 14-Jun-2013 at 2:39pm
Ok, I understand. I was executing the CanXX on a ContextMenu in this case. Since this method usually only gets executed when the menu is about to be displayed, I was hoping to save a trip to the database.
 
Thanks!



Print Page | Close Window