Love And Hate: A Beginner's Look At WPF in C#
June 17, 2024
• 1,342 views
Even at the time of publishing this video, the majority of my software development career was spent building Windows desktop applications. Now, it's been a few years since I've had to do that but it's still something I have experience in. Which brings us to my good friend WPF!
WPF is the evolution of WinForms and it has an expressive markup language, XAML, that you can leverage alongside your code.
So what's my beef with WPF?
Check out this video to see some WPF basics, a couple things I like, and some things that I was always fighting against!
View Transcript
so many of you don't know this but I spend years and years and years building with this technology hi my name is Nick Cosentino and I'm a principal software engineering manager at Microsoft before asp.net core and everything else was becoming very popular everything was kind of going to the web application side of things I spent tons of time building desktop applications for Windows this started off with Wind forms for many years and then from there we went over to WPF that's Windows presentation Foundation so in this video I'm going to walk you through some very basics of WPF going back to some of my roots and talking about some of the things that I really like and some of the things that I don't this is with respect to the framework and how it guides you to coding along with it a quick reminder that
if you like this kind of content subscribe to the channel and check out that pin comment for my courses on dome train with that said let's go make a new WPF project on my screen I'm just in Visual Studio here I've gone to select a new project to create we're going to select a WPF application I have it in the left bar here but you can go ahead and search WPF and select WPF application so go ahead and do that I am going to name mine WPF playground sample application for the project and I'm going to make a new solution just called WPF playground because spoiler alert I'm going to make some more videos about this content and we'll expand upon this once I go ahead and create this we're going to use net 8 we will get a new application and it's very basic
but I'm going to walk you through some of the pieces that we have here because again if you've not used WPF you've never seen this stuff before this is just a very lightweight intro video to some of the things that we have going on here so the first thing that you'll notice is that we have a visual editor here right we have a preview of what main window looks like in this case that is going to be as you might expect the main window of our desktop application but you can see that my screen is already divided into two now I do have code on the bottom I'm going to go ahead and minimize this part so we can see a little bit more code but we have the preview up top and the code on the bottom but the code that you're seeing is
not like code this is what we call zaml XL and this is going to be the markup language that we use for creating WPF applications so the first thing that you'll notice is that there's just a ton of what looks like Nam spaces because that's essentially what these are Nam spaces at the top here we have a class name that we're giving this window right so if I go split this out a little bit more we can see that it becomes maybe slightly more readable but these are the different properties and things that we can go configure on this window and of course there are many more to go play around with and we're not going to go dive into every single control and everything that's on the window for properties and things like that I just want to show you that this is sort
of the different pieces that we have to work with here so main window preview up top we have the zaml in the bottom you can go ahead and change things like put I can put Dev leader here right so you can see that automatically the title updates if I go run this and we're going to look at other pieces of this by the way but I just want to show you that if we go run this I don't know if my hot reload is enabled but we might be able to get something like that going on here so maybe I will put my name here you can see that I just went ahead and saved it and it did hot reload the zaml right so I put Nick instead of Dev leader as a title if I go ahead and put that back uh there's
no editing tricks here I'm just alt tabbing in between so my video editor is not trying to make this all get put together I know he could but I'm not trying to trick you right we do have hot reloading with WPF in Visual Studio which is awesome awesome because if you're trying to play around with some designing it makes it a lot easier than having to go play around with it in the preview and then maybe when you go run the application it's behaving a little bit differently than you expect so this is a very nice feature that we have to work with I'll go ahead and stop this though the other thing that we get in WPF uh and if you're familiar with Wind forms before WPF and it's not unique to either of these two Frameworks but we do have the ability to
have drag and drop so my toolbox on the left hand side here I can go ahead and I could go add a label or something onto here so I can go click this label you can see that the zaml is automatically updated right so there's a Content property it added in some horizontal alignment just some basic stuff like I didn't put any of this on here it just did it automatically when I dropped it in right so you can see that it tried to do some left horizontal alignment but vertical alignment to the center now that seems kind of weird because it looks like it's centered overall but that's because there's a margin here now it's truly left aligned I can go put this to the center now too now it's truly in the center right and then we can go put Nick as the
label just as an example right so you do get this visual editor which is very nice the other thing that I want to show you is that it's very common for almost well all of the things that we have that have a zaml suffix so the the file name is zaml there is a code behind C file in the solution explore on the left you'll notice I have main window. zaml Visual Studio kind of does a bit of a a UI trick here for you where it collapses together so main Windows AML if I expand it we have main Windows amo. Cs and I can go to this file if I double click it to open it and you can see that we do have code here right this is the main window class we were looking at but you'll notice that it's partial it's
partial because we're going to have code generation that takes the zaml and goes and creates the other pieces of the code for this main window it's just that you're not seeing it in this file I'm going to go ahead and remove a lot of these extra usings just to make this a little bit more readable but you'll see right we have a window that's the name of our class and it inherits from window so very much like Wind forms and other control libraries this is pretty common thing where we have this hierarchy of controls if you are familiar with things like composition versus inheritance a lot of the time UI Frameworks lean heavily into inheritance not something that I'm a huge fan of but it's just a very common thing that we see with UI Frameworks so main window itself inherits from this window base
class and then you can see that we have this construct on here that has initialized component this is again if you're familiar with Wind forms this is a way that we can basically ask the class to go set up all of the things that we see if we go back over to main Windows AML all the stuff that you're seeing here calling initialized component will essentially get that set up for us at runtime just wanted to call out that a lot of the time you will see a zaml file coupled with a CS file I'm going to go show you one more part before I start talking about some of the things that I like about WPF and maybe some things that I would like to move away from just because they seem like anti patterns but unfortunately that's the way the framework is set
up so you end up sort of fighting against the framework if you're trying to achieve some of these other patterns so I'll get into that in just a moment but before we move on this is just a reminder that I do have courses available on dome train if you want to level up in your C programming if you head over to dome train you can see that I have a course bundle that has my getting started and deep dive courses on C between the two of these that's 11 hours of programming in the C language taking from absolutely no programming experience to being able to build basic applications you'll learn everything about variables Loops a bit of async programming and object-oriented programming as well make sure to check it out if we go over to app xaml.cs and app zaml you can see that app
zaml is again it's just another zaml file right not something that you're going to see and it's kind of interesting that if we go look at app xaml.cs it's very similar to the window one but the difference is that this is not a UI control control it's an application that we're inheriting from if I go into application you can see that if I keep going into here there's nothing along the hierarchy that is like a UI element the app itself in this case our in or our specific type is called app but it inherits from a base application this is not a UI element this is just the entry point to our application and that's the entry point in all WPF applications is going to be application the other part if we go to app. zaml is that you'll notice that we do have a
zaml file associated with it which is kind of weird because I think for a lot of us that are working in WPF or even the first example I showed you where the main window has this visual editor you know and you get the preview you don't get that here for app. zaml you don't have that it's just a markup file that goes along with it as you can see here this is just the default we do have a resource collection that we can go have resources for our application things like strings Styles Graphics stuff like that you can drop into your resources the other thing to pay attention to is that there is a startup URI and the startup URI points at the main window in this case that's going to be the thing that we launch when we go to run this application right
so that's what the startup URI is if we remove that what happens right let's go run this now it's running right you might not know you can't tell but the application is running but it doesn't know what to go do right there isn't a window or anything else to go launch it basically is running the application and it's going okay I I'm doing what you said right the startup URI is telling us that we need to go have this as the control if I go run this again you'll see it fixes it right so it knows go put up this main window okay so we have that we can see that this is a pretty empty thing here if you wanted to do some initialization logic I think a lot of us are well depending on when you started you might be used to something
like a program.cs file and then you would have something like a public class was copilot going to do it for me here you go um you have something like this it might have a static void main uh don't worry about the STA thread for just the point of this conversation you might have something like um string pams here right even have an INT return type but we're used to having something like this as our entry point you're not getting that with WPF this is kind of hidden from us like I was saying some of us depending on when you started in C you might not seen this kind of thing because we do have top level statements now where you can basically just write your code at the top of the application literally return zero or console right line hello world is a you know
a full program on its own but historically we had this kind of thing and all that I'm trying to point out is that we don't have that in WPF it's not explicit but if you wanted to have some initialization logic there's a couple things you could do so the first is on the app in the Constructor we don't need initialized component here but you could do do some initialization if I could type right so that's one place you can do it that's in the Constructor though so you might want to think about what truly belongs to the Constructor and then you have this on started or on Startup sorry so you have this which is almost like an event handler it's not quite if you wanted to do an event handler there is also an event handler that you could do and that didn't go
and make come on Visual Studio there we go thank you so you can do an event handler you can do the override and you can use a Constructor these are all tools that you have to use to do some startup logic they will have nuances some differences between them so something to think about right just wanted to point out that if you're like hey I made an app in WPF where the heck is the entry point how do I add some startup code here you go here's three different variations of how you can do that so not going to go into the the details of those I'm just pointing out some things to you before I show you some things that I would like to move away from I'm going to start by showing you a couple of things that you can leverage in WPF
and I wanted to start by showing you a pattern that's pretty common so depending on what types of UI Frameworks you've used um you might have heard of something called MVC which is model view controller in WPF the pattern that gets used is model view view model the way that we try to approach this kind of stuff in WPF is like instead of hard coding stuff right into the zaml we can actually do what's called binding to a view model what that looks like is that here we could go do binding so you can see the syntax with the curly braces binding and then the thing that we want to go bind to have something like this and you might say well where does that come from or we could do you can see that as I'm typing it the autocomplete is not picking that
up and it's because it doesn't know where that exists and that's because right now it doesn't exist anywhere but where it will be looking for the thing that I'm typing at runtime is off of the data context and this isn't something we've talked about yet the data context is a property that you can assign onto your control so in this case the main window we can go make a data context which is just well I shouldn't say just it's not quite just a data transfer object but a very basic view model in this case would be a data transfer object which is just something that has some properties the one that we're going to make in just a moment is a very basic dto data transfer object so we will go make one that has a custom title and then we'll see that we can
go assign it if you look in my preview right now you'll notice that there is no title there now right because it doesn't know where to pull custom title from it can't show anything what I'm going to do is go over to main window xaml.cs just to go create the view model okay so we have that and I did call it custom title but this isn't enough we've just gone ahead and made a class we're not using it anywhere and that's the part that we have to go do so the data context on a control in this case main window is mutable and it's mutable from the outside it's also mutable from the inside so we can go assign a data context to be like this there's no dependency injection in this case I'm just neing up a new main window view model directly inside
of here again if I go back to the preview you can see hey look nothing fancy going on here we don't have a title but if I go run this you can see that the title is hello world so that binding is taking place the title equals binding custom title it's pulling the custom title property if I go back to here it's pulling that custom title property off of this main window view model which is assigned to the data context to do one more quick example we had this label here I'm going to go do binding custom label and that means we have to go make that property now so we'll put custom label here and I'm going to put some string in here obviously you would all agree with that right Dev leader rocks let's go see hey look boom The Binding takes place
for us so I just wanted to show you that in WPF The Binding part that we have in the zaml the way that it works is that the part that you are binding to is going to be off of the data context now without going into a super deep dive on all of these things cuz there's so much to explore here bindings can get incredibly complicated you can do things like binding elements like in a list control to a list that is in code then you have to figure out how you map those objects to be displayed in the UI there are things where you want to do a binding and you need to convert the value so maybe you have a set of enums or something that's on a property if that enum is set to a particular value you want to be able
to hide or disable something you need to be able to convert that enum value into a visual State and that needs a value converter so there's lots of stuff that you can do with bindings you can do bindings to other control properties instead of just the data Conta so this kind of stuff can get incredibly complicated this is going to lead me to a couple of things that I want to talk about that I'm not a huge fan of in WPF in terms of some of the patterns that it pushes us into the first one is going to be that as awesome as it is to go Define all of this stuff in zaml you're not writing code you're writing in the markup language so it's not like you have to go put all the logic in here right I'm not writing if statements and
loops and things like that for some people it's very nice because you have this markup language to work with you can go make reusable bindings and things like that or the value converters all that stuff and you can have it with your nice preview window but in my opinion one of the biggest challenges is that if we want to go test this kind of stuff the more things that you put into your zaml the harder it becomes to go test what I mean by that is the more stuff that is in your example the more that you need to be able to run your application and have it visually showing you what's happening truly you need to be able to say show a window for the bindings to go take place that means if you wanted to test that your control is in the right
State you need to be able to show it and that means that the types of tests that you can write are much more limited it's not that it's wrong it's not that there's no way to test it it's just that it restricts the type of testing that you are able to do one of the ways that I worked around this historically is that the more things you see inside of zaml the more things I start to pull out so I've gone as far as even having the bindings done in the code behind where we go create a binding object and basically move away from having almost anything in the zaml it does mean that you lose a ton of the benefits but for us in some of the situations we had we wanted to make sure that we could test what the user interface was
doing without having to go launch something have it click through do uh you know you can do like screenshot testing or there's tons of other types of tests you can do but we didn't want to have to go show the user interface to test a bunch of stuff we took that pattern to an extreme I think there's probably a happy middle ground but in my opinion the more stuff that you put into zaml the harder it becomes to test so that's one thing the other thing that I wanted to mention if I go back into the code here it has to do with the data context and view models the thing that I'm not a huge fan of is that the way that WPF guides us into creating user interfaces I feel like it is done from the control itself down to the code behind
and what I mean by that is that when we are working in WPF it's kind of like you are driven by the controls that you're working in versus having some background code or some backend code that knows how to go show the elements properly you can absolutely design it the way that I just said in fact that's the direction I try to go in but it feels like you are fighting against the framework to do it one of the ways that we see this happen is if we look at the Constructor here for main window if you're not familiar with dependency injection I'll kind of explain it as I'm talking about this problem but a lot of the time what I don't want to do is have mutable controls I don't want someone to go create the main window and then go assign the data
context I also don't want to have to go new up my view model inside of here what I would like to be able to do is have a main window view model passed in this is the behavior that I want everyone to have to use and I would love for this data context to basically be a read only so you go assign the data context now it's assigned for the lifetime of this control in my opinion if we enforce this type of rule I think that it would make for a lot more what I might call clean code in WF I don't like saying the word clean because I think that's very subjective and in fact this is just my opinion so it is subjective but the reason that I like doing this is that you can have weird things going on where people reassign
the data context and instead I think the pattern that's more beneficial is that you have if anything a mutable view model so you can do things that will adjust the state of your view model that way you don't have to interact with the control itself for changing any other state explicitly we do that of course through the bindings again if you're able to pass in the view model you can assign it to the data content your data bindings should take care of the rest of the things that you want your view to do with respect to your view model but this pattern isn't really something that you see encouraged in a lot of WPF code a lot of the time you will see things get made and then data context assigned from the outside personal opinion but I think that this would have been a
nice way to kind of enforce that we go create controls with that said I do think that WPF is a very powerful desktop development framework I have used it extensively for many years it's been a few years since I've had to go developing it though but I do think that it's something that if you're interested in making desktop applications WPF is a great option of course there is Maui which I have not spent time with that is crossplatform much newer and it also leverages zaml so I've heard people say that you know if you're familiar with zaml and WPF it can be transferable to Maui but I don't have the experience at working in Maui now if you're interested in seeing how I start to address some of these challenges that I was calling out especially with the main window in the data context and
how we might be able to leverage dependency injection you can go ahead and check out this video next thanks and I'll see you next time
Frequently Asked Questions
What is WPF and how does it differ from WinForms?
WPF, or Windows Presentation Foundation, is a UI framework for building desktop applications on Windows. Unlike WinForms, which is more control-based and relies heavily on the Windows Forms model, WPF uses a markup language called XAML to define UI elements and supports advanced features like data binding, styles, and templates.
What is the purpose of the DataContext in WPF?
The DataContext in WPF is used to establish the data source for data binding. It allows you to bind UI elements to properties in a view model or data object, enabling a clean separation between the UI and the underlying data.
Can I test WPF applications easily, and what challenges might I face?
Testing WPF applications can be challenging due to the heavy reliance on the visual aspects of the UI. The more logic you place in XAML, the harder it becomes to test without running the application. I recommend minimizing the amount of logic in XAML and handling more in the code-behind or view models to facilitate easier testing.
These FAQs were generated by AI from the video transcript.