Async Commands (and Async Relay Command) - EASY WPF (.NET CORE)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone singleton Sean here and today we're going to be going over async commands in WPF so I recently got a suggestion to go over this looked around on YouTube didn't see anything so I figured I would take a shot at it so what I have set up here is this login view model and I have this login command now ideally this login command is going to call this service I have here this authentication service and it's going to call this login method and as you can see this method is a task so we're gonna have to await it we need an async commands so just some quick background on commands in WPF there's really two ways that most people do them they either put all of their command logic into actual command classes or they use some kind of relay or delegate or call that command whatever you want to call it to handle their command logic and then just pass a callback from the view model into that relay commands so we're gonna be going over both methods in relation to asic commands in this video but whichever method you choose you're gonna have to start off by creating a new folder here for commands and inside here we're gonna have an async command base class so all of our async commands are going to inherit from this base class and this base class is going to take care of all the async things that we need to do so this of course is going to implement the I command interface so can't execute that basically just tells whatever this command is binded to whether or not the command can execute so say if can't execute is false then the button that this is binded to will be disabled so what we're gonna do for now is just leave this as true but we're gonna come back to this in just a little bit and customize this implementation and then we also have this execute method down here and this is where we're gonna put our command logic now in our case we need to call an async method so we need to await something and that's a problem because this is just a void so what we can do is make this an async void and this is a little bit different than most async await things because really you would want this to be an async task but if we do that that'll break this interface we need to leave this as async void and the big issue with async void comes with exception hand so say this there is an exception as you can see we'll just leave this here so this thread is an exception so any caller of this execute method even if the call is wrapped in a try-catch the try catch that this is wrapped in is not going to catch the exception and that's bad because exceptions should be handled and taken care of so you can put your application in a valid state so that's another thing we're gonna have to take care of but for now let's just continue to set up this base class so what we're gonna do in here is just a wait and execute async method and what this is gonna be is just an abstract method so we're gonna have to make this class abstract so it's gonna be an abstract method let's generate that and now any subclasses of async command base are going to implement this execute a sink and take care of their async work and there and we'll handle all of the plumbing of this async void in this base class as well as this can execute Nick and execute changed so now we have this base class let's go ahead and implement a login command and that's going to inherit from async command base and now we generate all the overrides and all we need to worry about is implementing this execute async so that's great because everything is taken care of in this async command base so for this login commands all I'm gonna do is get my login view model and I'm also going to get an eye authentication service let's generate constructor for all that and now let's do the login so we're gonna make this an async task so we can await in here and first off I'm going to set my login view model status message to logging in and then I'm gonna take my authentication service and call that async login method I'm going to pass in the user name from the view model and once that's done I'm gonna set the status message to successfully logged in there we go so that is my async command now let's go into my login view model let's create a constructor here and we're gonna set the login command to a new login command and that takes this view model and we'll just new up an authentication service right here ideally I would take this through the constructor but this is just a simple example okay so let's put a breakpoint right here and let's test out this wonderful async command all right so I'll put a user name here and we'll login we hit this breakpoint let's just go ahead and continue through here so we're logging in and then we hit the next breakpoint and we get the success status message so that's great we did all the async/await that we wanted to but there actually is a problem here and that is you could spam this button as much as you want and ideally you wouldn't want the user to be able to spam this button as many times as they want if the task is already executing so what we can do is go into our async command base and what a lot of async command implementations do that I've seen is they will change this can execute the false if the command is already executing so let's go ahead and set that up so we're gonna have a private pool for is executing and we're actually going to wrap this in a property is executing so the Gator is just gonna return is executing and then in the sutter we're gonna set is executing to the value but we're also gonna have to raise this can execute change so let's take that event and let's invoke it and this takes this as the sender and new event argues we don't really have any event args we'll just pass in default ones and that's going to raise this can execute changed and then whatever this command is binded to say a button it's gonna grab this nuke NX value and in our case a can execute value is going to be if the command is not executing so if it's not executing then it can execute and that's great so now we need to actually toggle this property so we're gonna do that in our execute method so before we execute we're gonna set is executing the trail and then once we're done we're gonna set is executing the false and for some reason I still have it's saying this is never used when clearly it is but maybe that'll go away another thing is I do this question mark here and what that does is if Ken executes changed has no subscribers then it's not going to invoke the event which is great because if it didn't have any subscribers and we tried to invoke this event it would throw a null pointer so now we have that let's go ahead and test this out there we go it went away okay so we're gonna log in and now we're logging in we can't click this button it's disabled I kind of have like a style issue here because my text stays white but anyways the button is correctly being disabled so that's great our agent command is looking better now the other thing when you do is take care of exceptions so what happens if execute async there's exceptions well let's try that out let's throw a new exception and we'll just say login failed all right so we log in and we throw this exception we continue in the application crashes so there's multiple approaches you could take care you could do nothing and just let your application crash and just assume that any subclasses of async command base handle their exceptions or you could wrap this in a try-catch like that and then do nothing inside of this exception handler so kind of just like a fire-and-forget but that might not be the best because it's always good to handle exception somehow so another thing you can do is create a constructor and this constructor will require a callback for an exception and we'll call this one exception let's generate a field for that and let's move this up here clean this up a little bit and now that we have this callback what we can do is whenever an exception gets caught we can call that callback with the exception and this pretty much forces the exception to be handled because we have this constructor and now our login command is going to need an action for an exception or a callback and then we pass that to the base class like that and now in our login view model we need to pass that one exception callback and what we can do is just put a little inline callback here and set the status message on the view model to the exception message so now we're forcing this exception they'll always be handled so we'll log in and we get login failed because that is our exception message so that's one way you can do it again choose whatever you feel is right for your application maybe you're okay with just fire-and-forget don't handle exceptions maybe you don't want to handle exception handling at all and you just want to make sure that all the derived async commands handle exceptions on their end but for now I'm just gonna leave this with the callback required so that pretty much sums up doing commands per class implementing your commands in a little class let's go ahead and actually do an async relay command or delegate command whatever you're the freak you want to call it so let's make this new item here and this is going to be that async relay command and this is going to be a public class and we're just going to inherit from our async command face of course so let's implement that abstract class and now we're also going to need that constructor as well and now this is a relay command so what we need is a callback to execute and it needs to be async as well so what this needs to be is a funk that returns a task and that test is going to be what we await so we're just gonna call this the callback and we need to get this from the constructor so let's put a parameter there for that and then let's set that feel that we just created and now we have this async function the call so all we need to do down here is actually await it so let's make this async and let's oh wait this callback function so we call this function right here that returns a task and then we await it and let's test this out in our login view models so what we're going to do is set the login command to be a new async relay command and that's gonna need our callback so we're just gonna call this login that's a method that we haven't implemented yet and then we still have this own exception so let's just copy that from up here paste it right there and let's create this callback that we want to call in our async relay commands let's generate the method login and all that's gonna do I'm not sure just going to grab this from my other login command and paste it in there and we need an authentication service and what I'm actually gonna do is just create a new one right here like that and then we don't need to reference the login view model because we're inside of it right now so we can just grab all those properties and there we go so we should execute this login method let's go ahead and test this out all right so we login and there we go we're in this login method we can continue we're logging in now button is disabled that's great and then we finally log in and it sets the status message and that is great we can also throw exceptions in here like that and let's see how this goes so we log in and log in fail it so that's great that is our async commands so the most important thing is this async command base we keep track of if the command is executing and if it is executing then we cannot execute we can only execute if it's not already executing and we correctly set that property before and after we execute and then we also did some exception handling multiple ways you can do this you could force a callback to handle the exception you could get rid of this and do a fire-and-forget or you could just not try catch at all and force all of the subclasses to implement exception handling anyways that's going to wrap up async commands if you have any questions criticisms or concerns be sure to leave them below in the comment section I hope you guys learned something from this video and if you did be sure to leave a like or subscribe for more thank you
Info
Channel: SingletonSean
Views: 5,081
Rating: undefined out of 5
Keywords: wpf, programming, visual, studio, xaml, custom, control, generic, system, line, display, dispatcher, timer, template, binding, behavior, c#, how, to, series, tutorial, easy, time, reusable, maintain, package, design, part, dependency, property, attached, event, converter, code, metadata, framework, register, static, state, default, clean, view, sizing, responsive, style, grid, wrap, panel, stack, scroll, viewer, bar, card, component, first, measure, width, usercontrol, command, async, relay, delegate, callback, exception, handling, func, action, task, void, model
Id: dbh1st68Tco
Channel Id: undefined
Length: 14min 14sec (854 seconds)
Published: Sat Jul 18 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.