Commands and Events - Refactoring WPF Window Code for MVVM
July 22, 2024
• 1,225 views
For me, MVVM makes a lot of sense with binding until I have to work with window controls.
Then it all falls apart.
Things seem to get weird when we want to have actions that we're hooking up to. How do we show a window? How do we close one? How do we know when the window itself is closing?
These are all questions that I'll answer in this video. Let's see how to make it happen with commands and events!
View Transcript
historically I've always found that working with Windows and view models inside of WPF can get really confusing controls seem to make sense but windows are where things start to fall apart hi my name is Nick centino and I'm a principal software engineering manager at Microsoft I put up a couple of videos recently about having something like mvvm but also using classes that act like a presenter to be able to control the different things that we have in our user interfaces in those videos I was showing a little bit of a messy situation with directly accessing controls and being able to Port some things to view models in this video I want to close off on the splash screen series by describing how we can try to transform some of those interactions especially when it comes to the window type control and how we can have
mvvm or binding to take care of some of those things for us that sounds interesting remember to subscribe to the channel and check out that pin comment for my courses on dome train with that said let's go check out this bright magenta splash screen one more time all right if you haven't seen my previous videos I'll put a link right up at the top here you can go check them out and come right back to this one but what we're going to be doing is taking this very beautiful splash screen and trying to correct or improve upon some of the interactions that we have inside of this presenter class to explain what was going on in this presenter class before you'll notice I have a couple of spots where I have some commented code out and I'm going to talk about some of these interactions
that we used to have what we used to do in our presenter was we would take in the Splash Window directly so the splash screen presenter was having a direct reference or direct know knowled specifically about WPF controls now in my personal opinion when I'm designing user interfaces specifically at the presenter level I do personally try to abstract away the idea that we are working with WPF now it's not always the case this is a goal of mine to be able to move in this direction but I will cut Corners here and there if it's going to mean that I can make progress on my application I figured with the original splash screen we had we could get away with directly accessing the window but I wanted to move away from that so you'll notice I have this interface here that's just called I splash
screen and this is the only thing that we're going to depend on for the splash screen itself but you'll notice the splash screen has a view model on it right so I splash screen is exposing the view model this is very much like a WPF control exposing the data context the data context on a WPF control is public but it's also just an object so we kind of lose that strong typing with my interface specifically for the splash I'm just exposing up the view model that I want to have instead of just an object data context now you'll notice that all that I'm using in here is the view model directly so you might say well hey Nick why the heck aren't you just passing in the view model why did you pass in the whole ey splash screen if really all that you wanted
off of that was the view model itself and to explain that very briefly it's just the way that my dependency injections working if I only pass in the view model there is nothing else that's going to resolve the actual window that we want so the window never gets created and I'm just passing in a view model so using the I splash screen just the way my dependency injection container is set up is going to forcefully resolve the control as well so I just wanted to give you that bit of background this may look very different in your applications depending on how you're setting up dependency injection and truly if your dependency injection looks different than mine you would probably not need to pass in the whole window itself in terms of the interface but that's how mine is set up to do resolution this is
just a brief Interruption to remind you that I do have courses available on dome train focused on C so whether you're interested in getting started in C looking for a little bit more of an intermediate course focus on object-oriented programming and some async programming or are you just looking to update your refactoring skills and see some examples that we can walk through together you can go ahead and check them out by visiting the links in the description and the comment below thanks and back to the video like I said previously if we scroll a little bit lower into the main method of this presenter when we go to show the splash screen asynchronously we used to be just calling stuff on the window to go show it right so you can see there's a window show we were closing the window so these are directly
accessing WPF control pieces right um the progress reporter this is just a small note uh the progress reporter needed to operate on The View model so I'm just uh doing that directly now um because we don't have the whole Splash Window that's okay but the other thing right here was we were changing the window style directly on the control as well and again if we think about the code that's here there's nothing that's technically wrong with it this code worked the fact that I was able to modify the control directly from the presenter this code was functional it served its purpose we have a splash screen that can show for a period of time allow background work to run close after a period of time it's all functional so as I walk through this I just want you to think that like it's not a
matter of being right or wrong because at the end of the day the software is working we probably could have written tests on in a reasonably straightforward way but these are just design practices that if you want to start moving more in that direction here's how you're able to do that how do we get to the point where we can start getting away from things like show and close because that's what I want to talk about primarily in this video this has been something that's stumped me for a long time in WPF every time I take a break from WPF and go back I go hey how was it that I was able to get binding to work or have events and commands working to be able to show Windows because in my opinion binding a property on a control feels pretty straightforward right but
when it comes to taking an action like I want to go show a window what am I supposed to bind to now I'm going to walk through how I like to use commands and events in terms of the patterns and practices there this might look different for you there could be gaps in the way that I have them set up in mind but this is just roughly how I like to approach it so as I've kind of alluded to here commands and events are going to be the thing that really help us with these close and show methods and the way that I like to do this is that we have a command but there is also a corresponding event that goes along with it so we're going to jump over to the view bottle next but I wanted to show you kind of where
we have a close command being called when we wanted to show like we're asking to show the window up here and we're asking to close it and I'm phrasing it that way right on purpose we're asking to do this from The View model hey i' like to go close this window okay with that said let's go look at the implementation of this if we jump over to the view model itself you'll notice that I have three commands I just talked about two but we have three one of them is close and one of them is open these are the ones that we saw being called from the other spot on The View model and the terminology that I like to use is when we say close command this is going to be the view model requesting that we want to have something get closed same
with open so you would say open command and that would call something that would request that we go open if we look up here you can see the open command is going to invoke this event called request open and the close command does request close so you can see request open and close as events right here but what the heck is this other one right the closing one and this is just going to be something that flows in the opposite direction so we'll see that in just a moment but just to recap so far we have a close and open command and they will call request open or request close close but who are they requesting it to and how is that going to work so if we scroll up this is my window class and you might go oh wait hold on Nick you
were talking about mvvm you were talking about controls and view models and you have code behind you have code behind you're breaking rules this must be bad and I'm just going to say hey look this is how I like to do it I'm not against code behind in my controls but you'll notice that I'm just wiring things up truthfully I'm just being able to kind of go between one context and another one and I'm not trying to put business logic in here that's going to be making decisions about things so all that I'm doing in the Constructor of my window is I'm wiring up to the view model when someone requests to close we will go close when someone requests to open we will go show there are ways that you can do this if we start talking with the other direction which is down
here for on closing you could hook up to the events on the control itself and you could say hey when someone is uh closing this window so someone's pressed the X right we could do binding in the zaml and there's I can't I think it's called interactivity is the is the name space yeah I believe it's that's what it's called and you're able to basically wire up event handlers to commands I don't like doing that because if you watch my previous videos I don't like putting more logic or set up into zaml I know zaml is supposed to be able to give us these benefits but I find the more stuff I put into zaml the harder I find to test it and work around some things so usually I shy away from stuff like that personal preference that's my bias but that's why having
something like this where I'm just overriding the on closing and then invoking or I guess executing this closing command that's my preference the view or the window in this case hooks up to the request methods on The View model and that way it can handle them and say hey look someone's requesting to open we'll open someone's requesting to close we'll close but the window itself is responsible for knowing when it is closing right there's literally a hook Point here with this override to say we are closing what do we want to do so when the window itself is closing it will go tell the view model so this is working the other direction hey view model I'm closing so the closing command will get executed when this happens if I go back into the view model the closing command is going to invoke this event
so these things flow the opposite direction from the original ones that we just looked at when we're requesting the view is going to handle it and then when the view is doing the action and actually closing it will go flow commands and events the other way if we go look at who's hooked up to closing well if we go back to our Splash presenter if we go right up here you can see that we're hooking up to that closing event right at the top this used to be closing right on the control itself but this is now closing on The View model I wanted to kind of paint that picture to show you the direction that the commands and events are flowing one way and then back from the control itself the other way that should cover show it should cover close right those are
two things I mentioned this part originally is now just kind of replaced because we have the view model directly so nothing too fancy there but this part's still a little bit weird because we were directly changing the window style What I've Done instead is conceptually the way that I labeled this was on The View model like when we were doing this on the window on The View model I just want to indicate hey look we can close this window now like that's what this means once we reach the state we're saying this thing is allowed to be closed because before that we're still processing right that was the point of our splash screen we'll hold it open while it's processing once it's done that background work we can say hey look you're free to close this thing so conceptually that's what it's doing but what
does it mean in practice this so let's go have a look back at our view model we'll see this can close Okay so it has an onproperty changed hookup uh kind of a typical setup here but you'll notice that it has one extra property changed and that's because I'm exposing this Windows style can close that means if someone can close we will go on property change so anyone listening to Windows style will get notified hey look someone changed can cloes that means if Windows Style sty is depending on can clo we must tell people listening to Windows style that things have changed it's a little bit weird you have to know that if you have dependent properties like this to go uh call this extra on property changed but that means now if we go to our zaml you'll notice the window style in our
window is now bound to window Style on The View model jumping back to here one other thing I wanted to call out this is where um levels of abstraction at least in my opinion get a little bit bizarre I'm just going to explain this very briefly but one thing I said at the very beginning was that I don't want my uh presenter to have to know about WPF Concepts right now one kind of thing that I am breaking a bit of a rule on back here is my view model is technically exposing things like window style very much means even if I hover my cursor over you can see window style comes from system. windows. Windows style now if I really wanted to continue to break apart that dependency and that level of abstraction I could and I very much have before in other applications
done two levels of view models and this is going to sound a little bit ridiculous but I just wanted to kind of mention it to give you some food for thought if you truly want to make it such that your view model itself feels WPF agnostic what you can do is have two levels of view models and that way at one level this would not exist on this view model but you could have a WPF specific view model and it would wrap this one it would encapsulate it entirely and that way it can subscribe to when the property has changed for can close and it exposes this window style one now what that means is that the splash presenter itself only cares about the non WPF one and why well that's because it's only setting can close it doesn't need to know about the window
style from its perspective if it's just saying hey you can close this thing and then if you have a WPF wrapper around the view model itself what you're able to do from there is be notified when the can Clos property changes and propagate even further again back up to this whole window that your window style has changed what that means is that the window itself takes in a view model which is a WPF specific view model so window WPF view model inside of that is the more specific view model that is not WPF dependent is that Overkill a lot of the time yes like to be completely transparent a lot of the time yes but if you are in a position where you're trying to make user interfaces that are WPF agnostic especially the business logic you can introduce that extra level of Separation just
to recap what we were able to do in this video so far is being able to get rid of a dependency on the Splash Window so we'll get rid of that we don't need to have show called and we don't need to have close called anymore because we're invoking or calling execute on the open and close commands on The View model these two commands will go call request events those events have event handlers that are hooked up inside of the view itself right so if we go all the way up here the window itself will hook up to these two things for the lifetime of the view model because they have the same lifetime the way I have these set up they will call close and show respectively on the window itself the other thing is on closing invokes or calls execute on closing command
so flows things the exact opposite direction and that means the presenter will eventually get this called because it's hooked up to that on The View model we don't need this code anymore and we don't need to do uh this part because now we can just say that we're all done and we can close so if we go run this we should see that we have all of our codes still functioning and there we have it a beautiful splash screen flipped over the window style and it closes on its own so it's still completely functional we've just moved away to being able to hook up to the view model itself instead of the view I hope you found that helpful thanks so much for watching and I'll see you next time
Frequently Asked Questions
What is the main goal of refactoring WPF window code for MVVM?
The main goal is to abstract away direct dependencies on WPF controls in the presenter class, allowing for cleaner code and better separation of concerns. This helps in making the application more maintainable and testable.
Why do you use commands and events in your MVVM implementation?
I use commands and events to handle actions like showing and closing windows in a way that aligns with the MVVM pattern. This allows the ViewModel to request actions without directly manipulating the view, maintaining a clear separation between the UI and business logic.
What is the significance of the I splash screen interface in your implementation?
The I splash screen interface is significant because it allows me to define a contract for the splash screen that exposes the ViewModel, without tightly coupling the presenter to the actual WPF control. This abstraction facilitates easier testing and enhances flexibility in the code.
These FAQs were generated by AI from the video transcript.