Build a WPF MVVM Application - START TO FINISH TUTORIAL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
alrighty i am excited because today we're going to be building a wpf application so i got this comment from alan v and they said how would you go about having a three part layout where one side is used for displaying data and the other has a mini nav bar to add edit or remove objects so that kind of sums up a master detail view which is what we have here so this is like the mini nav bar that they mentioned where we have a list of youtube viewers and they can use this drop-down to edit or delete the user and then they can select the user to view the user's details over here and then if they want to add a user they can click this add button and i expect this to show some kind of moodle where they can fill in these details and submit the viewer and they'll get added to this list so that's what we're going to be building and quite frankly i haven't really planned this out other than this ui but i think that's okay this is going to be a relatively small application we're also going to be hooking up to a database so that's going to be fun like a full application we got going on here so yeah overall this is going to be a pretty long video i might take a break or pause to think about stuff here and there but other than that what are we waiting for let's hop into this so moving over to visual studio like i said literally have nothing right now so i'm going to start off by adding a new project and this is going to be a wpf application i'll just call this youtube viewers since that's kind of the theme of the ui but for my project name there's going to be youtubeviewers.wpf and then solution name is just going to be youtube viewers and then right now we're targeting net5 i think that's good this should work on future versions of net if you're watching from the future but let's go ahead and create alright so some quick setup i'm going to name my main window youtube viewers and then i'm going to head into my app.xaml and remove this startup uri because i don't like to start up by just referencing the main window like they have by default i like to do all of my startup configuration in the app.xml.cs because there's just way more flexibility like you can configure services in your startup and you can set up your data context and all kind of application state in your app.xaml that's yes so i'm going to override onstartup here and what i'm going to do is i'm going to set the main window for my app to a new main window and main window is just the window that we have to find right here and we're just instantiating that using this constructor so i think that's good on setup let's run this make sure our app actually starts oh i already realized we did miss something so we have to do a mean window dot show so that actually shows up and now we should see our window and yay all right we got our app rolling so this is going to be a mvvm application so we're going to have some views let's add a folder for that and also view models and we'll just focus on these two for now we'll bring in models and stores and other stuff like that later but i think overall i want to start by building the ui so let's add a new view here so this is going to be a user control that's how we build views in wpf and we put our user controls in the window but i'm going to call this the youtube viewers view i guess that's kind of weird the youtube viewers view a lot of you in here but i don't know that's the theme we're rolling with we'll stick with it and then just to reference our ui so this youtube viewers view pretty much represents like everything so it's gonna have our title our add button and it's also going to put together these two big components that we have right here so these are actually going to be separate components they're not going to be defined in the youtube viewers view so let's create those components let's put that in the components folder i would like my views folder to be like the main views i guess i'll center them for view could be like a page it's the entire page but anyways the view is going to have components so let's add a new item in our components folder this is also going to be a user control and the first component we're going to have is that listing component we have on the left so that's going to be the youtube viewers listing so create that and then the other component we're gonna have is the youtube viewers details so that's the details panel on the right youtube viewers details so this is pretty much everything we're gonna do to split up our main youtube viewers view but now we can start scaffolding this out so looking at the ui we kind of have two rows here so the first row is the title and the add button so let's have some row definitions defined here and one row definition is going to be height auto so just going to fill up as much as it needs and then the other row definition is going to have our listing panel and our details panel and that's going to fill up the remaining space so it's going to be star height so this first row has two columns to it so the first column is our youtube viewer's title and then the other column is our add button so let's make another grid here with this time column definitions so we're gonna have one column definition we can make this the width of star that should work so fills up all of the width and then our add button is going to take up as much width as it needs so our title is going to be a text block the text in our case is youtube viewers and this goes in grid column 0 which is actually the default but i do like to define it and then we're going to have a button here with the content just being add and this is gonna go in grid column one and we're gonna have to do a bunch of styling to this button so we're gonna need padding we're gonna need color but we'll deal with all that later so this pretty much sums up the header of our view with the title in the button the next part is going to be another grid which also has two columns so the first column is going to be that listing of the youtube viewers and the width for this we'll do star and then the other part of this second row is going to be the details of the selected youtube viewer so this takes up probably about two-thirds of the screen so this width is gonna be two star so what we have here is a total of one star plus two star so three stars and since this is one star it's gonna take one third of the width and since this is two stars it's going to take two thirds of the width which is pretty similar to what our prototype shows but in our first column we're going to have our youtube viewers listing component so if we do a control dot on that we can import our component's namespace so here we go we prefixed that with components and we imported that right here so pointing to our namespace of youtubeviewers.wpf dot components so that's good and that's gonna go in grid column zero and then our other component will go in grade column one and this is the youtube viewers details and we also have some margin in between these so i'll define that might be about 10 to the left i don't know we'll play with all this while we're like live debugging so now we pretty much have to head over to these components and fill those in with some ui and actually this grid right here should be grid row one and for consistency i'll make this grid row zero even though that's the default but let's head over to the youtube viewers listing and knock this out so looking at our listing we have a border that surrounds the entire listing and the background here not the background the border brush here we're gonna do like a light gray that's a little bit too light maybe just regular gray that should be good border thickness will just be one and inside here we're gonna have a list view and i don't know this is kind of hard to scaffold out while we don't really have any data bound to our list view so i think we'll come back here once we have like a view model that we can bind to and actually display some items in this list view i don't feel like scaffolding something out if i'm just going to delete it later so we'll just leave this for now and let's head into the youtube viewers details so we can definitely do this and same kind of thing we're going to have a border that surrounds everything and the border brush is going to be gray again so might end up extracting this gray to like a style so we don't duplicate it between both of these borders but we can do that later the border thickness is going to be one again and inside here we're just gonna have a grid and it looks like we got four rows in here so the first row first off we're going to need our row definitions and pretty much all of these are going to be auto height so nothing really needs to fill the space in the grid they're all just arrows of text so the first row is just going to be a text block with the user's name as the text we can just hard code this for now we're going to have to come back and bind this to a view model though but i'll just make this sean the font size is kind of bigger we'll go with 16 maybe we'll play around with all this in the future but this is grid row zero and then the next three rows have two columns so we'll have to make sure all of these columns line up but i'm gonna have each row and its own grid with column definitions and both of these column definitions can be with auto so again nothing really needs to expand and then the first column we have in our first row speaking of row let me add grid row one to this grid up here and the text here is gonna be youtube username or how about just username i feel like youtube is kind of implied here and then this is a little bit bold so we'll set that for the font weight and then we have another text block with the user's actual username wait this doesn't even make sense i already have the username defined as the title up here this is a little bit excessive i don't think we need this so we're going to skip the username row and we'll just make this is subscribed and then this next text block will be the value of that which is going to be a yes or no and we'll just hard code this to yes for now and then we're gonna have this entire grid again except this time grid row two and the question is is member and we'll roll with yes and we can remove this fourth row definition since we knocked out a row and let me change this to singleton sean since this header is the youtube username now so this should actually be good we don't really have any margins or anything to find here we'll just do that while we're live debugging i don't feel like guessing right now but i think we're good on our components and our main view now let's just define this in the main window for now so we'll open up this grid and plop in the youtube viewers view which we'll need to import from views there we go and let's see how bad this looks i don't think it's going to look super pretty we don't really have any styling or margin and yeah we're off to a great start so one thing we have like a really thick border right here and i think our list view should have a border thickness of zero since our border is being handled up here and yeah that takes care of that this youtube viewers should be way bigger so let's set the font size as maybe 28 maybe bigger how about 32 i think that's good this add button is all kinds of messed up i think we're gonna have to vertical align center because it's like growing to be as big as our text block so yeah there we go we'll need some padding maybe 15 left right and then five top bottom i think that's good we're also going to need i think bigger font size too at least looking at our prototype how about 16 i think i actually want these borders darker as well so in our components let's make these is there a dark gray let's do that for both of these borders that's actually lighter why is dark gray lighter than just regular gray i guess we'll roll with gray for now but when i extract this to a style we can play around with some hex values instead to get the right darkness that we want one thing i do want is i want these corners to be curvy so we'll set the corner radius i think 5 should be good enough and it looks like our list view is clipping over the border we'll fix that down the road that's so annoying i always run into that but i do know how to fix it and then this corner radius should look good for the details panel there we go we're going to need some padding on these details so let's play around with that i think we can define padding on our border let's go with 20 so kind of a lot i think that's good the username should be bigger i think let's go with 28 i want this to be big that's good well a little bit too big how about 24. i don't want it to be competing with like our main title and then let's get some margin between our rows on these details so we'll do 20 on the top for each of our rows i think that should look good oh this is left how about top the second value is top can't forget that and that definitely looks better and i don't know this whole font looks small i feel like i want my overall font to be not that small so can we set the font size on our window to 14 does that look good yeah that's bigger i like that i think the defaults like 12 that's a little bit too small but now i want a lot of margin between these so let's define that we'll do margin i guess 20 we keep doing 20 everywhere but i think that's a good value to start with we'll play around with it and that looks good next thing i want is these to be lined up so this is a cool trick in wpf so it's kind of difficult since we have two separate sets of column definitions so what we can do is come up to this root grid that contains both of the grids that have their own column definitions and we can set grid is shared size scope to true and then we can define one this column definition the shared size group so this can be any name you want we'll just call it label because this is the column that contains the label and if two columns have the same shared size group so these two columns do and they're within a grid that has is shared test scope to true then each column is going to have the same width depending on whichever column is bigger so this column is longer which means this column down here expands to be as long as this one so this makes it much more pleasant everything is lined up in these subsequent columns so this is definitely looking better we need some more margin on like our entire page so i think i do want like a default margin that is applied all the way on our main window so we can set that here we'll have the margin about 25 so a little bit more and obviously we want that on all four sides i think that's exactly what we want it's a kind of a lot to the top i don't know how i feel about that but maybe it'll look better if we add some margin between this top row and our listing and details row so in our youtube viewers view let's add some margin to the top of our listing and details components let's do 20 to the top i keep doing it to the left let's see oh that looks good okay and then lastly i think we need more margin between these what do we do we did 10. how about 20. 20 is like the magic number it seems to just be working that looks good okay so i think we're pretty close to what we wanted we still need some styling on this button and i do want to change this border color eventually and you know before we move into view models let's do it let's add some styles so in our app.xaml i'm going to expand these resources and have a resource dictionary in here and let's just start out with a color that we can have for our border so a solid color brush will give it a key so we can reference it we'll call it border primary and the color is gonna be we'll start with gray for now just to make sure that this brush works when we use it so now in both of these borders that we have we're going to point our border brush to a static resource and here we go we get some intellisense so border primary is what we want and there's the gray so it is working and then our other border is going to need that as well there we go let me run this because i want to make sure it does work i've had some issues with styles in the past and looks good okay so now we can change this to whatever we want let me see if i can get a hex value let me get like a color picker on google so we'll do a pretty dark gray about 1c1 c1c let's paste that in there as our color oh that's really dark i don't know how that's going to look that looks pretty good i like it it does kind of look like our borders are blurry though let me see i just did a google search because i was wondering why i don't have all the answers all the time and we should be able to do snaps to device pixels i have used this in the past and that doesn't seem to do anything let's try restarting oh okay yeah that definitely fixed it so now we can clearly see this border looks much better than this one so for our details we'll do snaps to device pixels as well to true and restart again and yeah that looks really good now so that should be good for the border styling we still have to fix these stupid corners but let's style this add button so in our app.xaml we're gonna have a style targeting buttons do i want to make this the default style i think i do so if i don't specify a key it'll be applied by default let's do that so our button is gonna have a foreground of white so let's set that using a setter and then we're gonna have a background we'll grab a hex value for this but i'll just go with blue for now and then we'll have some default padding as well i think we have done 15 and five and then we'll also set the vertical alignment as center by default same with horizontal alignment that should be good i guess while we're at it text alignment should also be centered that's not even a property i think we have to do text block dot text alignment i think we'll see all right so let me grab a hex value real quick or let me just grab the exact one from figma i guess i could have done that for the borders as well oh it's just black okay but for the color here we go 54 7 aff let me copy that and paste that here as a hex value there we go that looks good so let me go to my button and we can remove some stuff on here so we don't need vertical alignment that's the default we don't need padding that's the default and let's see how this looks so it looks pretty good we do need to change the border so there's still a border that should be zero and then when you hover over it you get this annoying highlight so literally the only way to get rid of this highlight and i feel like i've mentioned this in other videos but we have to override the default template for the button and while we're overriding the default template we can also mess with the border inside the template too so this property is template and for setting this we're going to open this up and define a control template the target type is a button and then we're just going to have a content presenter in here so by overriding this and just showing the content presenter that'll get rid of that annoying highlight and then surrounding this we want a border so let's add that i also wanted some corner radius on our button so we'll go with three not too much and then the background is gonna be a template binding to background so using template binding inside a template is basically just gonna point to whatever background we have set on the button in this case it's gonna be this blue color so we want to do the same thing with padding as well so make sure we grab the padding that we have set on the component we also want margin so let's template bind to that so let's see how this looks i think i want to add some animations to this button so we'll do that but let's just make sure that looks good okay oh we also want the cursor to change when we're above it i think we have to set a property the cursor that should be a hand i think that's what we want so that should fix that and then i want some animations whenever we hover so i want to change the background to be a little bit darker just to give some indication that we're hovering over it so we can have some style triggers in here and we're gonna have a trigger based on if our mouse is over the button and when that's true that's when we want to fire this trigger so when we enter this trigger we'll have some interactions here and we're going to start a storyboard so storyboards pretty much contain animations so we're going to define a storyboard in here and we're going to have a color animation and we're just going to animate to we'll do blue for now but we'll change this to look better in a little bit the duration will do about zero hours zero minutes and zero point five seconds that might be too long we'll see and then what are we animating so we defined that with the storyboard target property and we're going to be animating the backgrounds but the issue i've had this so many times with animations but basically background is a brush so if we pick the definition here we have brush so it's not a color so we can't just target the background we have to target the color of the brush that the background has and we can't just do dot color because since background can be a brush it could be any kind of brush it could be a gradient brush which doesn't have this color property so we have to cast this to a solid color brush and we can do that with parentheses and we want solid color brush dot color and for the most part we're going to be using only solid color brushes which is basically what you get if you define any kind of color as the background so that should be good for animating to the hover color but we're also going to need some exit action so let's change this and define exit actions and whenever we unhover from our button we want to set the background color to just the regular backgrounds so we can set that here and let's make sure this works so hovering over and oh yeah okay so it does work definitely too slow let me make this point two maybe that's good i don't know maybe even point one will be good but let me grab a better color because the blue is just not the right shade so let me paste in our regular blue and just grab something darker how about that it could be anything as long as it's darker paste that in as our hover color and try this again yeah it's still too slow let's do point one who would have thought yeah i think that's good just a tad bit of animation that's all i think we need all right so last thing i want to do before we move in the view models is fix these stupid borders all right so let's see let me move this over here and we're going to go into the listing where we have that defined and i've done this in the past and i have a project where i have done it so let me just copy that and i'll explain it so let me snag that so we use an opacity mask and we have to be inside of a grid so let me surround all of this with grid so we have an opacity mask and then we have a border that uses that mask and has the same corner radius as our root border and then we just have all of our content inside here so basically this mask and this border combined make sure that our content in here doesn't go over the edges of our root border so let's see there we go as you can see the list view is not covering the freaking corners all right so i think our ui is good let's move into view models so the first view model is actually we're going to need a base view model so let me add a view model base and this is going to have to implement i notify property changed so let's import that and implement this interface and basically all of our view models are going to raise this property changed event whenever a property changes and our ui is going to know to re-grab the value of whatever property change so i'd like to have this helper method to easily raise property changed we can make this virtual in case we have to override it it'll just be void we'll call it one property change it's going to take the name of the property that changed so the property name you can also have everyone points this out but i never use it because i don't feel like i have to i have a snippet that does the hard part but you can have like this caller member name here if you don't want to pass in a property name and get it passed in automatically let's see it's caller member name like that you can import that if you don't want to pass in this property name manually every time but personally i like to pass it in so i'm going to leave it as is and all this method is going to do is take our property changed event and if it has subscribers we're going to invoke it with this is the sender and new property changed event args which just take the property name and it does say property name is optional which is true so if property name is null then that means that it's gonna raise property changed and the ui is gonna grab everything that it's binding to not just a specific property so just a cool little tip there but for the most part probably gonna be passing in a property name so we got our base view model view model base let's add the first view model here is going to be for our youtube viewers view so this is going to be the youtube viewers view model and let's inherit from viewmodelbase so we can get all that property change stuff for free and let's go into our youtube viewers view and think about what we need so we're going to need a command to handle this button click so let's define that first it's going to be an i command this will be the add youtube viewer command and it can just be read only we'll implement all this later so that pretty much takes care of everything in the header but then we're also going to have to handle all of the bindings and all the data we have to pass down to these components so i'm going to wrap all of that in their own view models so in that case let's create some more view models over here so the first view model is going to be for our youtube viewers listing view so the youtube viewers listing view model right here and this is going to extend our v model base and then the last v model we're going to have is the u2 viewers details view model so same thing extend view model base which visual studio just did for me that's cool i like it so since our youtube viewers view has each of these components then we're going to need the view models for each of these components on our youtube viewer's view model so these can actually be read only as well so this is going to be the youtube viewers listing view model we'll just call it that so same name and can just be read only and we're also going to have the youtube viewers details view model same name can be read only as well and these can be read only because they're not going to change we can just initialize these in the constructor so just instantiate those for now and same with the youtube viewers details v model so we have these sub view models defined on here and ultimately we want each of our components to bind to whatever properties we have on those v models so to do that we're going to set the data context as a binding to each of these v models so we'll grab the name of the viewmodel and this youtube viewers listing view is going to bind to the youtube viewers listing view model and then we're going to do the same thing for the youtube viewers details that's going to bind to properties on the youtube viewers details view model so paste that in there that should be good for those property bindings that we have let's also bind to this command so our button the command for this button is going to bind to the command we have on our view model the add youtube viewers command so let's paste that in there we haven't done anything with that command yet but we will eventually and let's head all the way back up to our main window and similar to how i talked about data context for each of these components if we want to have all of these bindings available then that means the data context for our youtube viewers view is going to have to be some kind of youtube viewers view model so we can access all these bindings so since we only have one page in our application and i expect it to stay that way we can just set the data context of our entire main window to a youtube viewer's view model and then all of these bindings will get passed down to the youtube viewer's view but if we had multiple pages in our application and had some kind of navigation we definitely have to do something more complex if you're interested in that check out my navigation series which is like the full guide for navigation in wpf but we don't really have navigation in this app so what we can do is head up to the app.xml.cs and for this main window i'm just gonna set the data context to a new youtube viewers view model so import that all right now that we have all of our data context set up let's head into one of our component view models so i think i'm going to start with the listing v model so this is the view model for our youtube viewers listing and what are we going to have on here well we have our list view in here so we're going to need a list of youtube viewers so we're going to have a property on here it can just be an innumerable and the type what's the type going to be well we're going to need some other object here so we're going to need another viewmodel type so let me create that and these are going to be for our listing items so we're going to call this the youtube viewers listing item view model and this is also going to extend the model base and it's going to have a few things so first we're gonna have a property for their name so this can actually just be read only the username and just the getter it's not gonna change and then we're actually going to have two more properties on here these are going to be commands so i did point out in our ui prototype that we had the little drop down menu so this little three dot thing right here and that's going to open up for edit and delete actions so each of these commands is going to have to hook up to those actions so we're going to have an edit command and a delete command and all of these properties are going to be set up in the constructor so the username will actually come through the constructor we can just set that and then we'll set up these commands down the road but we're good on the view model for our listing items so we're going to head back up to our listing view model and the type here is going to be our youtube viewers listing item v model so we're going to expose a list of those and we'll just call this the youtube viewers listing under view models so pretty generic names we have here and this is going to be read only but we're actually going to point to an observable collection so we're going to define a read-only observable collection of these youtube viewers listing item viewmodels and we'll just call it that but this is just a field gonna be a private field and our collection that we expose to our view is gonna be this observable collection field but i am just exposing it as an innumerable so basically we're encapsulating this observable collection which means all the viewmodels that we add or remove or change is going to be done within this v model so again just encapsulating and then we'll instantiate this observable collection in the constructor we'll make it empty for now but we're going to hard code some data in here so why don't we actually just do that now so we'll add a new youtube viewers listing item view model so we can throw some usernames in here i think my prototype had like mary sean alan so that's who actually recommended this whole master detail thing so props to elan but this should be good and i do want to mention the thing with observable collections so the reason we use this instead of like lists and stuff is because our ui is automatically going to update whenever we add or remove items from an observable collection so that's the main power of observable collections so now that we expose this innumerable we can bind to this in our youtube viewers listing so our list view the item source is gonna be a binding to our youtube viewers listing item view models and we're gonna have to do more here we're gonna have to define how we want these items to be displayed but let's run this and see how this looks and all right perfect so this is what i expected so it's just doing two string on the view model which doesn't really do anything it just prints out the namespace and name of the class so we're gonna have to change how these are displayed and we could do that by defining a list view item template on our list view so inside here we'll have a data template and inside of our data template we can pretty much do whatever we want so we're just gonna have a grid and based on our prototype this grid is gonna have two columns and column one can be with star so it'll fill everything that's gonna be the username and that's gonna make our drop down menu get pushed all the way to the right so with that we're gonna have a text block the text is gonna be a binding to user name so right here inside this data template our data context so what we use to access bindings is going to be our item view model so we can access these properties and bind to them inside of here and it's going to be grid column 0 and then i'm just going to scaffold out the drop down menu and actually i want to use a nuget package for this so i did a ui workshop where i built a drop down menu with those three triple dots so let's go ahead and install that so we can manage nuget packages this is a good example because we haven't done anything with nuke packages in this project yet and what did i call this i think drop down menu.wpf is what i called it yeah here we go so 69 downloads this is by singleton sean let's install that so success and now i think i just do drop down menu and we can import that from our namespace so it imported it as drop down menu control we'll just call this i don't know custom and change that down here and this drop down menu so it's gonna be grid column one we should also have some margin to the left we'll do ten and for now the content we'll just say like hello world how about that so we'll style this in a little bit but i want to see how this looks so let's run this i think we did everything oh there's one more thing we have to do i should have done it okay but yeah this kind of works we got our username which is definitely binding correctly to what we hard coded that's good but we should get some padding between these so let's get some padding i think we just want oh you can have padding on a grid my bad so let me surround this by a border that should be good so border will have some padding i think we want a little bit on the left and right so let's do five and then top bottom we'll do 10. that looks good it looks like the text is kind of small maybe we should set that so text block dot font size let's go back to 16 yeah let's do that i like that but to push this little drop down menu which is working there we go hello world we're gonna need a background and border and stuff on that but to push this all the way to the right even though we did get our column definitions correct list views are kind of weird so we have to set the list view item container style so open that to a style targeting list view item and we have to set the i think it's horizontal content alignment to stretch which i would think it'd be stretched by default but it's not so look at that there we go maybe we do want more padding how about we just do 10 all around yeah that looks good okay so this drop down menu definitely needs to look better let's surround this with a border and the background should be white i think it's transparent right now whatever's in there let's have a border brush i don't think i want this the same as the borders that i have for my details in my listing panels let's just make this regular how about gray and then border thickness just one let's see does this update oh yeah there we go okay and also some padding and actually no i don't want padding here i want padding on my actual buttons that i have to find in here so we are going to have two buttons one for edit and one for delete and instead of text this is content that's just how buttons are and we have too much stuff inside of a border we're gonna have to wrap this in a grid so let's surround with grid or also or let's make it simple let's do a stack panel so we don't have to have like grid of rows and stuff i feel like that's unnecessary so just a stack panel just stack these on top of each other and let's see how does that look oh it's doing our default button style so maybe i shouldn't have made that style the default but regardless these buttons are going to have their own styles so i want the same style for both of them so we can have a stack panel resources and any style we define in here is going to be applied to anything inside of the stack panel which is what i want so we're going to have a style targeting buttons and no key so this is going to be the default style and actually i do want a lot of the same stuff from our default button style so we can base one our default style so referencing a default style we can do static resource i think it's x type button that should point to our default button style let's see we should still see the yeah okay it is working all right so we're basing off our default button style and the things i want to change is the background so let's set the background as i guess gray for now we'll have to do something lighter we'll bring in some hex values so how's that look that is working uh whenever we hover that's not what we want so i think we are going to have to head into our app.xaml and redefine this style so style inheritance in wpf is kind of a pain there's a lot of duplication involved it's not like css and web development which is i don't know kind of a mess as well but a little bit more flexible wpf pretty complex actually but let's change these to different colors so if we look at this there we go that looks good we got our hovers kind of messed up because dark gray is for some reason lighter than regular gray so let's do hex values instead so we get this super light gray as the default so let's set that same thing as our exit action we want to go back to this and then let's grab a darker gray for hover and that should be good let's see how this looks and one more thing i think we have to set the horizontal alignment as stretch i think by default it's center based on what we did for our default button and that's kind of making things look weird we should probably set our foreground to black in this case since white and gray just look way too similar there's like no contrast at all so make this black and i selected background i want foreground and yeah this looks good okay although one thing i don't like is that our default button style which i'm about to just scrap this because i don't think we should be basing off this but it sets this corner radius and i don't really want a corner radius on my drop down buttons so i'm going to grab this and override that here and get rid of the corner radius and in fact i'm just not gonna base off my default button i feel like we're just overriding what that already does for the most part so let's see how this looks uh we're gonna need our padding so maybe i do want that so let's paste those in there and i think i'm probably going to change the padding a little bit so i might want a little bit more because these are kind of small so about 20 and 10. i think that's good maybe i need to just set the width of my entire stack panel or we should probably do min with let's do 150 is that gonna look good maybe a little bit less how about a hundred i feel like that's what we just had maybe a little bit more how about 125. all right that looks good i like it i actually don't need this horizontal alignment stretch because that was just to override the base style so we can get rid of that and we're still good so i think our ui is completely good now but i can continue with these bindings so this edit button is going to bind to a command on our viewmodel and i think we just called this the edit command and then same with our delete button we just called it the delete command let's make sure and yep here we go edit and delete commands so all good there and i think that's everything with our youtube viewers listing view and view model so i think we're good to move over to the details view model so let me just close out a bunch of stuff and let's bring up the corresponding view so the youtube viewers details.xaml and we're gonna have a few bindings on here so we're gonna have to bind to some kind of username property on the details v model and then we're also gonna have to bind to whether or not the user is subscribed or not and that's gonna be a boolean so true or false so we can't really display that as text instead we have to dynamically set the text based on whatever that boolean value is so we could do that with like a text block style so define a style and then styles can have triggers and we can have a data trigger that binds to something like is subscribed which would be a boolean and then if that were true then we could set the text to yes but i really don't feel like cluttering my xaml i feel like xaml is already a quirky tool to work with so instead what i'm gonna do is do that calculation in my view model and just bind to is subscribed display and this property is just going to be a string that is either yes or no which means i don't have to do some kind of boolean to text conversion in my xaml it'll all be handled in the viewmodel using some good old c sharp and we'll do the same thing for is member so this will bind to an is member display so let's head into our view model and we're going to have some properties in here i think these are going to be read only as well so pretty much all the properties we've done have been read only we'll get into some prop change whenever we do like the form for adding a new viewer but for now it's going to be read only so this can be a string for a username so read only just a getter and then we're going to have what do we call these is subscribed display and then is member display and let's set these up in the constructor so the username we'll just set that to singleton sean and then is subscribe display is gonna be yes then is member display will make no i'll definitely have to think about becoming a member definitely some good perks there but let's see how that looks on our ui and doesn't look very good and why is that do we have to reboot maybe and yep that was the issue so now our bindings are all good and we got our yes no and our username so we're pretty good on this first view and viewmodel but now we need our functionality to actually work so if i click these viewers in this listing over here i want that to be reflected in the details so we pretty much need to communicate between our youtube viewers listing view model and our youtube viewers details view model and view model communication that's going to be done through a store so a store if you're familiar with like react and web development frameworks this is probably familiar but it's similar like a react context and it's pretty much our single source of truth for where our data is stored and we can pass that store to the view models that need that shared data and i have more videos where i go through stores but let's demo it so that it actually makes sense so i'm gonna have a new folder for stores and the first store i'm gonna have is the selected youtube viewer store and on here i'm just gonna have a property for the selected youtube viewer so i'm gonna use my prop change snippet which scaffolds out a getter and a setter and in the setter we call it one property changed but i'm actually not going to call it one property change i'm going to raise a different event because one property changed is kind of specific to view models and this is a store so it's above the view model layer so i'm going to take this out but the type here so what is the type it's going to be a youtube viewer so not a youtube viewer view model or something like that because stores in my opinion are above the view model layer just by a little bit kind of like on the same level almost but i don't want my stores to reference view models i want my view models to reference a store so this youtube viewer is going to be a model that we're going to create so we'll add that in a second but let's just scaffold out this property the youtube viewer and actually this is the selected youtube viewer because we're in the selected youtube viewer store and we'll get rid of one property changed we'll come back to that in a second but let's add a new folder in our project for models and we're gonna have a new class in here just a youtube viewer and the three things that our youtube viewers have is a username so we're gonna have a prop this is a string for username should it be read only i'm gonna go with yes so we'll come back to that later but i'm feeling we're not gonna have to set this and then we also have a boolean for is subscribed and another boolean for is member and these are all read only i don't really want them to change but we can get those through the constructor there we go that looks good and that's pretty much all our model needs so back in our store let's import that from our models layer and whenever my selected youtube viewer changes so in the setter i do want to raise an event so that view models that are using the store can be notified whenever this value changes and they can update the view so i'm going to have an event we can just make this an action i don't think we really have to pass in the youtube viewer as a parameter to this action so we'll just make it an action with no parameters and we'll call it selected youtube viewer change and the reason i don't really think i need to pass this as a parameter to the action is because my view models are going to have access to the store anyways so if they want to get the youtube viewer that changed they can just re-grab this property but if you really wanted to you could pass in the youtube viewer as a parameter here but now that i have this event i'm gonna take it whenever the value changes and if it has subscribers i will invoke it and that's just gonna raise this event so now we're ready to use the store and the thing about stores in most cases is that there's a single source of truth so you're usually just gonna have one store so in that case i'm just gonna define that store right at the beginning of my application and just pass that down through my application so this is the selected youtube viewer store we will call it that and then in my constructor for my app i'll just initialize this let's pass this down through our app so we can pass that to our youtube usb model let's add that parameter to our constructor there we go let's just import the namespace and then we're going to need the store in our youtube viewers details view model let's pass that down there we're also going to need it in our listing view model so that we can update the store whenever the selected item in the list changes so let's add a parameter to those constructors just do that with the control dot and let's head down here and again just import the namespace and same thing in our detailsviewmodel and we'll start here so i'm going to put this store into a field so let me assign that to a field so ctrl dot create and assign the field i like to have underscores before my field names and now we're going to get all of this data from the store instead of hard coding it so let's get rid of that and looks like it renamed this too so i don't want it underscored before that there we go and now a username i'm going to grab that from my store so i'll get the selected youtube viewer there and grab their username and i assume this viewer could be gnaw so i should optional chain this so i don't get some kind of not pointer exception and then similar thing for these display properties so take the store take the selected youtube viewer and actually i keep on referencing this store and then the viewer i can just put that in its own little helper property here it can just be private we don't really need to expose this but it's just going to be a youtube viewer and the selected youtube viewer and that's going to point to our selected youtube viewer store and the selected youtube viewer so now for these properties we just need to access that property so it kind of cleans things up a little bit we don't have to keep on doing the same exact thing but now we're going to dig into is subscribed and if selected youtube viewer was null then the fallback value that we specify with two question marks will just be false so let me actually surround that in parentheses just to kind of demonstrate how this is working so we're getting is subscribed falling back to false and if that is true then we'll display yes if it is false then we'll display no and then same thing for is member display so i'm just copy that and rename everything we're checking is member and displaying either yes or no this is kind of against the way i usually program like i like to make things as readable as possible but i don't know i think this looks okay we'll roll with it and maybe we should have a fallback value for username so we'll just say unknown and actually i don't even know how i feel about these fallback values i feel like our ui shouldn't even display this data if no user is selected like we should have another bully in here like has selected youtube viewer and that could just check if selected youtube viewer does not equal null and then we could use this property in the view to only display all of these details if we actually have a user selected otherwise we could display something like please select the user so maybe we'll do that i'll leave this property here but we'll come back to that i really want to focus on showing how all of the store works and actually being able to change the selected user but this should be good enough for now to actually see data so in fact if i ran this i should see like unknown and then i guess no and no and yup there we go so now i just need to hook up my store to this listing view model so that it changes the selected youtube viewer so for this listing view let's see how do we want to do this i think what i want to do in this case is just bind to selected item so that can be a binding too selected youtube viewer listing item view model it's kind of a long name not sure how i feel about that but now in our listing view model let's have a property and this will actually be a prop change so this property actually is going to change and our view is going to have to be in sync but the type here is going to be a youtube viewer's listing item view model since those are the items in our list view and let me just grab that name that i have in my view so the selected youtube viewer listing item view model and paste that as the property name and the field name same thing but lowercase s and an underscore and that should be good but the key here is that whenever this changes we want to update our selected youtube viewer store so let me put that into a field so we can access it in our setter so here we go and now in the setter i'll take the store and set the selected youtube viewer so we could instantiate the youtube viewer here but one thing i've done in the past is i'll just pass in youtube viewers to my listing item view models so in this case we have mary here is subscribe but not a member and then let's pass in youtube viewers to these other view models so this is sean and then alan and then i'm not subscribed to myself let me move this up here get rid of this don't need that and now what i can do is add a constructor on my listing itemview model that takes in a youtube viewer so let's do that and now i'll just get rid of this old constructor so knock that out and i'll put this youtube viewer into a field so i assign that to a field get my underscore in here and then that also means that username can come from my youtube viewer that i passed in let's just grab the username off that and now that i have the youtube viewer on my viewmodel that means back in my listing view model whenever i get a new selected youtube viewer listing item view model i can just set the selected youtube viewer on my store to the youtube viewer that i have on my view model so back in my view model what i actually want to do is expose this as a property that's public so i can actually get it in my listing view model let me make this uppercase just for convention for properties so now my listing v model i can just grab the youtube viewer so let me explain this from the top so i add a listing item view model to my list and the listing on the view model takes youtube viewers so now in my listing v model i have the youtube viewer defined here and that means whenever i change the selected item on my view not only do i get the selected youtube viewer view model but i also can grab the underlying youtube viewer model and pass that to my selected youtube viewers store and then when i set this on my store we're going to raise this selected youtube viewer change event and then my details view model so let me go to that is going to have to handle that event which i thought we did but we didn't so let's grab our store and we want to subscribe to selected youtube viewer change so plus equals and i'll just leave the generated name that works but basically our selected youtube viewer is gonna have a different value so that means we need to refresh all of these properties and tell our view hey the selected youtube viewer has changed re-grab all of this data so for that all we have to do is call one property changed and pass the name of all these properties so we use name of for strong typing and we want to update has selected youtube viewer and our other properties so username is subscribe display and is member display and another thing we have to look out for is whenever you subscribe to this global store the store lives the entire lifetime of the application and you might have a view model that doesn't live the entire lifetime of the application and is constantly created and destroyed so since we subscribe here this means that this view model is never going to be able to clean up because it's always going to have a reference by our store so in that case you'd have to dispose of this view model and to do that you could have a dispose method on your viewmodel base so let's actually add that so dispose and nothing to dispose in our viewmodel base so we'll just make it empty and we want to let other classes override the spoos so and our details v model wherever i put that what i'm going to do is override dispose and just unsubscribe so now we don't have memory leaks although it really wouldn't be an issue here because this v model lives the entire lifetime of the application but if we ever wanted this view model to be destroyed we'd have to call dispose before them so definitely something i just wanted to call out but not super important here since our application is small and we really only have one view but yeah once the selected youtube viewer changes in the store then our ui is going to get updated so let's go ahead and see this so starting off we have unknown i think we'll probably tackle this next and show something like please select the user but let's select sean and there we go we get sean i'm not subscribed and i'm not a member how about elon there we go he is subscribed and he is a member oh and i actually just found a naught pointer so if i unselect this so control clicked then we get a null pointer because we're trying to get the youtube viewer from this new value but our new value is null so we can't get a youtube viewer so we're gonna have to null check this we can just do an optional chain here and that should be good it should just set the selected youtube viewer in our store to no so let's try that and there we go it goes back to null and we get unknown blah blah blah so let's tackle all that unknown stuff and that's going to be in our youtube viewer details component so what i'm going to do is define another grid here and this grid is just going to have a text block in it and it's gonna say please select a youtube viewer to see their details and now i have two grids inside my borders so i need to surround both of these in a grid so let's surround with a tag just a grid by default and now i only want to show this grid if has selected youtube viewer is false so for that condition what we can do is use a style so grid.style and a style in here targeting a grid and the style is going to have a trigger specifically a data trigger that binds to has selected youtube viewer and if that is false then i'm going to set the visibility of this grid to visible which means by default in our style the visibility is going to be collapsed and then i'm going to grab this style and do the same exact thing for our content grid and by default it's going to be collapsed but if we do have a selected youtube viewer so that's true then we'll display this grid so let's see how this looks and that font size is way too big i forgot i actually want this to just be default 14 i think and there we go although that text should have been wrapping why was it not wrapping do we need text wrapping as wrap with overflow oh okay yeah so we do need that but nonetheless i just wanted to be the default font size and that looks good so we can select users we can unselect and perfect all right now that we're pretty much done with this main youtube viewer's view we're gonna have to tackle these modals so we're gonna have a modal to add a youtube viewer and by moodle i just mean like a pop-up kind of thing that takes up the entire screen and then we're also going to have a moodle view to edit one of the youtube viewers in this list so we're going to set up our modal i think first i want to scaffold out the views that are going to go into the moodle so that being said let's add a new view over here a user control again this is going to be the add youtube viewer view and then we're also going to have another user control this will be the edit youtube viewer view so i actually just realized i don't have figma prototypes for these i have a vision in my head of how i want them to look but maybe i should put together a prototype real quick should i do this on video yeah let's do it okay so i'm going to copy this for now just get a starting point i'm going to get rid of this because we're not going to have that i'm going to get rid of that i'm probably going to get rid of all this and this is going to be called the add page it's going to be like smaller because it's just going to be a modal and then i'll call this title add youtube viewer maybe a little bit wider let's do that probably don't need as much margin let's move this server like that that's probably fine and actually i am going to have these labels so youtube username is subscribed and is member so let's copy those and move those over here there we go so i probably want to keep the same spacing and let's do something like that and then we're going to have a text box for the username so let me do a little rectangle here with a white fill and a black stroke i guess we'll space it out a little bit maybe this page will be smaller than i thought i think that's good keep it centered with our label and then is subscribed and is member those are just going to be check boxes so i'll do little squares here probably wanted to be the same height as that all right so that's good i want it to be lined up no fill and another black stroke actually this is probably going to be smaller than the text box let's do that about 15 that looks good still gonna be lined up centered with our label if i can get that i think that's what we want and then left align with our text box and then one more checkbox for is member and then i'm gonna have my button over here as well so we're gonna have that right here how about submit and then we'll have another button for cancel and this button is just going to be gray and the text will have to be not white make it stand out we'll make it black and i think that looks good enough and then we're going to have another page for editing so the edit page and actually that's just going to be exactly the same thing so the only difference is that there's going to be some pre-filled values here based on what you're editing and this title is going to be edit instead so that's pretty much it we can go ahead and build this so let me head back into our views and for our ad page we really have two rows here so the first row is the header for the page and then the second row is the entire form and the reason the form is just one row is because i'm going to move that to its own component so i can reuse it between the add and edit views so first we're going to have some row definitions so first row it's going to be auto height and then our component can just fill the remaining space just be star the form component that is and then we're gonna have our header i want the header to be the same font size that i have on my main view so in fact i can move this to its own style sure why not so we'll have a style targeting a text block and this is not going to be a default style because i don't want everything to have massive font size so we'll just call this the page header and the only thing special about this is the font size is gonna be 32 i think so kind of overkill to move this to its own style but who knows maybe it's useful i might want to do something to all the headers in my application so i think this is a good style we're gonna get rid of this font size and use our style so point to our static resource of page header and same thing for the header that we haven't added here let me just go ahead and copy this text block from our main page and move it into my ad view and the only difference is that this is the row that it goes in and the text is add youtube viewer and while i'm here i might as well just do the same exact thing for my edit view so let's paste that in there looks good and that's going to be edit youtube viewer so now i just need that single form component and i'm going to call that i'm going to put in my components folder but i'm going to call it the youtube viewer details form and i just realized i named my details component that i made before youtube viewers like with an s at the end details kind of wish it was just youtubeviewerdetails.xml i don't know why i have an s on there but maybe it makes sense because it's on the youtube viewers page but yeah gonna focus on the form now so we have username is subscribed is member and then we have the button actions so the overall structure for this form is gonna be similar to the youtube viewers details component where we had to use is shared size scope and have those two columns so i think i'm going to start by copying that grid where we defined is shared size scope and just used that as a starting point so let me copy that and paste that in our details form but i think it's going to be pretty different actually so first off we're not going to have like this textblock header because the username is actually going to be a form field so not going to have that and instead we're going to have another one of these two column layouts and that's going to be for the username so it's going to go in grid row zero we don't really need any top margin because there's not going to be anything above this and the text here is going to be username i think in my prototype i said youtube username again but we decided we just want username and this will be a binding to username we'll have that on our viewmodel eventually which we haven't created and then after username we have is subscribed and this is going to be a check box and is checked is going to be a binding to an is subscribed property and then we're going to do the same thing for is member it's going to be a binding to an is member property this is a check box once again and the properties is checked and that should be everything for like the form input we don't need this grid style because this is always going to be visible we don't have that selected stuff so that should be good the only thing after these form inputs is that we're going to have another row for the submit and cancel buttons so let's have i feel like we can just make this a stack panel and make the orientation horizontal and in that case we can just make it a wrap panel i think might as well so that's horizontal by default and this will go in grid row 1 so we're going to have some grid rows on our root grid so let's have row definitions server definition the first one is going to be height auto i was thinking star i think i want auto if i wanted the buttons to be at the bottom of our modal pop-up i would want this to be star maybe i want that i don't think i do let's do auto so that our buttons are pressed up against our forum so that's easier to click it i think that's like fitzlaw or something where buttons should be easy to acquire but anyways after that grid with the form inputs we're going to have this wrap panel that we mentioned and that's just going to have one button that's going to say submit and another button that's going to say cancel and these are going to bind to some commands which we'll eventually add to our future view model so we're gonna have a submit command and a cancel command i'm also gonna have some margin to the right of our submit button just to get some space between these buttons uh maybe just 10 i think 20 might be a little bit too much even though we've been going overboard with 20 in fact i think we're gonna have 20 as the top margin for our rap panel and i think we have margins on all these already yup we do 20 again very nice and then our cancel button is going to need its own style so let me define that in the app.xaml let's just copy our button style the default one that is and make some adjustments so first off we're gonna give this a key we're gonna call this button secondary and then we're gonna inherit our default button so we're gonna base off static resource x type of button so that'll point to our default button style up here which means we don't need this padding we don't need cursor all this alignment stuff will inherit foreground for a secondary button it's going to be black and then background let me just copy this from figma so it's e1 e1 e1 that's like super light i hope that looks good we'll see our templates going to be same as our default button so you don't need that the only thing that's going to change is these animations so the colors are going to be different so the exit action is going to return to our normal gray and then the interaction let's make it gray i could grab some kind of custom hex but i feel like gray should be good enough so that should be good for that button secondary style let's define that on our form so the style for our cancel button is going to be button secondary nice alright so we got our form so back in my ad view i'm gonna have that form in here so this is the youtube viewer details form let's import that from components we're going to have some top margin i'm going to go with 30 so a little bit more than our usual spacing or maybe not i should probably do 20 because i think that's what i did between my header and my content on my main page did i do that yeah so i did 20 here as well let's keep it consistent and then let's also add our form to the edit view so let's import that i think we should go to control dot there we go all right i forgot to set the grid row so this is grid row one and same for our ad view grid row one so before we start getting in the moodles and stuff i do want to just see how these views look so let me just hard code those into my main window so i'll get rid of our normal view and let's set our add view in here and let's why don't we just throw our edit view in here as well and i'll just make this a stack panel so that they're not overlapping and let's see how this looks for now all right so here's our add view just this section up here i think that looks okay uh thinking about it i'm not sure if i'm sold on making these bold is that kind of weird and this is not doesn't look like it's lined up with that so that's one thing i want to fix let's go to our form is there some kind of vertical alignment we have to do let's try that vertical alignment center oh yeah that definitely fixed it okay let's do that for the other checkbox and yeah that looks much better maybe that's everything we were missing because now this view feels better oh i forgot to make this username a text box instead of a text block so let's fix that text box uh it doesn't look good i think the width is gonna have to be star so that it actually fills do i want to set a max width let's do it okay let's do max with how about 250 i think that's good that looks good i feel like we could use some more spacing between like these buttons and this header and our form even though i said i didn't want to do that i kind of just feel like doing it anyways so let's do that in our ad view let's do 30 for add and edit so subtle difference i think it looks good and then i do want the buttons to have a little bit more spacing too let's do 30 on that stack panel and yeah that looks i think that looks better and then i actually can't scroll on this page and most of our pages don't need scrolling but it is a good idea to always just define a scroll bar at the root of your application so we'll do a scroll viewer over here and vertical scroll bar is going to be auto so it's only going to turn on when it needs to and same with the horizontal i think that's the default horizontal but just want to make sure now let's throw that in there and we should be able to scroll now there we go very nice and this gray hover is a little bit too dark so i think i am going to grab a hex value for our secondary button style so i'll just snag one from our color picker and paste that in there and that looks the same i don't know maybe we have to restart yeah there uh i don't know maybe we just need something lighter how about this so paste that in there and let's see how that looks oh now it's like super light let's try this all right that looks good just a little bit lighter i feel like there's a little bit too much margin between our buttons too so we did 10. i guess we'll try five that looks good so we should be good on the ui for our moodles the last thing i want is the ability to possibly disable this button so we could set is enabled as a binding to can submit so we'll have that on our view model eventually and if that's false then this button will be disabled i don't think we have a disabled state for that button so maybe we should add that so it does make it not really like it doesn't change the cursor but and you can't click it but i feel like we should have some kind of opacity so let's add that so app.xaml so this will be a trigger let's get this trigger closed and we're gonna have another trigger based on is enabled and if it is enabled as false then we're gonna set the button opacity to i think 0.7 is usually what i do i don't know we'll just see how it looks and yeah that looks good so clearly disabled and since we defined that on our base style then it'll also work for our secondary button as well and one other thing i feel like there's not enough like top bottom padding on our buttons let's do 10 for top bottom all right that's too much how about eight it's a weird number i think eight looks good how about 20 left and right how does that look oh that looks even better so yeah we'll do 20 left and right and then eat top and bottom shout out lamar jackson and that should be everything for our views i think let's get rid of this is enabled or turn that back to our binding to can submit which we'll implement on our view model so that's next let's create a couple of view models we'll have the add youtube viewer view model and then we'll also have the edit youtube viewer view model and then i think i also want to view model for the form so we can reuse that between our add and edit view models so this will be the youtube viewer details form view model and i guess on that note we'll start here so it's going to extend view model base and now we're going to go crazy with some prop changes so i have this prop change snip it i'll leave a link in the description but all this does is scaffold out a property and add this one property change so we're going to have a few of these the first one is going to be for the user's username and just like that scaffold out of property this snippet is so awesome and then we're also going to have a boolean for is subscribed and then one more a boolean again four is member so set that property so what other bindings do we need we got username subscribed member we need our commands so we're gonna need a submit and cancel command let's add those those can be read only import i command these submit commands and the cancel command and then we also have one more property for can submit and i think this is going to be a calculated property so a boolean can submit and the way we calculate this is i think all we should check is if the username actually has a value so it's not null or empty so the string is null or empty username and you can submit if it is not another empty but one thing to point out here is that can submit is dependent on our username property so that means in our username setter whenever this changes we're also going to have to raise own property changed for can submit so that our ui will reevaluate this property getter so we got the 4mv model so let's head into the add view model and start messing with this this is going to inherit from viewmodelbase and pretty much all we're going to have here is a property for our 4mv model so let's set that just give it a basic name and we'll instantiate that and set it in the constructor and we'll go ahead and do the same exact thing for the edit view model as well so now we've reached a point where things are a little bit complex we're gonna have to deal with some navigation so specifically we're gonna have to have a moodle that can handle this add and edit view model and we're gonna have to navigate into that model so overall we're gonna have to manage the state of our modal are we showing a modal what view model is currently in the modal and to manage that state we're going to have a store so i discuss stores and navigation more in my navigation series which is really great but we're going to get into it here as well so we're going to have a modal navigation store and this will just have a property i'll scaffold this out as a prop change but we're not going to be calling one property changed and it's going to have a viewmodel base property so let's import that let's fix this and this name is going to be the current viewmodel same with the property name currentviewmodel and i did mention earlier actually that stores shouldn't reference view models but for navigation it's like this store is on the same layer as our view models it's like a view model store almost in most cases you're probably not going to have one of these but navigation is one of those tricky concepts in wpf where you definitely need to manage this state but anyways whenever our current viewmodel changes we're going to raise an event like we did at our other store so this is going to be just an action for current view model changed and then we'll invoke that event in our setter so if it has subscribers raise the event and we should be good to use this store now so we're going to define the store in our app.xaml so same thing like we did for our other store except this can be the modal navigation store and let's instantiate that in our constructor and now we're ultimately going to bind to this current model on our modal navigation store in our main window and if we have a current remodel inside of that store then we're going to display some kind of modal on top of our pages content but i want to talk about our data context real quick for this main window so right now the data context is our youtube viewers view model so that being said we're going to have to have our modal navigation store as a property on this view model which doesn't really feel right because the modal stuff doesn't really apply to any of our youtube viewers stuff so i think what i'm gonna do and this is usually how i approach most applications but i have a main view model so let's add that and the main purpose of the main view model is just to be the shell of the application so it's just going to get the current view model from your navigation store or in this case our modal navigation store and display that in the main window and it kind of makes sense anyways to have like this one-to-one relationship between our views and view models in this case we're having a view model for our main window where before we didn't really have anything so we're gonna have this extend v model base we're gonna have our modal navigation store in here and then i mentioned the main view model controls navigation for your application we don't have navigation for like our main page we only have it for this modal that we're going to create so in that case i'm just going to have this property for the youtube viewers view model which is the only page that we have in our application so i don't need anything complex with like a navigation store or something that i've done in the past where i've had multiple pages we can just have the single v model as a property so i'm going to take both of these through the constructor let me just generate a new constructor so get rid of this and control dot on all of my fields and properties generate a constructor so what i'm going to do is head up into my app.xamay.cs where my application starts and now my data context for the main window is going to be a new main viewmodel and this takes my moodle navigation store and also my youtube viewers view model which i can just pass in here and let me just move this to a variable just to keep things a little bit cleaner there we go that's good i suppose i could also put this into a field like my other stuff if i wanted to but we'll leave this for now i think we're all good there so now my main window has a main view model as its data context so first off let me revert all of this other stuff i had and i guess i'll leave this grid but get rid of these modal views that i was kind of playing with and just bring back my old view and the data context for this is going to have to be a binding to my youtube viewers v model on my main view model which is the data context for my main window so there we go let's run this and make sure we don't have any like weird binding stuff but that looks good everything is working as expected still but now that we have our modal navigation store up we're first gonna have to expose the current modal view model as a property here so we can bind to it in our main window so the current modal view model and we're getting that from our modal navigation store the current view model i also want a property for if the modal is open so this will be a boolean is modal open and this will just be if the modal navigation store however we have an is open property on here i think that's what i usually like to do just to encapsulate all the open logic so we'll add a property on there and is open will be a calculated property based on the current view metal so if the current view model does not equal null then we are open so all good there but one other thing i have to do is subscribe to current view model changed and whenever the current v model changes on our modal we're gonna have to raise one property changed for our current modal view model as well as if our modal is open or not so make sure our view regrabs these properties that we grab out of our moodle i guess we could also dispose of this event subscription even though our main view model is going to live the entire lifetime of the application i guess for consistency we'll do it but it shouldn't have caused a memory leak anyways but now we expose all these modal properties on our main view model now we just need to use those in our main window and actually display the moodle so i did a ui workshop similar to what i'd done earlier with the drop down menu but i did another one for a modal so let's manage our nougat packages and maybe i called this modal.wpf oh simplemoodle.wpf this one actually has a lot of downloads wow i did not know that but let's install that and we'll put our modal inside of here actually i don't want margin on my moodle so let's move our margin onto this view and let's put our moto up here so import that and the content for the modal is going to be a content control with the content binding to our current modal view model property and let me just rename this to how about custom set that namespace there we go and our middle also has an is open property so let's bind that to if our moodle is open we don't need that property so is moodle open there we go and let's just hard code our moodle to be open so in my main view model i'll take the modal navigation store and set the current view model so how about a new add youtube viewer view model and we should see that form inside of a modal we should be how about it let's see or we're going to see something weird and yeah we do so a couple things here first off our moodle doesn't have the content that we want so what we have to do is map our add youtube viewer view model to the add youtube viewer view and the way to do that is with data templates so if we take our window resources you could define these data templates in your app.xaml2 but i'll just keep them here for now because this is where we actually do the mapping we're going to have a data template and the data type is going to be our add youtube viewer view model and whenever we have a content control where the content evaluates to be this add youtube viewer view model i want to actually display an add youtube viewer view and then i'm gonna do the same exact thing but this is gonna be for the edit youtube viewer view and for the edit view model as well so it doesn't really know what these view models are so we're gonna have to import those so we'll have another namespace we'll call this vms and then i'll point to our viewmodels namespace so we'll have to prefix this type and make sure it knows that it's coming from our viewmodel's namespace and there we go we get some intellisense there's all of our view models and same thing for the edit viewmodel too so now we're mapping our content in our content control to the correct view so we should see that displayed and there's gonna be one more issue as we can see and the last thing we actually have to do is make sure that our modal displays on top of our content right now it's behind it and it looks freaking terrible and i think we can just define a z index of one and that should put it above there we go now it's above and one more thing we should probably have some margin on our moodle content so we can just can we define it here is that going to look good we'll do 25 i wish it was a little bit wider though maybe we can have like we could set a width i don't really think i want to let's just do a bunch of margin to the left and right not a bunch but a little bit more that looks good and then okay even if our app gets too small we can still use the scroll bar so i think this is good and then just to make sure our edit looks good too we will change that here to the edit view model and i think we forgot to do something here and we did not extend viewmodelbase let's do that that should fix our errors and there we go now we get the edit view in here so now we just need to implement the functionality on these views and actually open these modals when we're supposed to so how about we start by opening the modals and this is finally where we're going to get into commands so let me do some cleanup here close out some pages get rid of this hard-coded view model in our moodle so ultimately what i want to do is make this add youtube viewers command open my add youtube viewer view and this command binds to this add button right here so let's create a new folder for commands and i do all kinds of navigation abstractions in my navigation series but i'm not sure if we're going to get to those here i feel like if we don't need them then we're not going to implement them but if you're interested in that check out my navigation series but we're going to start off by creating a new command in here this will be the open add youtube viewer command so this command is just going to open that moodle and we're going to have to implement i command so one thing i want to point out is that i'm using class commands there is a lot of discussion i know a lot of people like to use relay commands or callback commands delegate commands whatever you want to call them i have a video where i go through the differences between both and why i prefer class commands but i still think it's kind of preference there are some nice things about class commands which is why i use them so let's implement this interface and we get all kinds of stuff here i should probably abstract this into a command base why don't we start with that it's not a bad idea so we're going to have command base and this is going to implement i command so import that and implement the interface again which we just did but this is going to be the base command so execute is just going to be abstract and then can execute i like to make this virtual and just provide a default so by default can execute will be true and we'll have to make this class abstract can't forget that and then i like to have a helper method to automatically raise can execute change so let's add that this can just be protected might as well make it virtual in case we want to ever add it usually don't but this is one can execute changed and we'll take cane execute changed and invoke it with this as the sender and just empty event args so you might be wondering like what is can execute changed and basically what we do with commands is we raise this event whenever can execute would return a different value and if can execute returns false then your button that your command binds to will automatically be disabled but now that we have this base command let's head up to our original command and let's get rid of all this and extend command base and implement the abstract class so now much easier now all we have to do is implement execute so this is opening our modal so we're going to need our modal navigation store in here so let's import that and get that through the constructor and this is putting the add youtube viewer view model in the modal so let's instantiate one of those and then just take our modal navigation store set the current view model as our ad youtube viewer view model so now we'll be displaying that in the moodle and that's pretty much all we have to do so back in our youtube viewers view model let's set the add youtube viewers command to be a new open add youtube viewer commands that's going to need our modal navigation store so we're just going to have to get that through the constructor so there we go added that parameter and now let's head up to our app.example.cs so let's pass in our modal navigation store here and now we should be good to open our moodle so let's see let's add and there we go there's our modal and we get a bunch of binding errors i actually didn't expect that and i think that's because we're not binding to this form so let me go to the add youtube viewers v model so we got our form on here but i don't think our view so this view needs to set the data context for our form as a binding to our form view model so let's paste that into the view and we'll have to do the same thing for the edit view as well so let's do that there we go now we got our data context and let's see we shouldn't see those binding errors anymore and there we go all good and look at that looks like our buttons disabled so our can submit binding is working so let's actually type something in and okay so that's an annoying thing with text boxes is by default they only update bindings whenever you unfocus them so to fix that what we can do is come into our form and for this username binding we can set the update source trigger to property changed so immediately when this value changes we'll propagate the changes to our binding so let's type and there we go our button updates so the next thing i want to do is be able to close this model when it's open so when i click cancel i want it to close right now it does nothing so that's gonna be another command we'll call this the close modal command we're gonna extend command base so implement that all we're gonna do is implement execute and we're gonna be dealing with our modal so we're gonna need our modal navigation store put that into a field and get that through the constructor and all we're going to do is take our navigation store and setting the current viewmodel to gnaw would close the modal but i think what i want to do is have a close method on my modal navigation store to just encapsulate that logic so let's generate that and all this is going to do is set the current viewmodel to null so there we go that'll close it and that should be pretty much everything we need for the closed moodle command now where do we need this that's going to be in our form so our details form view model we have this cancel command let's get that through the constructor in fact let's get both of these commands through the constructor so i'm going to select these and generate a constructor for those and it puts it in like the weirdest place let me move this back down there there we go so now to update my view models that use this form so it's the add view model that's gonna need i guess we don't really have a submit command yet so let's make that null and then we'll have a cancel command which i'll put into a variable just a local one let's import i command and our cancel command is going to be our close modal command so import that and we're going to end up doing the same exact thing oh this needs our modal navigation source we're going to need that passed to this view model so let's get that through the constructor and we're going to end up doing the same exact thing in our edit view model so let me just copy this and head into this edit view model and change this import everything we need and again get the modal navigation stored through the constructor and then lastly since we made some constructor changes here our open v model is going to have to pass the modal navigation store which we conveniently have right here to our add youtube viewer view model so overall past the modal navigation store down to our add view model which creates this closed middle command using the navigation story and we pass that command down to our cancel button on our form view model so now we should be able to close the modal so let's open and then close there we go nice so we still have a bunch more to do here with like submit and stuff but let's knock out this navigation stuff so let's open the edit view model whenever we click edit on one of these list items so that's going to be all the way down in our youtube viewers listing item view model we get this edit command so this edit command let's create a command for that it's going to be the open edit youtube viewer command it's going to be super similar to our add one so let me just start by copying that but it is going to be quite different i think but this is a good start let's make sure we fix the naming and import all of our stuff and we're going to be using our edit youtube viewer view model so rename that and whenever we edit i want my form to be pre-filled with the youtube viewer details so i'm going to take that through the constructor this is just going to be the youtube viewer model i guess you could pass in the view model if you wanted to i feel like all we need is the model here so pass that in and what we're going to do is populate all of those properties on the form so we're going to set the username to the user viewer username that we're editing and then the other properties we're gonna set so is subscribed and is member we'll just snag those off of the model and there we go now our form is gonna be pre-filled so we're gonna have to pass this youtube viewer in when we instantiate this view model so we're gonna have to get that through the constructor so we're gonna need the youtube viewer that we wanna edit so let's put that into a field and then we'll be able to pass that to the view model that we create so all good here i think now we just need to set up this command in our listing item and i think we'll be able to just define the command as a parameter to this constructor usually i do this thing where i'll create the command in this constructor with like a factory or something but i don't think we have to do that here i think we can just take in the command through the constructor so we will assign edit command to whatever command we pass in and let's see this bubbles up to our viewers listing view model so this is all going to kind of get long let me have like a little helper method here i have a feeling that's just going to be temporary let's call it add youtube viewer or i don't know it might be permanent we'll see and we'll just take in the youtube viewer that we want to add and we'll grab this add method on our observable collection and we will pass that youtube viewer to the listing item view model and then we're gonna have to create this edit command so we'll put that into a variable the edit command and this will be a new open edit youtube viewer command the thing that we just created and we can pass in our youtube viewer and then we have to pass in our moodle navigation story so we're gonna have to get that passed all the way down here so let's get that as a parameter to this and then we'll have to get that as a parameter to this v model but let's use this add youtube viewer method real quick so actually we can just get rid of all this add youtube viewer and then we're gonna have to pass in our modal navigation story which we're gonna get through this constructor so add a parameter for that and let's keep on using this method so it kind of cleans this up a little bit keep on passing down our modal navigation store this is where that thing that i had mentioned create command like the factory method would have been useful so that we didn't have to know about this modal navigation story inside here i'll try and link an example of where i use create commands but i think this is okay for now it's not like we're getting a bunch of dependencies passed through this constructor so we're all good and let's pass down the modal navigation store now to our listing view model we finally have it here in our youtube viewers view model so let's kind of explain this our listing view model gets a model navigation store which we use to instantiate this open edit youtube viewer command which takes the youtube viewer that we want to edit and our modal navigation store which we passed down and then we just passed down this command to our listing item viewmodel so it's bound to this edit command so whenever we click edit in that dropdown what's going to happen oh i'm not digging that pop-up why would it do that well the editing is working as you can see but dang that pop-up is not looking good i do not know what we're gonna do about that but that pop-up needs to close whenever we click the button so let's see can we make that work all right so i took some time to think about it and what we're going to do is whenever we click these buttons either one i just want to manually close the drop down menu and that should be able to fix this issue that is quite nasty so to do this what we kind of have to do is give our drop down menu a name so we'll just call it drop down but the issue here is if we want to control this drop down menu in our code behind we can't really do that so if we try and access our drop down it's not going to be available because obviously we have multiple drop down menus because this is in our list view so since it's inside of this data template we can't access it so ultimately what i was thinking we could do is move all of this to its own component that will have its own code behind and we can do whatever we want there so let's add a new user control in our components so this is going to be the youtube viewers listing item so this is kind of cool having its own user control because we have our own view model for the listing item as well so this kind of keeps the one-to-one relationship there and now back in our listing we just copy everything inside of our data template so the border all the way up we can cut that out and instead we're going to use our youtube viewers listing item and that's all we have to do here now in our listing item let's just paste in that border that has all the ui for our listing items and we're gonna have to import our drop down menu namespace there we go so now we will be able to access the drop down menu and code behind so for this button style what i'm going to do is use an event setter and the event i want to subscribe to is whenever the button is clicked i think i don't know if we want to do click or mouse down i think click is probably what we want so we want a handler for that let's just generate one of those and that's called button click it's now in our code behind and whenever a button is clicked we want to take our drop down menu i guess we just called it drop down and we want to set is open to false so let's put a breakpoint here and try this out so open the dropdown menu let's edit and we hit our breakpoint let's continue drop down was closed and our modal was opened so that is definitely working let's get rid of the break point and see it real quick so edit there we go drop down was closed and our modal is open so that takes care of that issue now we can move along with the rest of our moto functionality so really all that's left is the submit button so whenever we add a new youtube viewer we want this after we hit submit we want this to appear in our list of youtube viewers so this is going to be its own command and we're going to have to i believe that is in the add youtube viewer view model so we're going to want to pass a submit command down to our forum view model so let's kind of set that up a little bit let's generate a variable for the submit command there we go and it's just null for now let's go ahead and create that command we'll call this the submit add youtube viewer command or we could just call it create youtube viewer command or maybe just add youtube viewer command that sounds good i think so let's set that up and whenever we add a youtube viewer it's ultimately gonna save that youtube viewer to a database which we haven't really set up but really whenever we execute this command it's going to have to be async so we're going to need some kind of async command so i'm going to add that that's going to be an async command base class and that's just going to allow us to have our execute method in our command be async so that we can wait stuff when we access the database so that ultimately we don't block the ui so let's set this up i think we can just inherit from command base so let's do that and this class is going to be abstract as well and classes that inherit from async command base are going to have to implement a method that is async so it's going to return a task and it's just going to be execute async i guess we'll pass down the parameter even though i usually don't use it and now whenever our command is executed so whenever our button that this command is binding to is clicked this is going to run and we want to call execute async so let's do that and we're going to have to await this so wait execute async we'll pass down the parameter and the issue here is that we're using async await and this method is not async so let's make it async and this is the classic issue with async voids but it's usually okay for button handlers which this pretty much is you can make an async void the only thing to look out for is that you definitely want to handle errors inside of this method because otherwise you're not going to handle them anywhere else and i think i have a video where i go deeper into async commands so i'll definitely link that but we're just going to go with a basic implementation here and in fact we're just going to eat the exceptions because otherwise if this exception funneled out of this method i'm pretty sure it would crash our process so we don't want our application to crash so eat the exception here and ultimately any class that extends our async command base should really implement its own error handling so we're definitely going to enforce that in our own commands that extend this but this should be good enough for async command base so back in our add youtube viewer command let's extend async command base and implement that and now we get this execute async that's going to run whenever we click our button so eventually we're going to have some logic here to actually add our youtube viewer to the database but for now whenever we submit on these screens we're just going to close our moodle so kind of going to do the same thing as our close modal command where we take our modal navigation store and we close it so i'm going to get that through the constructor of my add youtube viewer command so let's just copy that in here import everything we need and make sure we rename our constructor and then we'll just take our modal navigation store and close our moodle and then we're actually going to scaffold out our edit youtube viewer command in the same way so that's whenever we click submit on the edit model so i'm just going to copy this command exactly and rename it to be the edit youtube viewer command and let's rename that inside of the class there we go same functionality and we'll just change our comment a little bit so it is going to be quite different once we get around to implementing it but right now it's exactly the same as our other commands so let's go ahead and use these commands now so that's going to be in our add youtube viewer view model so we want our submit command to be the add youtube viewer command we can pass in our moodle navigation store and then let's head over to our edit view model and same thing submit command this time it's going to be an edit youtube viewer command and we'll pass that down to the form view model so now whenever we submit we should see that our modal closes so let's go to edit and submit there we go it closes we just have to actually make that change in the database and then whenever we add we can go ahead and submit and it closes so before we connect to a database let's actually make it so that whenever we add a youtube viewer it actually pops up in this list so this is actually another case of viewmodel communication so we want this add youtube viewer view model to communicate down to this youtube listing view model and how do we do that well like we did with the selected youtube viewer store we're gonna have another store and this one is going to be much more basic it's going to be the youtube viewers store so this is going to be the single source of truth for our youtube viewers and it's going to manage all of that state so let's make this public and right now let's just have an event we'll make it an action but the parameter for this action is going to be the youtube viewer because this event is going to be the youtube viewer added event and we'll make it public and this youtube viewer parameter is of course going to be the youtube viewer that was added so we're going to expose a method on here we're going to make it async because eventually like we said we're going to connect to a database and we can just call this method add i feel like youtube viewer is kind of implied we'll get a youtube viewer as a parameter and for now all we're going to do is take our youtube viewer add event and invoke it with our added youtube viewer so now we're going to need this youtuber store passed down to our add youtube viewer command and eventually our edit youtube viewer command so let's instantiate this this is going to live at the top of our application just like our other stores do so this is the youtube viewers store so add a field for that instantiate that in the constructor and we're going to pass this down we're going to start off by passing it down to our youtube viewers view model so the youtube viewers store we'll add a parameter for that so there we go and let's head down to the store let me get rid of this underscore in front and we're gonna have to pass the store down to our listing view model so that our list of youtube viewers knows whenever a youtube viewer was added so let's add a parameter there and then while we're here we're also gonna have to pass it to our open ad youtube viewer command because this command instantiates our add youtube viewer viewmodel which is going to use that store so let's add that and let's head down to our listing view model now and let's put this into a field so assign that let's see where did that put it did it put it in a weird spot no this is good okay so now that we have our youtube viewers store in our listing view model we are going to take that and subscribe to this youtube viewer added event so let's generate a handler for that and since we're subscribing to a store we're going to want to unsubscribe so let's override dispose and let's unsubscribe from that event so we don't get memory leaks and now whenever a youtube viewer is added in our application we can actually call this add youtube viewer method that we created a while back so let's do that and pass in the youtube viewer that was added and we also have to pass in our modal navigation store which we don't have in a field so let's put that into a field so we can actually use it later there we go in a field and actually now that's in a field we don't really need to pass it to this method we can just reference the field from inside of here and we i guess we don't really need to hard code youtube viewers anymore let's get rid of this because we can actually add them now so basically what's going to happen is our youtube viewers store is going to raise this youtube viewer added event whenever that happens and we're handling that event by adding a youtube viewer to our youtube viewers listing item view models so that's going to display in the ui and we should be good here now all we have to do is actually get this event to raise and that's going to be done starting in our open ad youtube viewer command so this is where we instantiate the add youtube viewer view model which is what is displayed in our moodle so let's put this youtube viewer store into a field and now let's pass this to our add youtube viewer view model so add that parameter to that constructor and let's go to that view model and now we have our youtube viewer store here and ultimately we want to use this in our add youtube viewer command so this is what actually submits the new youtube viewer so we definitely want the store so that we can add the youtube viewer to the store so let's update that constructor lots of constructor updating because we're just passing stuff around and now finally in our add youtube viewer commands we have our store so let's take it and let's add a new youtube viewer so we're gonna have to actually create that youtube viewer so let's put that into a variable locally we can get rid of this comment because we're actually doing it now and let's await this and let's instantiate our new youtube viewer so we need a username is subscribed and is a member so we can get that from our ad youtube viewer view model actually but in our command we currently don't have access to the view model so when we instantiate this command let's just pass in this viewmodel instance so add a parameter for that and now we have our viewmodel in here let's put that into a field and now that we have our view model in here our view model has all of the values that we put into our ui so if we take our viewmodel and we take the form view model and we look at the properties on here we can snag the username that the user typed in and the checkbox values for is member and is subscribed so let's just put our form view model into a variable real quick so we can access it a bunch of times and let's use that down here so the username is from our form viewmodel the username we can use our form v model to get is subscribed and lastly we have to get is member so we instantiate our youtube viewer by grabbing all the values from our form that our ui binds to and has all the values that the user typed in and then we add that youtube viewer to our youtube viewer store and ultimately that's going to raise this youtube viewer added event and our listing is going to update and we still need some error handling around here so whenever we call add on our youtube viewer store eventually that's going to hit the database so we definitely want to handle any kind of errors from that call so we'll be sure to do that eventually but right now let's actually test this out so right now our list of youtube viewers is empty we should probably have some kind of fallback text here when there is no youtube viewers but let's add one so how about alan who is subscribed and is a member let's submit and there we go we get it in our listing and we can edit this user there we go we get the details pre-filled but if we submit this it doesn't actually change anything so let's do that next so we're kind of going to go through the same process but it's going to be for editing a user so we're going to have another method on here we'll make it async because eventually we're going to hook to a database and it's going to take the youtube viewer that we've edited i feel like edit is kind of weird here i feel like it should be update and maybe this should have been create to kind of align with crud terminology but i feel like update's more appropriate here maybe ads okay we can change it if we need to but basically whenever youtube viewer is edited we're gonna raise a youtube viewer edited event and let's go ahead and just raise that so youtube viewer edited invoke it with the updated youtube viewer in fact this should be youtube viewer updated to align with our terminology here and now we're gonna have to handle this event inside of our listing view model so let's head down here and we've already handled youtube viewer added but now let's handle youtube viewer updated so generate a method for that and make sure we unsubscribe from this event whenever we dispose of this v model and now in this updated handler we're gonna want to update whatever youtube viewer was updated so we're gonna have to find the corresponding youtube viewer and really the only way to do that is if we have an id on our youtube viewers so let's add an id to this model and it makes sense to have an id anyways because these are eventually going to get saved in a database so we'll just take that id through the constructor and assign that property and let's see we're pregnant enough to update where we create a utv or so we can just take good new guide randomly generate one and now we have ids that we can match against so in order to update our listing we're first gonna have to find the listing item that has the same id as the youtube viewer that was updated so we'll take our list of items and we're gonna find the first one so we use first or default so we get null if we can't find it but we're going to frame the first one where the youtube viewer id matches the id of the updated youtube viewer so let's put that into a variable the youtube viewer view model and if this value isn't null that means that we actually found the youtube viewer so we'll check does not equal null then we'll take the youtube viewer view model and let's just have an update method on here and pass in the updated youtube viewer so let's generate this method and let's implement this so we can make this public and we're gonna have to set this youtube viewer property so let's make this a private set and we'll just take our youtube viewer property and set it to the new youtube viewer and then we're gonna have to call it one property changed for this username property because the username property depends on our youtube viewer so definitely want to update this that our ui updates just in case the username was changed which it likely will so we should be good here i now realize there's probably some issues with this edit command because when we instantiate the edit command we pass in the youtube viewer model and this could now change so this could definitely cause some issues we'll come back to this in a second but let's just get the basic editing functionality working so we're handling the edit event whenever that's raised and our listing view model here we go we subscribe to youtube viewer updated now we just need to actually raise this event and actually we're going to start with this functionality inside of this view model so here's where we instantiate the open edit youtube viewer command which opens our edit page and the moodle and i did just mention that one issue where we're passing in the stale model to the command which means we could open the edit screen with outdated youtube viewer information so what we're going to do is actually instantiate this command inside of our listing itemviewmodel and we'll be able to do some magic there so if we want to instantiate this command inside of our viewmodel we're going to have to pass our modal navigation story down there and this command's also eventually going to need our youtube viewers store so let's pass that down as well and get rid of this command and let's move this to its own variable because it's kind of a long line what's called itemv model and let's update this constructor so add a parameter there and we might just have to wipe the constructor and generate a new one let's do that just generate that and let's head in this model and let's get rid of this old constructor so it should be good there and now let's instantiate this edit command so that edit command is going to equal a new open edit youtube viewer command so we're going to open that moodle whenever we click edit and instead of passing the youtube viewer which we mentioned could get stale let's just pass in this viewmodel instance and we're also going to need to pass in our youtube viewer store so we can call update on there and then we need our modal navigation store as well so we should probably just wipe this constructor too and generate a new one so let's control dot on this generate just with nothing and let's assign all of these to fields actually so let's assign those and i actually don't need this youtube viewer field anymore because that field could get stale so don't want that and now what we're going to do is whenever we execute this method we're going to grab whatever the current youtube viewer is on our listing item view model so this will ensure that we get the latest youtube viewer instance just in case update was ever called on this item view model and we can just pass that in to our edit screen which is now going to be visible in our moodle since we set it as the current v model and i believe this viewmodel is gonna need our youtube viewers store so let's add a parameter for that and the reason we want that there is because whenever we instantiate this edit command so this is the command that actually handles the submit on the edit page we want to pass in our ut viewer store to that so add a parameter there put that into a field and now we can tick the youtube viewer store and we will update and pass in the updated youtube viewer but i think this is really going to be similar to what we did in the add youtube viewer command so i'm just going to copy all of this and paste that in the edit command and import all the stuff that we need so the youtube viewer we're going to grab that from the form and then not calling ad we're going to be calling update so that's really the main difference there and then we are going to need our edit youtube viewer view model as a field here so that we can actually access our form so let's pass that in so in our edit youtube viewer view model where we create this command let's pass this in and add that parameter put that into a field and now we can access our form which has all the info that we put into the ui and we'll instantiate this new youtube viewer and call update and update is going to raise this event which our listing view will handle so let's run this and we should be able to update and add and all that jazz we're gonna have to add another user this will be singleton sean who is just subscribed and there we go we get that added we can see those details that we just put in reflected in here but now let's go ahead and edit this and now i'm a member hooray let's submit and it doesn't quite update right away but i believe if we reselect this oh it still doesn't update so why is that so we're gonna have to trace this down but let's start by putting some break points so here's some good debugging so we kind of start in the store let's see what update gets so we'll call edit and change this to is member so update receives the youtube viewer and there we go true true oh it's because we're generating a new guide whenever we call update and obviously we use that guide to compare which itemview model we want to update so if we go to our edit command we should not be generating a new guide we should be getting the id of the youtube viewer that we want to update so do we have access to that i don't think we actually get that anywhere i guess we could put that on the edit youtube viewer view model so we'll just expose a property it'll just be read only and it'll be the youtube viewer id so the id of the youtube viewer that we're editing and we can set that down here so youtube viewer id and we get our youtube viewer passed into this constructor so we're going to snag the id of that and now that we have this in our command we can use that instead so our edit viewmodel just grab the youtube viewer id of the youtube viewer that we're updating so let's try this again should work so we'll add one so singleton sean and i'm not a member so submit and there we go we see that in the details let's edit this and let's make myself a member there we go and this doesn't update i think we're gonna have to raise an event to get this to update but if i unselect this and then select it again there we go ui does update and we can edit this and we see the updated details so we can actually change the name too and that should update in our listing so let's try that there we go it updates we just need to update this details panel so to do that i think we can have that functionality in the selected youtube your store so what we can do is get the youtube viewers store in here so put that into a field and we can just pass that through the constructor and we'll subscribe to the youtube viewers store updated event so let's generate a handler for that so basically what we're going to do here is if the updated youtube viewer has the same id as our selected youtube viewer then we're going to update this property to be whatever the new updated youtube viewer is so we'll check if the updated youtube viewer has an id that matches the selected youtube viewer which could be null so we'll do a null check here but check the id on that so if that is the case then we're gonna set the selected youtube viewer to the updated youtube viewer value so let's put a breakpoint here and we are gonna have to update our constructor to pass in our youtube viewers store so it is okay for your stores to depend on other stores in fact it's kind of a good thing to make sure your application state is all up to date and you're relying on these single sources of truth it's probably not a good thing if your stores have like circular dependencies and stuff so definitely look out for that but this is definitely a good example of stores depending on other stores so let's run this and we should see this details page get updated so i'm not a member let's create that and select this and let's edit make myself a member submit and alright we hit our break point let's see the ids are the same so we're updating our selected youtube viewer let's go and there we go our details page updates so our application is super reactive right now i think pretty much all we have to do is hook up to a database we also have to handle some edge cases like if there's no youtube viewers in here we're also gonna have to handle delete and then we're gonna have to handle error messages and limiting states but before we get into any of that i feel like the next step is connecting to a database but if you made it this far we got a pretty solid application right now all right so connecting to a database i've took some time to think about how i want to do this i'm also a little bit less sick today so hopefully i sound better but i did decide that i want to split my solution into multiple projects so we're going to have another project for all of our database interaction stuff that uses any framework so let's add a new project in our solution again going to be a class library and we'll call this youtube viewers dot entity framework and create that same target framework as your wpf project and while we're here i'm going to add one more project so let's add that another class library and this is going to be youtube viewers dot domain so this is going to be my domain layer same target framework and everything create that so how it's going to work is my entity framework project is actually going to reference so let's add a project reference it's going to reference my domain project so let's do that and then my wpf project is also going to reference my domain project and now this domain project can have shared code related to my domain so we'll see what that is in a second but let's get rid of these placeholder classes don't need those and actually let's focus on the domain layer right now so my domain is of course youtube viewers and it's kind of all centralized around this youtube viewer model that i have so in fact this is something that i'm going to move into my domain project so let's just move that over and the reason i definitely want this here is because my entity framework project and my wpf project are definitely both going to use this youtube viewer model so let's delete it in the wpf project don't need it anymore and in our domain project let's update this namespace this is dot youtubeviewers.domain.org now and we'll probably have to go through our wpf project and update all the places that referenced the old namespace so let's just knock that out real quick in fact we could just do a control search all through our project so let's control shift f and we want to replace in files and we want the replace value to be youtubeviewers.domain.model so let's paste that in there and we're replacing dot youtubeviewers.wpf.models and let's just search around for that see we got in a few places can we just do a replace all i think we can let's try it risky business but seems to have gone well so let's save everything and build again and it was successful okay so we have successfully moved that over to our domain project so in our domain project we're centering around this youtube viewer but what else does our application do what is our domain well really this is just kind of a crud application create read update and delete but that's still considered domain logic so we need to represent that in our domain layer so let's add a couple folders here one's going to be for queries so this is going to be the r of crud reading and then another folder for commands so this is going to be the create update and delete of crud and i'm separating these out instead of doing a traditional repository kind of taking a cqrs approach command query responsibility segregation just because i feel like it's easier to manage it's easier to extend and you don't end up with these repository classes that are like thousands of lines long so keeping things simple and we'll see just how that works so let's start off with queries and what are we going to be querying for well when our application starts so let's actually start it hopefully it runs okay we're going to want to query for all of the youtube viewers and populate this list so that's going to be our first query let's add something here and this is actually going to be an interface so this will be the i get all youtube viewers query so let's create that and we'll make it public and it's just going to have a single method that's going to return a task because when we implement this it's going to be asynchronous and it's going to give us back an innumerable of youtube viewers so import that model and we just call the method execute so we're going to be executing our query and i think this will take no parameters and the reason we added this as an interface and didn't actually just implement a class here and hit our database is because our entity framework project is going to implement this interface and take care of all the complex entity framework database interaction stuff so by doing that we can encapsulate all the entity framework stuff in our entity framework project and throughout our application and throughout our wpf application we can just reference this interface that lives in our domain layer which means if we ever needed to we could easily swap out this entity framework for something like dapper or like a file for some reason and it makes our code more flexible and also more testable even though we have zero percent code coverage which hurts my soul but let's just focus on the fun stuff for now so this should actually sum up all the queries we need so taking the same approach let's implement some commands again going to be as interfaces because of that whole tangent i just went on and one command we need is to create a new youtube viewer which is done in our modal so this is going to be the i create youtube viewer command and while we have this as pretty much empty let's just duplicate this one time and another time and the other command we're gonna have is the i update youtube your command and the last one we have is the i delete youtube your command and these commands should cover the rest of our use cases so let's scalp it out the i create youtube viewer commands so this will be a task so we can make it a sync not going to return anything and just an execute method that takes in the youtube viewer model that we want to create so let me actually copy this method signature because i think it's going to be the same for the i update let me rename this the i update youtube viewer command and let's paste in the method signature so execute with a youtube viewer and this will be the youtube viewer that we want to update and it'll contain all the updated values so the last interface the i delete youtube your commands so again just a single method this will be execute and in this case i think we can just take in the id of the youtube viewer that we want to delete so we don't need the whole youtube viewer model just deleting by id should be good enough and with that i think we're done our domain layer of course every time i say that we're done something i feel like we always come back and change something later but i'm feeling good about this i feel like we've added all of the commands queries and models that we need so now we can focus on any framework and actually implement these commands and queries against the database so any framework let's set that up let's manage some nougat packages and let's start off by not searching for any frame recorder because it's right here so let's install this and then for a database i think we're just gonna roll with sql light which is a file database that is pretty easy to set up so let's search for that provider i think nad frame recorder dot sql lite so here we go microsoft.netframerecord.sqlite install that and i think we'll need a few other packages but we'll deal with that when we get to actually scaffolding out our database and doing migrations so first we're going to need a db context that's going to describe our database tables so we'll call this the youtube viewers db context and we're going to inherit from db context so import that and all we're going to have is a property for a db set and this will be the db set for our youtube viewers and we could just import our model so if we look at that we have the id username is subscribed and is member so all of our properties but these are read only and we made them read only because we don't really want them to mutate throughout our application but since these properties are read only it's not going to work well with any framework because any framework needs to set those properties with the data that it gets back from the database and additionally this youtube viewer object is a domain object and i don't want to couple this domain object to my database table layout so what i'm going to do instead is add a dto so let's add a new folder for dtos and inside here we'll just have a single class for our youtube viewer dto and this is pretty much just going to be used to describe the database columns for our youtube viewers table so those columns are pretty much just going to be these properties so let's copy these and paste them in there the only difference is that we're going to be able to set these properties which is going to be needed by any framework and since this dto is completely decoupled from our youtube viewer in our domain layer we could add any properties we want without affecting our domain model so in my opinion it's nice to have these decoupled and actually additionally you can add data annotations for any framework to your dtos without cluttering your domain object so for example we could explicitly say this id property is the primary key but we don't really have to do that because any framework identifies that for us because the name of the property is id but anyways we have our dto now and that is going to be the type for our db set so let's import that from our dtos and we'll call this dbset youtube viewers so when we generate our database using this db context we'll get a table of youtube viewers which is exactly what we want so now that we have our db context we're gonna have to eventually use it but to use it we can't just use it we're gonna have to get it from a db context factory so let me add a class for that and i'll explain why we have to do this so this will be the youtube viewers db context factory and i'll just have a single method that'll return our db context and we'll just call it create take no parameters and just return a new db context and the reason we need this is because we're gonna have to create a new db context every time we execute one of our queries and commands because db contexts are not thread safe so we can't just reuse the same exact db context instance otherwise we run into concurrency issues and weird exceptions if multiple threads we're trying to execute some sql using the same dbcontext instance so gonna create a new one and that's why this factory is gonna be useful we're probably gonna have to pass in some other things into this factory most notably a connection string so let's add a field for that and get that through the constructor and we'll have to pass that to our db context through the constructor which we don't have one right now so let's generate one of those so we're going to generate a constructor that takes options and now we can build these options in our db context factory so let's get a db context options builder i think so import that from any frame record and instantiate that and actually i think we can just build it in line so we're going to want to use sqlite and pass in our connection string and then we can just use this options property to get back db context options so don't need this builder in a variable let's just rip out the options and just pass in those options to our new db context or now to think about it maybe it would make sense to just take these db context options through this constructor and throw that into a field just to make things more flexible so not just limiting us to passing in a connection string we can just pass in the entire options so i think that makes sense let's roll with that and now we're ready to implement our queries and commands so that's going to be in our ending the framework project let's add folders so we want queries and we want commands and let's create a bunch of classes so for queries just have one query the get all youtube viewers query and then for commands let's add three classes here the create youtube viewer command and the update youtube viewer command and the delete youtube viewer command and let's head back to our query and start with this so this is going to implement the i get all youtube viewers query from our domain project so let's implement that we're gonna have to grab our db context factory so let's get that through the constructor throw it into a field and generate that constructor and now this execute method gonna be pretty simple actually it's gonna be async and first we're gonna have to use our context factory to create our db context and we got to throw this in a using statement so that we dispose of the db context when we're done with it but just use our context factory and create the db context and we'll just use our context and get our youtube viewers db set and we can call to list async which will return all of our youtube viewer dtos from our database so let's put those into a variable real quick and obviously await this to list async so this gives us back an innumerable of youtube viewer dtos and we're gonna have to map these dtos to our youtube viewer domain object so we can return that mapped list of objects so we're going to take our dtos and select them to the domain object so let's grab that and just return a new youtube viewer we're going to grab the id off the dto the username is subscribed and is member and that's pretty much all of our query has to do so it takes our db context gets all the youtube viewer dtos from the database and maps them to youtube viewers so that's all for queries and for our commands they're going to follow kind of the same structure where we get our context factory passed in so i'm going to copy this and paste that into each of our commands so one by one same kind of thing and we'll start with the create youtube viewer command so let's implement the i create youtube your commands so import that implement the interface update our constructor name make this method async and actually another similarity we're going to have to create our db context so let's copy over all this using statement stuff and paste that in here the only difference is going to be what we execute with the db context so in this case we're going to map our youtube viewer to a youtube viewer dto so import that and instantiate that and all we have to do is set all of these properties using our domain object so we can grab the id and three other properties we got username is subscribed and is member so grab all those off of the domain object so now we got our dto and now we just got to save this dto to the database so to do that we just got to take our context and dig into our youtube viewers db set and add the dto and we don't need to await this this is all synchronous but we do have to await when we actually save these changes so this will actually perform the database query to create the youtube viewer so we're going to save changes async hit the database and save the dto so pretty straightforward there again so let me just copy all of this and same thing in the update youtube viewer command let's implement that interface from our domain layer so implement that let's paste in what we did in our create command so there is some duplication here most notably of this mapping from a youtube viewer to a youtube viewer dto so it's probably useful to extract this logic to a reusable method but i feel like it's pretty minimal here we're only doing it in two places so i'm going to leave it for now but maybe we'll come back to that anyways let's import what we need let's make this method a sync let me rename the constructor to match our class name and now instead of add we should be able to just call update and pass in our dto and that's pretty much it for this command and the very last one is the delete command let's update this constructor name implement our interface so that i delete you to your command and this is also going to use our db context so let me copy over a using statement let's make this method a sync and deletes are kind of weird in any framework you do only need the id but all we have to do is create a youtube viewer dto so you're still going to have this but all we have to do is set the id on this dto we don't need anything else so we can remove these properties and the id is going to be the id parameter we pass in and now instead of update we just call remove and this just tells any framework to delete the youtube viewer where the id matches the id that we actually want to delete all right so with that we're successfully implementing the commands and queries from our domain layer and our entity framework layer and actually hitting our database so i feel like we're at a point where we can use these commands and queries in our actual wpf app so i think we still have to reference our entity framework project so let's add a project reference in our wpf app to youtube viewers dot nd framework and now we're all set up and pretty much all of these commands and queries are going to be used in our youtube viewers store so whenever we call this add method we want to actually create the youtube viewer in our database whenever we call update we want to call update in our database we still need methods for delete and we need a method to load all the youtube viewers from our database but let's just start with these two just so that we can actually hit our database it's going to be fun so we are going to need those commands in here so let's put those in fields and i believe this is the i create youtube viewer command that we want so import that from our domain so again referencing the interface from our domain layer not the entity framework class so this allows us to be decoupled from any framework but add that field we also want the i update youtube viewer command and i guess we'll take the other commands and queries through the constructor while we're about to generate it so that i delete youtube your command and then the i get all youtube viewers query so we can grab all four of these fields and ctrl dot generate a constructor that takes all of them it's a really big constructor but can't really imagine we'd add much more to this constructor i mean overall this youtube viewers store is kind of like a facade and we're going to end up having four methods here one for add update eventually delete and load which is going to get all so i think it's fair to have four objects get passed to the constructor but anyways let's just grab our create youtube viewer command and execute this with the youtube viewer we want to create and we're going to have to await this since we're hitting our database and no error handling here this method could throw errors but any errors should be handled in the command which i don't think we've done yet well we're catching them we're just not handling them so we'll do that eventually but all good on this command now similar thing for update except we're going to call the update youtube viewer command and execute that with the youtube viewer that we want to update and same thing with error handling nothing here going to be handled in the command so we're going to have to update where we instantiate our store and i believe this is done in our app.xml.cs here we go so we're gonna have to instantiate our commands and queries in here so let me just copy all these fields because they're gonna be pretty much the same and paste those in here except here after we import these we're gonna actually instantiate these and when we instantiate these we're gonna use the classes that we created in our entity framework project so the get all youtube viewers query import that from our entity framework project and that's going to take our context factory we'll add that in a second but let's instantiate these other commands that we got here so the create youtube viewer command and then our other two commands so the update youtube viewer command and last but not least the delete youtube viewer command and we're gonna take all four of these commands and pass those into our store so that we can use them throughout our store so pass in the query and then the create command the update command and the delete command so all good no errors there we just need our context factory so let's put that into a field too this is the youtube viewers db context factory and we'll just call it that and let's instantiate that in this constructor so the context factor is going to equal a new context factor instance and now we have to pass in our db context options so we can do that here we're going to create a db context options builder so similar to what we saw earlier except now doing it here and we want to use sqlite we have to pass in a connection string i guess we'll grab that here so connection string we'll add that in a second but then just build these options so call options and we just plop the connection string here so connection string this has to be a sql like connection string so this is just data space source and then the name of your database file so we'll call this youtubeviewers.db and pass that in so all good for the context factory and then just pass in our context factory to our queries and commands all right so we're pretty close to being able to run this and actually having our database interaction working but we still have to generate our database and first we're gonna have to create enemy framework migrations that are going to describe our database structure so let's open our entity framework project in the terminal so open in terminal and we're going to use dotnet ef so any framework migrations add and then the name of our migration so this is just initial so let's do that and actually we should get an error so yeah we need microsoft.entityframerecord.design so let's add that package so manage nuke packages for our entity framework project and we want microsoft.endingfromrecord.design let's install that so all good there let's head back and try again so same command and still complaining i think we just have to rebuild let's try that so all good let's try this command again my terminal is glitching out but it is building and we actually fail again so unable to create our youtube viewers db context which makes sense because our db context takes in these db context options and entity framework doesn't really know what these options should be so to fix that we actually need something called a design time db context factory so let me just copy our db context factory so copy paste and we'll rename this to the youtube viewer's design time db context factory and this is really only going to be used for migrations so if you're checking this project in the source control you could probably exclude this but let's implement this so the youtube viewers design time db context factory and this has to implement i design time db context factory so import that and we have to specify our db context type so the youtube viewer is db context let's get rid of our constructor and this method is actually called create db context and let's see it also takes in an array of strings for parameters so args and i think these can be optional actually and now the reason you probably wouldn't want to check this in the source control is because we just hard code our connection string and all of our options right here so we're going to use an options builder and we're going to sequel light pass in our connection string we're gonna have to grab that from our app.xml.cs but just spit out the options and our connection string is right here and there we go and i will mention design time db context factory is not the only way to get migrations to run for your project if you use the net generic host you can actually get net ef migrations to run against a generic host in your app startup i have other videos where i set that up and having the generic host in general is super useful so maybe we'll get around to implementing that because that also allows us to set up dependency injection which would probably be nice but we gotta get our application working first anyways we're good on the design time db context factory let's run these migrations again so they are running my terminals just all screwed up and we got our migrations so we can review these looks good we got four columns in our youtube viewers table so we're good to create our database and you could scaffold out your database and actually create the database file from your terminal but i like to actually do it on app startup so if we come to owen start up here we can create a db context so let's do that right on startup using our db context factory that we set up up here and put into a field and we can create a db context and our context has a database property on it and on here we can just call migrate and what that's going to do is use the migrations that we just created to scaffold out our database so definitely nice to have this in case you have additional migrations in the future those are automatically going to be applied on startup and you don't have to remember whatever the command is to run the migrations you just do it automatically so we should be good on creating our database let's actually run this all right so we did actually migrate no exceptions there so we should have a database file let's check in our bin so we come over here and right click on our project let's open this in file explorer and head on down to the bin debug all the way down here and let's see here we go we got youtubeviewers.db it's a database file and it has 20 kilobytes so since it has some size to it that means our database table was probably successfully created so in our app if we add a youtube viewer let's try this let me put a break point on this command before we execute it and let's just throw something in here and submit that all right so we're going to hit our database here we go moment of truth so step over this and oh it was successful no exceptions there we go so if we bring up our database we should see this record in there so if you're following along you really don't have to do this because in the very very near future we're going to be loading all of our youtube viewers into the ui on startup but i do have my database in here let me see if i can find it so head back into the bin here we go youtube viewers and we got tables we got our youtube viewers table let's look at the top 200 rows and there's the youtube viewer that we just created so actually let's see if update works oh i guess we can't because we don't load it so we can't really change it but let's create a new one so singleton sean too create that and let's edit this and now i'm a member let's submit all right no exceptions that's always good but let me bring up the database table say refresh this and there we go was successful because it is subscribed and is number is true so our update did work by setting his member to true so now really all that's left with database interaction is that we have to load all of our youtube viewers into our listing on startup and then we also have to support deleting a youtube viewer but let's focus on loading youtube viewers first so starting from the top let's head into our youtube viewers store and we're just gonna have a method for load and all this gonna do is load all the youtube viewers so not gonna take any parameters gonna be async because we're gonna use our get all youtube viewers query so let's execute that and that'll give us back our youtube viewers so take the query and execute it and then we're going to put these youtube viewers into a property so let's expose a public property for an innumerable of youtube viewers and what's called that youtube viewers and it's going to be read only and actually point to a field that's going to be our list of youtube viewers so let's add that so we're gonna have to generate this field let's generate that it can be a read-only field and it's not gonna be an innumerable it's gonna be a list and the reason we want it as a list is so that we can actually add and remove items from this list so we can just instantiate that list in the constructor and obviously going to be empty by default but now when we load we can take our youtube viewers list and i guess we should clear it before we add these new youtube viewers just in case there's some stale data in here and then we'll take that list and we will add all of our loaded youtube viewers with ad range so that takes the collection of youtube viewers that we got back from our query and now that we're done this we want to raise an event that our view models are going to be able to hook into so that they can update the ui with the new youtube viewers so this event it can be an action you could have a parameter here for the i enumerable of youtube viewers but i feel like i don't need a parameter here because any view model that's subscribing to this event is gonna have access to this youtube viewers property anyways so you could have the parameter if you wanted to but i feel like we're good without it and we'll call this youtube viewers loaded and now that we have this event let's invoke it whenever our load is complete and we just invoke with no parameters so before we leave this youtube viewer store one thing i want to do is actually keep this youtube viewers list up to date as we add and update items so what we want to do is whenever we add a youtube viewer we're going to want to take our list of youtube viewers and actually add that new youtube viewer so pass that in and then for update ultimately what i want to do is replace the existing youtube viewer with the new youtube viewer that we get in this method as a parameter so i feel like we should maintain the index that the old youtube viewer has in our youtube viewers list so for that we should take our youtube viewers list and we should do a find index and we want to find the index of the youtube viewer where the id matches our updated youtube viewer so this will be the current index of the youtube viewer that we're updating and let's see this returns negative one if we can't find it so we're gonna have to account for that so let's see if the current index doesn't equal negative one so we actually found it then we should take our youtube viewers list and at that current index let's set the new updated youtube viewer and then i guess if it doesn't already exist in the list then we can just add it to the end so let's do an add and pass in the updated youtube viewer but it should technically always exist in the list if this update method is called from our listing view anyways but who knows how this will grow in the future but i think we're good here next thing i want to do is actually call this load method when our listing view loads so that's going to be done in a command actually so let's add a command over here we'll call this the load youtube viewers command and loading is async so we're going to extend async command base let's implement that and for loading i believe all we're going to need for now is our youtube viewers store since that is the load method on it so we'll just snag that through the constructor we'll make this method a sync and all we're going to do is take our youtube viewer store and load and like i mentioned earlier this execute method in our command should handle errors but we haven't really addressed that yet but i do want to surround this in a try catch even though it just throws we're going to come back here later and do some proper error handling but this is good for now and now we just need to execute this command so we're going to do that i suppose there's two places we could do it we could do it in our youtube viewers listing view model so that's the panel on the left that has our list of youtube viewers or we could actually just do it in the youtube viewers view model which is the view model for pretty much the entire page but i don't know let's do it in the listing view model i feel like that's more appropriate so we're gonna have a command we'll call this the load youtube viewers command and gonna be read only we're gonna set this up in the constructor so initialize that command as the command that we just created and that takes our youtube your store and we already got that so pass that in and now we have to actually execute this command so we could just do it right here and call it execute and all will be good but i don't really think it's a good practice to do complex logic like this and just a constructor especially since this execute method actually makes a database query so we're not going to do this and the alternative that i like to do is have a factory method so we'll have a method here it's going to return our view model and we can just call it loadviewmodel since that's what this factory method is going to do it's going to instantiate the v model and actually load it so let's instantiate this viewmodel first and we're going to pass in all these dependencies and i'm just not realizing this method should be static since it's a factory method to actually create the viewmodel we don't actually have an instance yet we're trying to use this to create the instance so doing that here and we're gonna have to pass in all these parameters and all these parameters are gonna be the same as what the constructor takes since we're passing all these to the constructor so let's do that the youtube viewer store the selected youtube viewer store and the modal navigation store so we instantiate our v model we're going to eventually return this from this method and all we want to do in between is take our viewmodel and call this load command so we'll just execute that and we have to pass in a parameter this can just be null so creating the viewmodel loading it and ultimately our constructor gets to stay simple we don't have to do anything complex in there but now the difference is whenever we instantiate this view model we should be doing it through this load method in our case so up to our youtube viewers v model here we're calling the constructor but instead we should call our factory method so we call that load viewmodel and i guess technically now we're making a database call inside of this constructor which probably isn't good but ultimately we should probably be passing these instantiated v models through the constructor instead of initializing them here so this might be something we come back to if we implement dependency injection but i think it's good for now we're just laying down the foundation but back in this youtube viewer's listing view model we are going to be calling that load command which is going to load the youtube viewers using our youtube viewer store and ultimately we make the database query and then we invoke this youtube viewer's loaded event so we need to handle this event and that's going to be in our youtube viewers listing view model so let's do that so take our youtube viewer store and we're going to subscribe to youtube viewers loaded so create a handler for that and what we're going to do is first off clear out our observable collection of item view models so just clear that and then we're going to iterate over all of the youtube viewers that were just loaded in our youtube viewers store so let's snag those the youtube viewers property so that's going to be the youtube viewer and actually we just call this handy little ad youtube viewer method that we have so paste that in there we're going to pass in each of our youtube viewers from the store and with that our ui should load all the youtube viewers whenever we open the application and before we do that let's unsubscribe from this method and dispose can't forget that minus equals and let's see this so let's put oh no breakpoints so loading up and there we go we load the youtube viewers right on startup so stepping through this we hit our load youtube viewers command let's go in there and we hit this load method we execute our youtube viewers query we get that we update the story we invoke this event and we got a handler for that our youtube viewers listing view model and that grabs all the youtube viewers that we just loaded and adds them to the ui so let's try adding a youtube viewer this would be alan who is subscribed and is a member and submit that there we go ui update we get our data in this details panel we can edit a user so i'm going to unsubscribe for myself there we go ui updates and if we reboot data loaded so we got reading data into this list we can create data we can update data so the last piece of crud is deleting data and we're already kind of set up for that all we have to do is implement it in our wpf application and by setup i mean we already have the database command we just got to use it so on our store one more method here this is going to be async to delete a youtube viewer and actually like we did with our query i think we only need the id of the youtube viewer we want to delete so let's just snag that and we're going to take our delete command and execute that with the youtube viewer's id let's add an event for youtube viewer deleted so the parameter here is going to be the youtube viewer's id the events youtube viewer deleted and we're going to invoke that down here so take youtube viewer deleted and invoke it with the youtube viewers id and all we got to do between here is update our youtube viewers list so we can take that list and i think there's some handy methods on list i think we can do remove all and for this we pass in a predicate for the youtube viewers we want to remove so we want to remove all youtube viewers where the id matches the id of the youtube viewer we just deleted and there should really only be one but it doesn't hurt to just use this handy remove all method so let's create a command to call this method on our store and we're going to add this in our commands folder the delete youtube viewer command and i feel like this is going to be super similar to our open edit youtube viewer command because this is the commands for the edit button on our little drop down menu and the other button we have there is delete so the buttons for these commands are side by side so let me grab all of this although we are going to inherit from async command base so we're gonna have to change that but let's paste this in here so there we go let's make this async command base and we're gonna have to implement that abstract class but let's just move all this execute logic into execute async actually all we need is the this youtube viewer line so not really much of the same stuff in these commands but we can grab that and paste that and execute async we're going to make this method async and we can get rid of execute so let's import youtube viewer we're gonna need the youtube viewers listing item view model we're gonna need the youtube viewers store and we actually don't need the moodle navigation store so let's get rid of that don't need that passed in either and we just got to rename our constructors let's do that and that should be everything we need because all we're going to do now is take our youtube viewers store and call delete and pass in the youtube viewer id and following what i had said earlier just want to wrap these and try catches for now so let's move that in there and now all good on the command layer now we just got to use this command i think this is going to be super simple to set up so in our listing item view model all we got to do is initialize the delete command to be a new delete youtube viewer command which we just created pass in this view model instance and the youtube view restore and let's drop a breakpoint in our command and test this out so i'm going to delete singleton sean 2. let's do it all right we hear a command that's a good start and before i actually delete this i realize we're not subscribing to our youtube viewer deleted event so we need to handle that in our listing view model so that we can actually remove the youtube viewer from our listing panel so let's take our store and subscribe to youtube viewer deleted generate a method for that let's make sure we unsubscribe and dispose while we're here and for this method let me move this down here all we have to do is take our observable collection of view models and let's see is there remove all no there's not but we can do first or default so we're gonna get the first oem view model where the youtube viewer id equals the id of the youtube viewer that was deleted so put that into a variable and now that we have the view model we should probably first check if it's not gnaw because first or default could return null so just a little null check here so it does not equal null if it doesn't then we'll take our observable collection of item view models and we can call remove and pass in the item that we just grabbed out of the collection so that'll remove it from the ui and i actually realized another issue so let's see that in action first so that it'll make more sense to describe so we got our breakpoint let's delete this youtube viewer we hit that let's get into our store we execute the command against our database successful let's make sure we actually remove it so right now we have three and the youtube viewers list in our store let's remove it and now we have two so that was successful and we invoke our youtube viewer deleted and now we're in the listing view model so let's remove that view model from our list we found it so we're gonna remove it and we should see that in the ui and there we go and i forgot to demonstrate what i thought would be an issue so what i'm thinking is if we select this youtube viewer and then delete it i don't think our details panel is going to update so oh it does why does it do that so i think it gets unselected because when we remove the view model from this listing view model then technically the selected item becomes null and it does actually update our selected item store so i'm pretty excited about how far we've come with this but now we have to account for anything that could go wrong so to demonstrate this i'm going to head into my queries and let's say that this query takes five seconds to execute for some reason and let's do this for all of our commands too so five second delay for each of our commands and now let's see how our application feels so starting up all right so we haven't created any youtube viewers so i guess we'll have to add one oh whoa okay so that's not cool so we have no idea that the application is loading when we don't have any youtube viewers we're just kind of confused we think we have none even though they're still loading so we need a loading indicator here how about for adding so let's submit this and oh i guess the app's broken i don't know what's going on maybe we just oh there we go so yeah we need a we need a loading spinner here as well so we know that we're submitting and yeah i'll just i could go through all of these but we need loading spinners and that's the first thing we're going to address so let's start with the loading spinner for this initial query of our youtube viewers listing and actually what i want is i want a loading spinner for pretty much all of this content i don't really want to show any of this until the youtube viewers have loaded so this kind of changes the way that i want to call our load youtube viewers command so right now we execute this command in our youtube viewers listing v model but this viewmodel only controls that panel on the left so having the load command here the best we'd be able to do is just have a loading spinner on that panel on the left although we could do the loading here and then communicate to our page v model that the load is occurring through the store by setting like is loading flags on the store but i don't feel like that's necessary and i feel like it's more appropriate to just have the loading on this youtube viewer's view model so let's move this up here so we're going to need our load command in the youtube viewers view model and then while we're here since this v model is not going to do the loading anymore we can just instantiate it regularly we don't need the factory method because instead the static factory method is going to be on this viewmodel so call this v model pass in what we need for the constructor which i think is going to be the same as what we have in the listing view model so maybe it'll be easier to just cut this out and while we're here we can get rid of this command initialization and come up to our youtube user model let's paste in that factory method except this is for the youtube viewers view model now so instantiate that and now we're executing the load command up here so it works exactly the same we just moved our command up a level and we still have that five second delay so any second now although i've been waiting for a few seconds and this actually isn't gonna work because we didn't initialize this command so let's do that so initialize that pass in our store and it's still not going to work because we're not calling this factory method so this is all the way up in our app.zambo.cs instead of calling the constructor we want to call load viewmodel so try this again there we go now they load so back on our youtube viewers view model i ultimately want to have a property it's going to be a prop change it's going to be a boolean for is loading and we want to toggle this property whenever our load youtube viewers command is executing so let's pass this viewmodel into the command so we just pass in this and add a parameter for our viewmodel and import that throw it into a field and now when we start executing this command we're going to take our viewmodel and set is loading to true and when we're done whether or not we catch an error or not so we're going to use a finally we want to set is loading to false so now we have this property on our viewmodel it's working as expected so now we just have to use it in the ui so let's go to our youtube viewers view and like i mentioned earlier i don't want to show any of this if the page is loading so we're going to wrap this in its own grid so surround that with a grid and then we're going to have another grid that we're going to display if the page is loading so for now we'll just put a text block in here and we'll center it in the center actually we'll make this text alignment center and it'll just say loading youtube viewers and then we only want to display this grid if is loading is true so we're gonna have to add styles to these grids and i think we've done this in another place i think it's in the youtube viewers details so we have these grid styles where we just toggle visibility so gonna be super similar to this let me just copy this and use that for each of these grids so changing the style except now we're binding to is loading and if is loading is true then we want our loading prop to be visible and for our grid with the content if is loading is false then we're ready to display the page's content so we should be able to see this now and it should look as expected so loading up i think i want this aligned in the center of the screen like vertically say can we add some vertical alignment center how does that look it's probably done by now let's try this again yeah there we go that's what i want except it's kind of bland it's just text so we can add a loading spinner so if we go to our nougat packages and search for loadingspinner.wpf this is again by singleton sean we're going to install this pretty nifty to use so in our component above our loading text we can add this loading spinner so import that we'll just call this namespace custom and now that we have multiple stuff in this grid this should probably be row definitions or just to keep things simple it can just be a stack panel so these will stack on top of each other automatically we'll have to update our style to account for this stack panel and we'll add some top margin on our text block let's do maybe 10 for now and then this loading spinner i don't know how big it is we'll have to play with it but let's see how it looks right now and i don't even see anything i think we have to give it a size and that's going to be using diameter so we'll roll with a hundred see how that looks i still don't see it i don't even know how to use my own components oh that's right there's actually a property on this called is loading and you can set this to true or false maybe i should have specified it as true by default so that we wouldn't run into this issue but we could bind this to our is limiting property on our view model but if we're displaying this component then we already know that we are loading so we'll just set that as true and now we should see it there we go that looks pretty good we're not centered anymore and i think that's because we're using a stack panel instead of a grid so the vertical alignment has to be on here instead of the text block so let's try this again and there we go that looks good it's i think the size is good i think we can control the thickness i think border thickness is what we want so let's try three maybe not i don't know what this is oh okay so this is on control we just want thickness and okay that's what i defined on the loading spinner so that should look better but there we go that looks good i think that's thick enough not too thick so we're good for loading on that initial query so i feel comfortable removing that delay on there all right one out of four now we just gotta handle these commands and that's gonna be a little bit more complex but similar pattern i think it's just the ui that's gonna be different so let's start with our add youtube viewer view model and our loading state is going to go on the form view model actually so let's have a prop change on here this will be a boolean for is submitting and actually we can use this is submitting property to toggle can submit so if we're already submitting then we definitely shouldn't be able to click the submit button again although to be honest that's something that we can actually just build into our async command base and we can just do that by controlling can execute so what we can do is we can have we can just start with a prop change but we're not going to call one property changed but this is going to be a boolean for is executing and then when we start this execute method we'll set is executing to true and when we're done so we can put this in a finale and we'll set is executing to false when we're all done and what we can do is override our can execute method and we can only execute if is executing is false so if we're not already executing then we can actually click the button so also since can execute is dependent on is executing we should call it one can execute changed and the setter for that property so that our ui knows to re-grab this value so this is a nice addition now our ui is automatically going to disable the button whenever we click it and it's still running so the user can't just spam it but anyways back in our 4mv model we have this property for is submitting so let's use that on the ui first so go into our youtube viewer details form component and i'm thinking to the right of our buttons we can have a little loading spinner here so let's import that and here's where that is loading dependency property is pretty useful so we can bind this to is submitting so this loading spinner is only going to be visible if we are submitting and we'll set the diameter it's going to be a small loading spinner so probably just 25 and now we have to add some margin between our cancel button and our loading spinner so 5 should be good and now we just have to toggle is submitting this property on our viewmodel and that's going to be done in our add youtube viewer command so when we start executing this method we're going to take our form v model and set is submitting to true and then when we're all done so finally here we'll set not that we're gonna set the forum view model is submitting to false and then we'll go ahead and do the same thing in the edit youtube viewer command so i have a finally block here that sets is submitting the false and then when we start this command we'll set is submitting to true all right so easier than expected there let's try this out let's add a youtube viewer and submit all right loading spinner looks good i was thinking about increasing the thickness but i feel like that's sufficient but now let's try editing so change some stuff and submit that and there we go get the loading spinner again i don't know maybe it should be thicker let's let's try it all right so we're going to set the thickness to maybe just two i think before we did three on the other loading spinner but let's see about this yeah i think that's better just a little bit more makes it stand out more so the last loading spinner we need in fact we can remove the delay from our create command and our update command for our database commands and lastly we just have to handle the delete delay so that delete button is in our youtube viewers listing item inside of our drop down menu so what i'm thinking is this delete button could have a loading spinner to the right of the delete text the only issue is whenever we click this delete button it closes the drop down automatically but we can remove that because the only reason it does that is since we added this event setter and we have this button click handler which closes our drop down resets is open to false which closes it so we could just move this event setter off of this style and only have it for our edit button so whenever this is clicked we'll use the button click handler so let me make sure that works let's open this so we should still be able to edit but then if i click delete it stays open and right now it's doing the delete so we just need this loading spinner over here there we go it took some time but there we go so our content now is not going to be just text we're gonna have to open this up and specify the button content like this and we have to throw everything into a grid or maybe a wrap panel is more appropriate to have things be side by side so first we're gonna have our text block for delete and have some margin to the right on this for our loading spinner so let's add that loading spinner import that we'll go with two for the thickness again and i guess for the diameter we'll try 25 it might have to be smaller in this case not sure and then for is loading we're gonna have to bind that to a property on our viewmodel and we'll call that property is deleting in this case and i can't call my loading spinner namespace just custom we're gonna have to call it i guess loading spinner and we can't use custom because we already use that for the drop down menu so maybe i should have thought about that before making my namespaces custom but that should be good and now got to move up to the view model and add this is deleting property so that's going to be our listing item view model let's add a prop change here a boolean 4 is deleting and now we just got to toggle this property on our delete command so if we head into the command we already got our viewmodel instance in here that's good so pretty much all we have to do is take our viewmodel and set is deleting to true at the beginning and then when we're all done we'll set his deleting to false all right let's run this and try deleting all right so take that click delete and our margin is not what we wanted but that was really the only issue i saw was the margin so we can just fix that and it's because i did margin on the bottom instead of the right so that's not good let's try this again although i'm not fully sure if i agree with this experience where clicking delete does the loading inside of this little spinner because technically you could just click edit after deleting and that would put you in a really weird situation we could just disable the edit button as well but oh i have a better idea so let's bring back this event center we had so we had an event center for click and the handler is button click so we're applying this to every button in our drop down menu and all this handler does is close the drop down whenever we click and let's just change this content back to delete so simple now and we can get rid of this click handler on our button because that's already applied by the style so i apologize for just completely undoing this but what i was thinking was when we're deleting we could just hide this drop down menu and share the loading spinner so we can have a grid that surrounds everything let's move these margins and grid column properties over and then inside this grid is going to be the drop down menu so let's move that inside of there while we're down here we can just remove the loading spinner from our button so again sorry for undoing all this but i feel like this is going to be better and then inside this container grid we can have two more grids and one grid is only going to display when we're deleting and that's going to have the loading spinner and the other grid is going to have our drop down menu so we kind of have this structure in other views i think we just did it in the youtube viewers view so we have this first stack panel that has the loading and then the second grid that has the content so we can just copy everything from the style up through the stack panel and we'll put that into the listing item inside of this grid right here so paste that in there make sure we use the right loading spinner and namespace that we had to change and then we should make this a grid instead of a stack panel so change that i don't think we need this vertical alignment either so we can get rid of that change to a grid everywhere and instead of is loading we're bonding to is deleting so that's what's going to make this loading spinner visible our loading spinner i think 20 was the diameter we used we can just hard code isolating the true and the thickness was two for the spinner we had previously we don't need a text block in here that says anything i think we're fine without it because the user knows what they just did and then we have the second grid that's going to contain our drop down menu let's make sure we bind to is deleting and make sure we get our closing grid tag so we can put that after this drop down menu there we go so we have our first grid with the loading spinner that's going to appear when we are deleting and then we have our second grid with the drop down menu that's going to be visible when we are not deleting so let's see how this looks i'm hoping this looks better let me just add a viewer and then oh and then delete and there we go we get this loading spinner i like it and then once we're done deleting it unselects it that's good so we've handled all of these loading states so we can remove loading from our database command so we've handled loading states but what about when these database commands and queries throw exceptions so let's throw some exceptions here and we're gonna have to handle these so let me just put these in each of the commands so one by one through here and then in our queries as well or i guess just our one query throw that exception and i think we just eat all of our exceptions right now so it's not like our application is going to crash so we threw the exception nothing really happened we just don't really know it was thrown so we should have an error message for that so starting with a query failing we're going to put an error message on our youtube viewers view so this is going to be once we're done loading so we can close out that stack panel with the loading spinner and it'll go in the second grid that's going to display when we are not loading so we're going to start off by surrounding the grid that has our content in another grid so let's surround all that with a grid and we're pretty much taking the same kind of approach that we did with loading where we're gonna have two grids and we're going to conditionally display them based on some kind of binding so similar style we're going to have in each grid and this first grid is going to have an error message in it so we're going to bind to some kind of property like has error message and if we do have an error message that this will be true then we want to display some kind of text in here so this will probably just be failed to load youtube viewers and we should probably have a common style for all of our error messages so we'll add that some kind of style called error message let me just add that real quick in our app.xaml so we just copy our page header style because error message is also going to be for a text block the only difference is that i guess for now we'll just make the foreground how about red and then back in our youtube viewers view the other thing we have to do is actually use this style for our content grid and we only want to display our content if has error message is false and if loading fails we should probably offer some kind of refresh button but for now we'll just say please restart the application at the end of this prompt so all good on the ui now we just have to add this has error message property and now i think about it we're kind of jumping the gun here i called this has error message which implies we're going to have probably an error message property on our view model as well so i don't think we have to hard code this error message here we can just bind to a property called error message so we're going to add two properties to our viewmodel error message and has error message so let's go into our view model so i have a prop change for error message and then another property it's going to be a boolean for has error message and this will just be a calculated property where we check if the error message property is not null or empty so put this exclamation point in front and checking the error message and since this property is dependent on error message we're going to want to raise one property change for it has error message whenever error message gets set so now we just got to set this error message and this is going to be done in the load youtube viewers command so we'll catch exceptions and we'll take our view model and set the error message to what we had before which was failed to load youtube viewers please restart the application and then before we execute all this we should probably clear out any stale error messages so we'll set error message to no so this should work we should see the error message and there we go it's not in the center like i'd like it to be so can we live update this let's see let's add horizontal i mean text alignment to center and vertical alignment to center and is that going to update oh yeah there we go let's put a period at the end so that's not going to live update but in our commands throw our period on there and then we should be good so we can remove the exception from our query all good there and now for our add and edit youtube viewer forms we're gonna head into our youtube viewer details forum component and i'm thinking underneath all of our buttons we can have another text block so go in grid row two give it some top margin we'll go with about 15 and the text for this will be some kind of binding to an error message property and the visibility is going to be dependent on has error message but has error message is a boolean or it's going to be and visibility is a visibility type so what we have to do is use a converter here and there's a built-in wpf converter and we're gonna have to define that so we can put that all the way up here so our user control resources we're going to use a boolean to visibility converter and we have to give it a key we'll just call it the name of the converter and we can use this down here so the converter is going to be our boolean to visibility converter so if has error message is true this will be visible if it's false then this will be collapsed so now we just need these properties on our form so we can just grab those off of our youtube viewers view model so we can copy these properties and move those into the youtube viewer details form view model so paste those in here so got our error messages on the forum now we just have to set this error message property in our commands so for the add youtube viewer command first let's clear the error message when we start executing and if we get an error we'll set the error message here this for adding so we'll say failed to add youtube viewer please try again later and then same kind of approach for edit so handle the exception set the error message we'll say failed to update youtube viewer please try again later and then just clear the error message whenever we start executing so let's try this should be good here so let's add and submit and that's not what we want so i forgot to add the styling and then i forgot to add the grid row so back in our form the style for this should be our error message and then we need another grid row so let's add that so three grid rows up here that should be good so let's add a youtube viewer and submit i don't like how it grows the window whenever this error message appears so i don't know maybe it'd be a good idea to hard code a width on this moodle so that we don't have to worry about it growing and then there's some other things we can change with this text block but let's hard code a width on the add youtube viewer view so we can just set this to maybe a thousand and we're going to do that for the edit view too so let's try that how's that's going to look and yeah that's way too big okay let's try 600 see how that looks hopefully not too big again uh still too big let's try 400 that's a little bit bigger than before but looks good to me so if we create this i feel like it was kind of weird how the model got taller but another thing about it i don't think it's too bad i feel like it's fine so for our database commands we can remove exceptions for create and update and now where do i want to handle error messages for delete so we click delete it closes it fails obviously i guess the best we could do is put an error message like underneath their name so let's try that so on our listing item view model we're gonna have an error message property and it has error message property so let's just add these over here and then we'll head up to the command and take our itemv model and set the error message to null when we start executing and when we catch exceptions we'll set it to failed to delete youtube viewer please try again later so now we just need that in the ui so i'm going to grab the text block that we put on our details form because it's going to be a similar approach on our listing item so let's move over here and now instead of just having this text block here with the username we're going to have a stack panel we just roll with that we don't need a grid or anything so move that in there and then we're gonna have our text block for the error message so we don't need a grid row one here top margin gonna be much less we could probably just do five and then i feel like this font size can be much smaller so let's just make it 10 so it doesn't take up too much space in our listing and then good on bindings we need our boolean divisibility converter in here so let me just grab that from our form copy that over paste that for the resources and then move this grid column up to the stack panel and i think we should be good to go let's try this so it looks good so far let's delete which should fail and we get the error message so that's probably the best we could do i feel like there's maybe a little bit too much margin actually let's try just three i don't know if that actually changed anything what if it's zero what's going to happen then uh so it definitely moved it closer i don't know zero looks pretty good let's just do zero all right so with that i feel like we can remove the exception from our delete command and quite frankly i feel like our application feature wise is pretty much done our ui looks pretty solid with the master detail view we load our youtube viewers from the database we can create update and delete youtube viewers on the fly through the ui so with that i guess we can dig into some fun stuff how about we set up dependency injection so for that we're gonna head up to our app.zambo.cs and we're gonna set up the dotnet generic host so the.net generic host does more than dependency injection it also handles things like app configuration within appsettings.json and other infrastructure like logging so definitely useful for more than just dependency injection so starting off we're going to need a new gea package for this this will be microsoft.extensions.hosting so let's search for that and install that so now that we have that package everything revolves around the host so we want to set up a generic host so we're going to have a property for our ihost let's import that from the hosting namespace well let's call this the host and we'll set up our host in our app constructor so the best way to do this is just with host create default builder because that adds like it says right here a bunch of pre-configured defaults i think one of those defaults is loading app settings from the appsettings.json file so definitely nice to have and then we can do a bunch of configuration with the host we can configure services for dependency injection logging app configuration but we'll do that in a second let's just build this for now and then we're going to use this on startup so when we start our application we're going to take our host and start it and then when we stop our application so we're going to override one exit we can take our host and stop async and then we can also dispose our host here as well so host.suppose so i'll go with general host setup now let's get into dependency injection so let's configure services here and we just pass a call back in here the first is the host builder context so we'll just call that context and then our services collection so let's call that services and now for the good stuff so we're going to register all of our stores and our database queries and commands and also our db context stuff so let's start with the db context so we can add and we're going to need some extension methods here so add singleton is what we're going to start with and we want to add our youtube viewers db context factory as a singleton and this service requires db context options so we can actually add those db context options as a singleton tool so db context options and let's just cut these out of here and paste those in there as the instance that we want to register so we're also going to have to move our connection string up here as well so we can reference it when we register the options and we can start deleting stuff down here too let's get rid of the factory so that's all good for db contact stuff now how about for these database services so the queries and commands let's just register those with our services these can be singletons as well and just to explain singleton means there's only one instance that's going to be created for the entire lifetime of the application so we really only need one instance of these queries and commands because there's not really any state inside of these so it's okay if multiple things are using the same instance so we can just add these and all these queries and commands take a db context factory but we already registered that so these should just be all good to register like this so the query is good and then all of our commands there we go so we can get rid of those down here and now for our stores so stores we definitely want to register as singleton because they hold our application state and it's our single source of truth so definitely don't want more than one instance so we'll register our modal navigation store and then our youtube viewer store and our selected youtube viewer store and all the dependencies for these constructors have already been registered so all good here so we can get rid of that and i do want to mention it doesn't matter the order that you register stuff in i know i kind of ordered it where things down here depend on things up here but the order definitely doesn't matter at all and now that all of these services are managed in dependency injection we can get rid of these fields up here so cleaning this up a little bit so we're definitely gonna have to update all of this stuff so it actually works so for the database migrations we can resolve our youtube viewers db context factory from our host services we can get required service and we want the db context factory so put that into a variable so we can use it so all good on these migrations and next up we have all this stuff down here but ultimately what i want to do is just be able to take our host services and just resolve a main window so really i want all of this to be managed by dependency injection so starting off we're gonna have to register our main window we should probably just make this a singleton i'm gonna said singleton sean adds singleton shawn sure but when i add main window and we're gonna have to manually register this so we can register with a callback and this callback takes our services that are built so this is our service provider so we're gonna use that to instantiate this main window and we want to set the main window data context to a main view model so we're going to take our services and resolve the main view model so by doing that that means we're gonna have to register our main v model so let's go ahead and do that that can also just be a singleton because we only have one main view model so how does that work so we should just be able to register this normally so don't have to do anything special so main view model depends on our modal navigation store we already registered that the last thing we have to register is this youtube viewers view model so let's add that and this could be a singleton because we only have one instance of this v model in our application but i feel like we should make it transient because like down the road we could have more i don't know why we would but basically what transient means is whenever we request the service it's going to create a new instance so we don't just have this single instance we're going to possibly have multiple in our case we're only going to have one because we're only going to resolve this once for our main v model and we can't just register like this even though our constructor takes all these services that we've already registered in dependency injection and the reason we can't register like this is because we want to construct this v model by calling loadviewmodel so what we can do is pass in another callback here and it's going to take our services as a parameter and in fact this callback is going to be kind of big to just have in a callback so i usually put stuff like this into their own methods so we can call this create youtube viewers view model and generate that method and we just throw that down here i guess and all we're going to do is return the result of taking our youtube viewers v model and loading it so this takes a youtube view our store we can get that from our built services so grab that store and then there's two other parameters so we want the selected youtube viewer store and i'm guessing this last one's the moodle navigation store and yeah there we go all good here so whenever we resolve this youtube usb model it's gonna execute this method to create the viewmodel and start loading it which is what we want so let's get rid of all this junk because it's all managed in dependency injection now and with that hopefully our application still works so let's run this and oh so it doesn't work unable to resolve service 4 i get all youtube viewers query so this was kind of a lame mistake so what's going on is our youtube viewers store is trying to resolve these interfaces but when we registered these queries and commands we didn't register them against our interfaces so we need to register the i get all youtube viewers query and the implementation for that is our entity framework query so we need to update that for all of these other registrations make sure we register against the interface so that these actually resolve correctly so let's try this again should be good now i'm hoping and there we go so loaded our data we can add stuff we can edit stuff and we can delete stuff okay all good still so i guess the last thing i want to do related to the host is load this connection string from an appsettings.json so over in our project let's add a new item here this will be a json file called appsettings.json and we want to take this file and we want to make sure that we copy i guess we can do copy if newer but make sure we copy it to our output directory and then we can have this connection strings object in here and we'll just call this i guess sqlite and the value for this will grab from our app.xml.css so cut this out paste that in here so our connection strings and our app settings.json now and now we can grab it out of there by taking our host context taking the configuration and we should be able to get connection strings do we have to import something here maybe it's get connection strings is that it oh there we go so it's get connection string thanks intellisense and the name that we gave was sqlite so let's get that and now this should work we should be able to hit our database even though our connection strings in this app settings file so let's start up and there we go still works perfectly fine i guess another cool thing i like to do related to configuring services and host builder stuff is if this gets to be too big you can break this down into extension methods so we can have something like add db context and then we can add this extension method so ever in our project let's add a new folder for host builders and we'll call this it's going to be a class the add db context host builder extensions and this will be a static class so we can have an extension method in here so we'll have this static method again has to be static since it's an extension method and we'll call it add db context we're extending our i host so import that and the return for this method can i think it can be the i host builder so want to return this so we get the fluent builder feel so actually we're not taking an ihost we're taking in an i host builder so fix that but now back in our app.xaml.cs we can import this at least i thought maybe we named this wrong maybe i need to return the host builder first for intellisense to pick it up let's try this and then import now oh just looking at the air it's conflicting with entity framework because obviously there's a extension method on services to add db context which isn't popping up now because we have so many errors so let's just manually import youtubeviewers.wpf dot host builders and okay now it picks it up so there's our extension method we can even click on it here we go we're in here now so what i want to do is move this stuff into the extension method so let's just grab configure services too because we're going to be calling that in here so copy that and paste that in here we're calling configure services on our host builder that we're extending and then close this out so we need a curly bracket and a parenthesis and then import all of this stuff that we need so one by one all good there and now all of this is tucked away in the extension method and we can remove it from here so just wanted to show that off i don't think i'm going to go through and do it for all of this but if this callback started to get out of control i definitely consider moving stuff like this to maybe like an ad services and ad stores ad view models and then maybe and add views or something so before i close out the building of this app there's definitely some things i wanted to cover to make this feel complete so for one we could definitely go through and test out some edge cases so one i can think of off the bat is if the name is really long so let's just make it super long and submit that so yeah definitely don't want this to happen so up in our main window i feel like for one we should set a max width on our application's content so maybe like a thousands usually what i do so now this is a bit better and also if we go full screen then our application isn't like super wide which would look weird in fact let me remove next width i don't think we've gone full screen at all yet and yeah it just takes up i feel like more space than it needs so we can bring the back that max width at least i like the max width you can play with it a little bit more if you want to and then we have some wrapping issues so i don't know i don't like this horizontal scroll bar so in our listing component so youtube viewers listing we can take our list view and we can take horizontal scroll bar visibility and disable that so there we go now you can't even scroll and how do we want to do this text so do we want to wrap it let's see how that looks so we can come into our youtube viewers listing item component and we'll set text wrapping to wrap and i don't know how i feel about that because it just takes up more lines and it doesn't look good with our items that take out one line so i don't know i might just not wrap it so yeah i don't know i feel like we shouldn't wrap it but then maybe over here we should so that you can actually see what the full name is i feel like it's okay to not show it fully in the preview but over here would make sense to show it so in our youtube viewers details panel we'll come on down to the username and we'll take the text wrapping and wrap it and that looks good i think that's fair so let's create a bunch more youtube viewers oh and we added a bug so we set that max width on our main window but now our modal has that max width so the background of the modal doesn't fill the screen so that's not good let's move the max width down to our view so that it doesn't mess with our moodle and there we go that's better so let's create a few more viewers just give them all the same name and i just wanted to make sure scrolling worked which it does i'll admit i did expect scrolling to be on this list view but that wouldn't make sense because we have scrolling over here so the scroll bar is going to have higher priority and i think that's fine we could roll with that one thing i don't like is that you can actually multi-select on this list view which doesn't really make sense we don't really support bulk operations and we kind of expect only one to be selected so in our listing component we can set selection mode to just single and now if we try to multi-select that doesn't work so basically what i'm saying is that if you're building your own application definitely go through and check edge cases like this just to make sure your app is working as expected i know it's probably common sense but i feel like sometimes we all forget so another thing i was thinking is when we add a youtube viewer so submit that it'd be nice if it selected it immediately i feel like that would be a pleasant experience so we can do that and what we can do is go into our selected youtube viewer store and let's subscribe to youtube viewer added on our youtube viewers store and when a youtube viewer is added we're just gonna set that youtube viewer as the selected youtube viewer so now if i add something and submit it gets selected immediately although it's not represented in this list view can we fix that i think we're gonna have to head into our listing view model and i kind of have an idea of how we can do this so we could remove this field that backs our selected itemview model so let's get rid of that which means we don't have to set it we still have to call it one property change let's move that to the bottom of this method and we'll see why we do that in a second and then to get the youtube viewer that we passed to our selected youtube viewer store we can grab that off the value that gets passed into our setter so now for the getter we can take our observable collection of listing item v models and we would get the first or default where the youtube viewer id equals the id of the selected youtube viewer on our selected youtube viewers store so now it's almost like we're binding to our selected youtube viewers store which means we'll be in sync with any changes that happen over there and actually we don't have to call one property changed in this setter we should be calling on property changed for this property whenever our selected youtube viewer store raises a selected youtube viewer changed event so let's subscribe to that and make sure we unsubscribe and dispose and all we're going to do in this handler is call one property changed for our selected youtube viewer listing item view model so let's make sure this works i don't know if i've ever done a two-way binding to a story that's probably the first time i've ever done it so we're gonna add a youtube viewer and submit and it doesn't even work so what's going on here does our selecting still work that does so what's going on here let's make sure we are re-grabbing this when we create the youtube viewer so we are so what's the value of this item view model when we add this youtube viewer so okay it looks like it's gnaw so that's not good because it shouldn't be gnaw i feel like the issue is that we're handling selected youtube viewer changed but when we handle this we haven't added the youtube viewer to our observable collection so i think what we should do is since this property is dependent on our observable collection of youtube viewers we should probably update this property or in other words call it one property change for this property whenever anything changes in our observable collection so we can take our observable collection and subscribe to collection change and get a handler for that we shouldn't technically have to unsubscribe because this observable collection lives inside of this object so it should be good there but all i want to do is whenever the collection changes i want to call one property changed for the selected item so paste that in there and let's see this should work now i hope so there's no reason for it not to work so let's add and just pop something in there and okay yeah so it is selected it's not like focused but you can definitely see the selected state of it and i think that's good so i'm glad we were able to get that working we have like this two-way binding to our store which is pretty cool all right so i'm pretty satisfied with where this application is we've hooked up to a database the ui is pretty solid we handled plenty of edge cases with loading errors and any ui edge cases we also set up dependency injection and the dotnet generic host so i'm going to wrap up our development here because otherwise this video is going to be infinitely long i think it already is infinitely long but if you want to add things like logging advanced navigation animation drag and drop validation a windows notify tray icon layout components all kinds of stuff like that and as i list those things out i second guess myself and think maybe i should just make this video infinitely long but no if you want to check out those things i have plenty of videos like that throughout my channel so i'll try and link some more topics in the description but we're gonna wrap up so all we're gonna do now is set up git source control so i have this git terminal at the root of my project so if i ls here we just have the solution in here and all of our project folders so i'm going to do a git init to initialize our repository and we definitely should have done this at like the start of our project and committed as we went but we can just do it all at once we're going to want to add a git ignore file so we can do touch dot git ignore and now if i ls here uh it doesn't pop up maybe ls a we got our git ignore file and let me just bring up windows explorer side by side in case you're not doing this from a terminal but added this git ignore file just a text file and we're going to open that file i'm just going to use vim but you can just open it and use the built-in windows text editor but we can ignore this dot vs directory so this right here we can also ignore our bin and obj directories so dot bin or not dot bin just bin and obj we also said we were gonna ignore our design time db context factory so let me copy this name and add that in here so dot cs if our connection string were sensitive then you'd probably want to ignore appsettings.json but i think that's fine to leave in for now because it's just a local sqlite file so i think this is everything we need to ignore so let's write this so save the file close it and now let's track everything in source control so it get add dot and now let's get status make sure we didn't miss anything with our ignores so it looks like our bin and obj is definitely not in here so that's good you definitely want to track migrations in source control so those were added too our design time db context is not in here so that's good so let's commit this so git commit our message so i don't even know how to describe this commit we'll just call it add application or how about add initial application just in case i add some other stuff down the road but just going to commit this and all good there so if we get log there we go we see our only commit so now let's push this up to github so if you don't have a github account you should definitely make one not only is it good for collaboration and you can keep your code in the cloud but it's pretty good for the sake of a resume too but anyways what we're gonna do is come up here and create a new repository to host our project i always call this youtube viewers wpf and this is a crud wpf application for managing youtube viewers i guess the only sad part about looking back on our application is that it's kind of pointless like all you do is manually add these youtube viewers i don't even get the point of it but oh well it's a good example of how to build a wpf app so it's up to all of you to take this and build something amazing and useful but we can skip all these steps down here you can make the repository private if you want but we can create this and we want to grab this git link right here so copy that and we want to add that as a remote so we'll do git remote and you are going to have to do this from the terminal add we'll call it origin and paste that in here and run this you are going to have to have ssh keys set up in your terminal i don't even know if i have that but i guess we're going to find out but now we can do a git push origin and we want to push our master branch so let's do that moment of truth oh okay so i did have ssh keys set up hooray so if you don't have ssh keys set up you have to read through the github docs hopefully i can link those in the description otherwise you might be able to use https instead i think you have to specify like your username and password if you do this but hopefully you can seamlessly get that set up but if we refresh this we should see our application in here so we got our code in here we could add a readme if we wanted to but what i want to do is actually publish our application and upload it as a release so what we got to do is first we should get into our youtube viewers.wpf folder since this is the app that we want to publish and let's build this and release so not build configuration release we probably should have ran our app in release mode to make sure it worked but i'm feeling pretty confident and now we just want to do a net publish in release mode and we're gonna publish this as self-contained and what that means is that the dotnet runtime is going to be included with our app so the computer running this application doesn't actually have to have.net installed which could be useful but to do self-contained i think we have to specify this runtime identifier we can just do win x 64. i have another video where i explained net publish way more in depth so i'll try and link that in the description too but moving along here we can also publish as a single file which i like to do because whenever we do self-contained and it includes the.net runtime there's so many files so having the single file option is definitely nice so that's dash p colon publish single file equals true and let's try this all right it's doing it so success let's head into our bin where that was published so youtube viewers.wpf bin release windows win-x 64 i think and then this publish directory so there we go so publish single file isn't necessarily a single file like all of these are required but it's way smaller than if you didn't have published single file so let's run this and there we go our application works so we can add a youtube viewer all good we can restart the app and all the database stuff is working fine so let me actually rename this from youtubeviewers.wpf that doesn't look very good so to do that we can come into our project come the properties so all we got to do is change the assembly name to what we want so we'll just call this youtube space viewers and make sure we save that and now try publishing again and here we go that looks better in my opinion so now let's take all of this and we can send this to a compressed folder we'll call it youtube viewers.1.0.0 and now back in github let's create a new release and we'll just call this youtube viewers 1.0.0 or actually we just have the version we can also create a new tag we'll call this 1.0.0 as well uh description the initial release of the application and then we can just drop that zip in here so take that and drop it oh and while it's waiting we should prefix our version with v so let's do that v 1.0.0 let's make that our release name as well and there we go our zip was uploaded so let's publish this all right this is pretty exciting so let's download the zip now so anyone could come in here download this so download the zip thing my internet is like super slow all right but here we go download complete so let's open this let's extract all of it and here we go let's start the app and there we go i must have published this with a database file in there so i should probably republish this so that i don't have the stale data in here i'll do that offline but yeah that wraps up our application so hopefully this tutorial was extremely helpful in getting started with wpf hopefully you can take these skills and build something more useful than our app that we built here but definitely a good start and we tackled some pretty advanced stuff if you want to dig further in the wpf then quite frankly my channel is the place to be i have plenty of videos on wpf and i'll try and link as many of those that i feel would be helpful to watch to extend this series in the description but if you made it this far congrats definitely consider yourself a wpf application developer but if you had any questions criticisms or concerns throughout this video be sure to leave them below in the comments section if you enjoyed the video of just building this entire app consider becoming a member other than that leave a like or subscribe for more thank you
Info
Channel: SingletonSean
Views: 90,915
Rating: undefined out of 5
Keywords: wpf, full, course, start, finish, application, .net, project, core, ui, view, viewmodel, mvvm, stack, end, microsoft, model, store, state, management, xaml, text, list, event, driven, domain, design, user, interface, api, dependecny, injection, nuget, package, long, learn, how, to, build, pattern, enterprise, architecture, decouple, maintain, structure, tutorial, views, figma, control, component, page, service, downstream, database, entity, framework, dapper, dbcontext, db, async, await, script, deploy, publish, git, github, reference, fullstack, help, beginner
Id: 54ZmhbpjBmg
Channel Id: undefined
Length: 244min 32sec (14672 seconds)
Published: Sun May 29 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.