BrandGhost

How To Build Modular WPF Applications With a Plugin Architecture

WPF is a powerful user interface framework for CSharp, but I hate some of it. Is hate too strong of a word? Probably. But in the many years I spent building desktop applications in WPF, I found that I would be fighting against a lot of the framework to make things happen. However, two things that I don't compromise on are: - Dependency Injection - Plugins So we're going to see how we can build a plugin-based WPF application and set you on course for using a plugin architecture in CSharp!
View Transcript
if you're like me then you love building applications with a plug-in style architecture and if you're not like me then watch this video so hopefully I can convince you otherwise hi my name is Nick centino and I'm a principal software engineering manager at Microsoft in this video we're going to walk through some WPF code and basically work on a previous example where we started using dependency injection for our WPF application if you haven't watched that yet you can go ahead and check that out now and come back and watch this one but we're going to build on top of that and basically take our dependency injection code to will make this work in a plug-in style if you find this kind of content interesting remember to subscribe to the channel and check out that pin comment for my courses on dome Trin with that said let's jump over to visual studio check out this WPF app and see how we can start pulling things apart on my screen right now I have the code where we left off in the previous video and what I'm going to do from here is explain briefly what we have going on and the direction that we're heading in what we can see is that we have the I service collection kind of setup that's very traditional for as. core applications but we're also using that here in this WPF app what we're doing is leveraging the extension method type of syntax that we are using again very popularly in asp.net core so we basically just go register explicitly any dependencies that we're interested in and that way the more different view models Windows controls that you have You' go tack these on you can go split these up into different extension methods to organize them however you'd like personally I'm not a huge fan of this I'm not a huge fan of it because it means that if you're using plugins or if you're continuing to build stuff up you have to keep touching your core application to go know to go add these registrations and in my opinion just not a great feel just a personal opinion though it's not how I like to design things when we're thinking about building plug-in based applications a lot of the time you'll have a core application and you may extend that for different reasons that aren't plug-in based at all and that's totally cool generally I move to a pretty extreme end of the spectr where my core application is extremely lightweight and almost everything else is a plugin you may find a good balance somewhere in the middle so I'm not suggesting you have to follow exactly what I'm saying all of the way I'm just trying to give you some of the tools so that you can start moving in that direction a little bit more if you find it comfortable so what we're looking at in this application is very simple it just has a main window with a view model but what I want to show you is that we can go make a plugin for this instead and it's going to be a very contrived example you might say hey Nick this is totally Overkill and you're absolutely right in this case it's a very simple application to begin with but I want to show you the basis for how you can start to build plugin applications in WPF instead of having the main window defined inside of here instead what we're going to do is make a control library and pull it out into a new project so let's go ahead and do that to start with you can see that I'm making a plug-in one project it's a control Library it's going to be net 8 we'll go add this and there's a whole lot of stuff that we don't need in here to begin with because I am just going to take the main window completely out of the other app so we have that now inside here the uh name space has probably got to be corrected but we're not going to worry about that for now if we go back to here you can see that it's not happy it's not going to compile we're not going to need this anymore so we're going to get rid of the extension methods this is one of the things I said I'm not a huge fan of it I think it's better for COD code organization than just dropping it all in one one spot because you can make different extension methods but I don't like having to do this if I were using autofac my plugins would have their own modules so you get that kind of organization and it's not in the core application that's kind of nice but we're not going to use autofac here we're going to stay with the service collection and instead we're going to use something called screw Tor to basically help us get that assembly scanning functionality that autofac would otherwise give us we don't get to do this part with the configure services and we're going to have to revisit this part here with the main window because this is going to be a bit of a tricky thing that we have to navigate and there's a bunch of different ways that you can go solve it the very next step that we're going to look at is adding screw door before we continue on this is just a quick reminder that I do have courses available on D train if you're just getting started in your programming journey and you want to learn C you can head over to dome train I have a getting started in C course it's approximately 5 hours of content taking you from absolutely no experience to being able to program in C and after that I have my deep dive course which will take you to the next level with another 6 hours of content so that you can start building basic applications head over to D train and check it out let's head back to the video I'm just in the nugat package manager here I've searched for screw door I'm going to go install this you can see it has a ton of downloads it's 128 million at the time of recording very popular package to work with what it's going to enable for us is this right here we can go do scan which is pretty cool co-pilot looks like it's trying to do a whole bunch of stuff that I don't actually want it to do so we're going to skip that I'm going to call it X here give me one sec cuz co-pilot is autocompleting faster than I can type almost something like this we're going to try to polish this up a little bit let me get rid of this massage it a little bit more to kind of get that link like syntax that I think a lot of people love to see it's kind of like a builder style bit of a pipeline kind of effect going on we have this here but the details of what it's doing aren't quite right first of all Assemblies of and then picking where this app is that's only going to look in our current assembly so we do need some assembly scanning code to fix this up I'm going to go borrow this code from another project just to highlight what's going on here this is a very naive way to go load all of the DLS that are in your bin directory as assemblies there are some gas like if you have unmanaged DS or you know you should be checking what you're doing in production not just blindly loading random dlls right so this part we're going to say do from assemblies and give it our collection of assemblies instead this part here I'm not going to be picky I'm just saying go load all of the classes from those assemblies again for your use case you may not want to do this that's totally cool I'm just showing you what I like to do and how you could go do this but it's more important to understand what these things do so this is going to go add all of the types to find in the assemblies that we're loading in which is going to be every dll in the bin directory from there as self with interfaces is going to allow the types themselves to be registered either as their explicit type so if you have a class that's inheriting from an interface you can resolve it as the class itself in this case we're going to be looking at Main window so we could resolve it as main window or any interface that it implements the other thing is that we're saying all of these things are going to be singled you may not want to or you might not be able to go enforce these constraints across every type from every plugin that you want to include so this is something to think about pros and cons to weigh out not telling you it's the right way this is just a way that you can do it from here we're going to get all of these things registered which is pretty awesome that gets us most of the way one problem we have left is getting this main window now we don't know about main window from the core application and that's that's because main window is now this concept it's defined in another plugin the core application has no idea about it what we might want to do is say hey look this is going to depend on your application of course if you pretend there's only ever one window in your app you could say I just want to go resolve window Whoever has that defined that's going to be the only main window and again you're going to want to come up with a better plug-in contract in your situation but I wanted to show you why this doesn't work so if I go run this we would hope that this goes and resolves Main window now the first thing I have to do is go build this thing or else it's really not going to work but we need to go build it and move the dlls into the bin directory there's other things you can do to go automate this kind of thing in this case I'm not showing that and those are going to be uh mostly build system things or if you want to use some tricks and visual studio you can do that too I'm just going and moving these binaries into the bin directory over here but it's still not going to work I just wanted to prove to you that I am taking the necessary steps the reason it doesn't work has to do with the registrations it says no service for Type window has been registered and it's entirely right it's a little bit confusing but this says as self with interfaces if we look at Main window main window is the Type window is not it's a base type and if we go into here we can see that window content control there is an interface right I add child interesting just to prove that this all worked it's just that we have the wrong type I could go ask for this thing I could go into here get me a required service of AD child I'm going to cheat for just a moment I'm going to cast it as a window just because I know confidently that we only have one of these but if we go run this hey look it works so now we have a little bit of a trick or a problem to go solve in my opinion one way that you could do this and there are many one way is to come up with like an SDK and this is something that you could share between your core application and plugins and if you have different types of plugins you might have different sdks that you're using for different plugin implementations I'm going to keep this one really simple we're going to make an SDK that has an imain window it's going to be super simple and it's going to have a simple show method on it and we're going to see how this works I'm going to go add a new project it's just going to be a class Library and I'm going to call it WPF playground. SDK again very very simple comes with one class we're going to get rid of that the theory here is that a plug-in would be able to define a main window for us to use and we can do void show just like this very simple what I'm going to do now is go to our main window if I go add this on I need to be able to go add the dependency on our playground plugin I'm going to go add the SDK as a dependency so now we should be able to see that inside here again when we go to do that registration this type will be registered as main window and everything that it implements as an interface one of which is I main window so far so good let me go rename this file before we move on so it's not so confusing now from here we have to go to the core application that means we want Iain window here it's not going to work for just a moment and that's because because the core application also needs to have a reference to that SDK so the SDK something to think about here is just a shared project between your core application and plugins of a particular type this is how you know how to go have a contract between these two types of things I main window let's go resolve that boom so now we have that here the show method works because it's right on here it's on the interface itself it just so happens that it has the same name as a main window or in this case a window already has a show method we didn't have to go Define it we don't have to go put a show method on here because it already exists let's go back double check what we have going on here I didn't change any of this code and now once we have this entry point set up the theory is any other plugins that we want to go add there's only going to be one main window plugin but if you wanted to go do other things you can have all of that stuff load dynamically the only time it gets weird is if you ever need to reference some particular type from the core application but that's where you might want to consider an SDK or maybe consider that a plugin wasn't the right choice so before we go run this I have to do one more build or else we're going to be in for a rough ride because we changed the code I have to go move those DLS again again if you're looking at this saying hey look it seems like plugins are a total pain in the butt why would you want to go copy this stuff all the time I do have other videos that show you some tricks on this kind of stuff or you would have this built into your build and continuous integration pipeline to basically package up how you want things to work there are different ways to handle it I'm just showing you the really tedious way because I don't have those things set up but we're replacing the dlls in the bin directory now if we go run this we'll see that we have a main window that's defined completely outside of our core application our core application literally has no idea aidea about this implementation and we were able to scan for assemblies in the bin directory from there go load in what we need just based on the interface so that's the the contract that we're working with with plugins and because that one plugin defines an implementation for one of these we automatically had it loaded in the benefit of all of this is that once your entry point is set up in this particular way you hopefully don't have to come back here and do a whole lot this code should basically live like this maybe you want to wrap some logging and stuff around it but ideally as you're building out your app you don't really have to come back here if you thought this was interesting and you want to see how you can do this kind of thing with ASP onc core you can go ahead and check out this video next thanks and I'll see you next time

Frequently Asked Questions

What is the main focus of this video?

In this video, I focus on building modular WPF applications using a plugin architecture. I demonstrate how to leverage dependency injection and assembly scanning to create a flexible and extensible application structure.

Why do you prefer a plugin architecture over traditional methods?

I prefer a plugin architecture because it allows for a lightweight core application where most functionality can be added through plugins. This separation of concerns makes it easier to manage and extend the application without constantly modifying the core code.

What tools or libraries do you recommend for implementing this architecture?

I recommend using the Microsoft.Extensions.DependencyInjection library for dependency injection and Scrutor for assembly scanning. These tools help streamline the process of registering and resolving dependencies in a modular way.

These FAQs were generated by AI from the video transcript.
An error has occurred. This application may no longer respond until reloaded. Reload