Build WPF AsyncCommand using C# async await, Task, and MVVM

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everybody and welcome back to another episode of design develop share as always i'm david anderson and today's video this month is going to be a little bit about building blocks in wpf with the model view view model pattern and so what we're going to accomplish today is we're going to build a asynchronous command so a little bit of infrastructure stay tuned because this is going to be a little bit different than an async command you might find out there on google and or a bing search so in front of me on the screen what i have here is actually how i do software design so this is how i design out classes and their properties and methods for a software team this is what they would typically consume and so here we've got the design for an async command we've got the corresponding interface so iasync command and what we're going to do that's different about this is we're going to actually keep track of the num basically we're going to keep track of the running tasks that the async command creates and executes and we're going to use that to allow the caller to have more control over whether the task can execute because the fundamental flaw that i think a lot of async command implementations have out there is they make a pretty big presumption that you only ever want exactly one and only one asynchronous task running through that command when in fact there might be situations where you also want multiple asynchronous tasks running at the same time all executed from the same command on your view model so we're still going to be able to accomplish running exactly one asynchronous task but we're also going to be able to do the latter so let's take a look at this we're going to build i async command we're going to add a running tasks read only property that returns an i enumerable of task we're going to remove the messy object parameter stuff that we don't need for now because we are going to mirror this design and also build a generic version of the async command and get rid of the object parameter that i command traditionally has in favor of a generically typed parameter so for the first one though we're not going to have a parameter we're going just have can execute that returns bull execute async that returns task the implementation just a few notes here we're going to accomplish that by implementing i command xq and can xq explicitly we're going to hide those away so essentially what will happen is when wpf invokes the command it'll pass a null value for the argument and basically the parameter is essentially disregarded in our implementation and then we are going to hook into the command manager record suggested and i have some advice for you guys on whether you should do this or not and call command manager invalidate record suggested or if you should take an alternative approach to raising the can't execute changed event handler manually from within the command and we'll talk about that here in a moment so let's go open visual studio and let's add our first class file so this is actually going to be our interface and this is going to be i async command we're going to add this to our windows name space so fault track dot shell all this source code will be available on the azure devops project so the first thing we're going to do of course is inherit from i command and we're going to add our members so we're going to have of course bool can execute and we're going to have task execute async either one are going to take parameters [Music] that's our interface the only additional thing we're going to add is the i innumerable of task we'll call that running tasks [Music] read only property [Music] now let's add our concrete class implementation async command of course we're going to implement the interface now i'm going to use resharper for this just to get the boilerplate code there and out of the way we're going to go ahead and remove a lot of the default regions that it adds we'll clean this up a little bit and the very first thing we're going to do is move the property running tasks in the event up at the top kind of separate this out a little bit for the event handler we're actually going to use the event body syntax we're going to add a add method body and this is going to [Music] basically we're going to hook into the command manager record suggested event and then same thing for remove [Music] unsubscribe from the event so we're basically just delegating the excuse changed event into requery suggested essentially for running tasks we're going to create a private observable collection and we're going to need an observable collection for how we're going to implement keeping track of running tasks here in a moment but this will be an observable collection of tasks we'll call this running tasks lowercase r we're going to initialize that in the constructor [Music] this will also be actually protected because we are going to actually make this abstract and we'll simply initialize our observable collection and then for our property we'll go ahead and just return that backing field like so now for the traditional i command implementation for can execute and execute we're going to hide those ways so this public bool can't excuse actually going to become bull i command can't execute we're simply going to return the value of can execute without the parameter do the same thing with execute this will be i command execute and same thing here we're simply going to [Music] execute async now what's different about this one is this is going to be an asynchronous task so this is actually going to be async void this is a fire and forget method so when wpf when you click on a button and you have a command binding and it executes this function it's fire and forget you can't wait on it so we are going to do async void fire and forget we can't change the message method signature but that's okay so we are going to await our execute async however and there's some additional work inside of here that we're going to do here in a moment so the other methods basically become abstract we want to delegate these to a deriver who's going to implement them themselves so both of these will be abstract like so so we have a basic boilerplate for an async command but what we really want to do now is basically give the caller who's going to or the driver or the implementer if you want to call it we're going to give that person the control to determine whether this can execute or not whether they think it should execute if there's already a task running maybe in that case it can't execute or maybe they want multiple tasks to be executed by this command we're going to give them that control so to do that we're going to come back here to our execute method we are going to actually instead of awaiting execute async we're going to go ahead and store that into a variable and call that running task because as soon as this line of code hits whatever is being awaited inside of here is already going to be running but we're just going to capture that and then we're going to add that task to our collection now what we're going to do is say try a weight running task we're going to add a finally block and we're basically going to then remove that same task from the collection so when the task is complete whether it's faulted or ran to completion we're basically going to remove that at the end so what this does is it allows the implementer then to check the number of running tasks or they could even check the state of the tasks inside the can execute that they're going to override and implement they have that control now that's kind of the key main difference between this implementation and many of them out there the other thing we're going to do is because we are hooking into record suggested and because this is going to depend on the running task collection on whether this will be able to execute or not we are also going to have to notify the command manager that we need to re-evaluate can execute when we add and remove a task for that collection so to do that we're going to subscribe to the collection changed of our observable collection just create a method for that we don't particularly care about the action in this case although we could but we're basically going to say command manager invalidate rate query suggested so that will notify the command manager that it needs to re-evaluate can execute and that will allow the ui to react to enabled and disabled control states based on that now the alternative approach to this is this has been the source of some controversy in the past because it does have some performance considerations so here's my thing and my advice and my recommendation to anyone doing wpf and video this is my recommended approach for commands hook into this because then you hook into the default behavior of the command system in wpf which is anytime keyboard input or mouse input or control focus changes it will automatically re-evaluate the can execute changed of our commands and we want that otherwise you have to program calls to raise this event all over the place now the fundamental rule that a lot of people violate when they implement commands is they write a lot of slow executing code inside can xq that's where they make the big mistake don't do that all code you write and can execute should run and return literally in a millisecond maybe two max cpu ticks preferably but that's the mistake people make and why they have performance issues taking this approach as long as you abide by that fundamental philosophy of always only writing fast running code inside of here you'll never really have any problems even with a large number of controls that means no database code no async code no networking no long running cpu intensive tasks no disk io keep it short and sweet it can however as a piece of advice and a practice it can utilize properties from a view model as long as those properties are computed and their events are raised to the property changed event it's okay to reference those from here but that's where people make the mistake however if you do have performance issues you can take the alternative approach which instead of subscribing and hooking into record suggested you could simply expose a raise can't execute changed function from here that then you can call from your viewmodel to manually do it when properties and state of your viewmodel changes but for the most part 99.9 of time i feel like this approach is perfectly fine and acceptable i've written a number of applications that are out there today that have a large number of controls and different views and and they're fine as long as you abide by that principle of only writing really fast exchanging code in here video for another day on how to handle slow code or networking or async or something like that with cad execute video for another day so moving on we have our boilerplate async command so we're going to go ahead and copy this implementation we're going to go ahead and create our generic typed version here so we're going to take these two files and going to copy those i tend to follow some of the older microsoft practices that are around in the net source code code base and add a little tick mark at the end to represent the generic version of the interface we're going to make this a i believe it's a contravariant or covariant i want to say covariant so we're going to say n of t and we're simply now going to say t parameter for both of our methods here we'll do the same thing inside our async command the actual implementation [Music] go ahead and add our generic type parameter and now we simply are going to add our parameters to our abstract method so t parameter [Music] now we need to pass the parameter from the i command implementation but we're going to cast it to type t and both can execute and execute so now we have a version of a an i async command that can take a parameter and a version that doesn't so that's just really nice because you don't always need parameters and it's just kind of an annoyance i found what will happen with the version that takes a parameter is if you don't need it it'll pass null same thing in the non-generic version it'll just pass null and we simply disregard it in the implementation of the code so that's all we have to do here we have our async command implementations now so what we're going to do is actually test this out and show you that it actually works so to do that i actually have a git stash just to expedite the process here we're going to go ahead and apply this stash this is simply going to give us a couple of changes to the project i've implemented a status text property we're going to display some status in the lower left hand right lower left hand corner of the application there's going to be a open menu command we're going to look at that in a little bit more detail here in a second and then of course we've just got the rest of the boilerplate stuff that kind of wires everything together i'm not going to go through that you can download the code link in the description below and go through that later so let's go ahead and apply that stash this also is the get cracking git client from axosoft highly recommend it it is a phenomenal get client [Music] okay looks like i made a little type of oh we need to implement our type parameter for our interface there sorry about that okay so our solution builds so we're gonna go ahead and just run this just so you can see so we have a little menu item up here and note the left hand lower left hand corner down here i'm going to click the button it became disabled we've got some stats text and you can see after a short delay that status text actually changed and all that happened just by our implementation of our async command and that is asynchronous code so let's go look at the implementation of that command and go up to the desktop app here and if we go to the commands folder this is also my practice that i do not like putting the implementation of a command i tend to avoid using action commands or delegate commands nowadays and i tend to prefer to put commands in dedicated classes because in a larger code base view models get way too much code they really should be lightweight i do have other videos i want to do to talk about that but we have an open menu command this is instantiated from the view model that it belongs with basically and in here now you can see for can xq we've over written that and we're able to return whether or not running tasks the count is equal to zero so if there are no tasks currently running then can execute will return true otherwise it returns false and that's kind of the value of capturing each task object as it gets begins to execute through the command system and then we have our implementation of the command itself the execution where we just simply change the shuttle status text and then just to mimic the asynchrony we await a 2 000 millisecond delay and we have that task there so very straightforward we could run this and there's our default behavior and we can go back and we can change this so if we were simply to return true we would then be able to run as many tasks as the user can click on the button as fast as they can click so maybe you have a feature where you want a user to be able to queue up one to ten different events or operations just depending on your requirements so now if i click on this a bunch now you can see we've got running tasks and they just all go and do their thing and return back and then eventually the ui updates all throughout the process now obviously in that case you'd have to handle your own synchronization and you basically might have multiple threads and multiple background tasks going on so you'd have to kind of handle your ui synchronization or whatever you want to do but i can imagine some scenarios where maybe like i want to save five files maybe i queue off a task for each file save over the network or something like that but anyway guys that is the objective that we wanted to set out to do for asynchronous commands using windows presentation foundation and the model view v model pattern i hope you found this helpful asynchronous code in mvvm can be very challenging but they are just normal commands so before we go let's show you one last piece which is actually the view model itself so we've got the shell menu view model and there you have it there's a basic i async command we instantiate that in the constructor and because i async command is also i command you can date a bind to it and your butt and click event like you would anything else so it's a very straightforward implementation but remember that key characteristic difference between this implementation and what's out there is the fact that we are not making an assumption that the caller or implementer only wants exactly one asynchronous task running at any given time so guys if you enjoyed this video or you found it helpful remember to hit the like button below if you love the content you want to keep keep up to date for more down the road definitely consider subscribing to the channel and in the description below the video there will be a link to this project on azure devops where you can get have access to the source code and run the project you can download that and play around with that and guys thank you for watching and we'll see you next time you
Info
Channel: David Anderson
Views: 9,926
Rating: undefined out of 5
Keywords: async command, async await, async await wpf, async command wpf, asynccommand wpf, wpf async command, asynchronous, async, programming, async await c#, c#, dotnet, .net, .net framework, wpf, windows presentation foundation, csharp, mvvm, c# tutorial, c# async await, visual studio, xamarin, c# programming, asyncronous, c# course, learn c#, tutorial, wpf mvvm, mvvm wpf, how to, xaml, icommand, design patterns, wpf tutorial, wpf tutorial c#, architecture, code, wpf asynccommand, icommand wpf
Id: F7hRmbdE9eY
Channel Id: undefined
Length: 19min 0sec (1140 seconds)
Published: Mon Nov 23 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.