AVOID This Anti-Pattern... But Here's How It Can Help in WPF
Here's an anti-pattern you've been told to avoid -- and for good reason.
Except... I'm going to show you how it can SAVE YOUR BUTT in WPF.
You're going to have to understand WHY it's not great so you can later try to solve this problem in a better way.
In my opinion, WPF doesn't do a great job with dependency injection. In fact, many of the patterns in WPF make it feel like a battle against DI frameworks.
In this video, I'll show you how we can use the Service Locator anti-pattern to work around this when building value converters. I'll explain why I don't like it, how it will help, and then follow up with what we can do better.
View Transcript
I've spent years building WPF applications and I always felt like I was fighting against the framework in this particular situation hi my name is Nick centino and I'm a principal software engineering manager at Microsoft when I'm building things in WPF I always want to be able to take advantage of dependency injection because that's a common pattern I use everywhere else in my C development but I seem to notice that in a lot of places in WPF and especially value converters which we're going to look at today I'm always fighting against the framework to make dependency injection work now you might be building value converters that never need dependencies passed in however if you do need dependencies passed in you'll quickly find that WPF doesn't really like to support this out of the box so in this video I'm going to walk you through what's going
on with that and one solution that we can look at to make that better total disclaimer this is not my preferred way to do it but I want to walk you through it because it can be a quick and dirty way for you to get this working but if you stay to the end I'll show you another way that it's a little bit more advanced that I prefer 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 train that said let's go over to visual studio and see what's going on when we want to pass independencies to Value converters in a previous video that I made related to this topic we were building a WPF app and we needed to put in a value converter that we built ourself now
you can see on my screen right here I have a label that has a value converter called NYX cool converter and if we go look at this thing we can see if I jump over to it that I have Nick's cool converter here and there's no dependencies that we want to pass in we just don't need them now if I want to go change that so let's say I want to get rid of this converter we're going to go look at a very similar one but it's going to take in a dependency this is going to be a very contrived example and if you were to go look at this and then the solution that follows you're going to say Nick this is totally Overkill but I need to give you contrived examples and that way you can see how it might be relevant to
your current situation if I over complicate the example Le you might not even see the relevancy or how you can apply it so I want to make it contrived so that you can understand it and reapply the techniques we're about to look at in this case here I have NYX cool converter it takes in this string formatting helper which is very basic just has this silly little method format double on it and you'll notice I am using a primary Constructor here you don't have to do that you can use whatever you want but we want to pass in a dependency through the Constructor and we're going to find out very quickly that this is very limiting for us so when I go to run the WPF app we're going to notice that it's not going to work if we go a little bit further I
just want to show you that this converter that we have is very similar to the one that was above that I deleted but we're going to see that we don't have a convert back method like the previous one this is a one-way converter so we just don't have to worry about that for this example and then this value converter again the contents that you see here not super important like I do recommend being a little bit more defensive and not just blindly casting things but you'll see that I'm just setting it up so that we can call our dependency which is called string formatting Helper and we can call this method on it so it's just setting us up so that we need something passed into the converter in order for it to get used now one more quick check if we go back to
the zaml again we can see that we're leveraging it here and we're going to try and get that converter from the resources on the window so let's go run this and see what happens right when we try to start up we do get an exception when we try to go initialize this and the dialogue is totally hidden from my screen but I'm pretty sure it throws a null ref exception when we try to do initialized component the reason that it's doing that is because it can't make the value converter and I want to go back here for a second because if I stop this thing doesn't work right if I go back to the zaml you'll notice that we have a bit of a squiggly line going on here on line 13 so why is that there right if I hover over it it says
the type NYX cool converter does not include any accessible Constructors very interesting right let's just rebuild this to make sure that it took effect and again if I go jump into this head over here we have this Constructor is it complaining because it's a primary Constructor well let's get rid of that maybe that'll make some people happy too because they don't really seem to like them so let's go put this string formatting helper here so I'll go add that and then I'm going to go make the Constructor I'm just using the visual studio refactoring menus here so generate a Constructor now we have it passed in normal Constructor is that going to make this thing happy let's rebuild it again just to double check sometimes the source generation gets a little bit out of sync but no it still doesn't have it it's saying that
it can't work because it doesn't have parameterless Constructor that's what we need to have when we're using converters coming out of these resource dictionaries so we have a bit of a problem if we want to use the converter this way it means that we can't have a Constructor with parameters we could go solve this in a different way but if you want to stick to the syntax that we have here how can we go make sure that we have dependency injection work and not have to worry about this before we move on this is just a reminder that I do have courses available on D train if you want to level up in your C programming if you head over to D 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 you 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 so to go back and look at some of the code we have going on in this application because the context is going to be important here in this application I do have dependency injections set up with I service collection just to show you what's going on if I go into the application and we start from the beginning I have some assembly scanning because this is technically a plug-in based application but the point that you really want to focus on right here is that I am using I service collection and screw tour together to go scan assemblies I'm
going to add in all the classes register them as themselves and the interfaces they Implement and then they're all Singleton so that's what this part does but then I'm basically just building the service provider from the service collection that means as soon as we go resolve the main window this is the single entry point that will start making dependency injection happen for us if you're not familiar with this it's very similar it's literally the exact same concept to what we use in as speed on at core web apps same thing I'm just leveraging it in WPF so we get all of that dependency injection magic that means the main window is resolved the way that we want so that's why you can see here I'm passing in the view model this is also passed in through dependency injection so that works nicely for us but
the problem is this stuff doesn't work because it needs a parameterless Constructor so going back to the converter We have basically the way that I want to show you how to solve this like I said at the beginning of this video is not my preferred way but it can be a nice shortcut for you we are going to use an anti- pattern and it's called the service locator pattern I don't like this because it violates a couple of design and coding principles that I'm just really not in favor of but if you're in a pinch and this kind of thing works for you and you understand the drawbacks then I think that you can apply it I would just suggest that if you understand the drawbacks and they're not going to last long for you you probably want to code your way out of this
situation sooner rather than later to explain what we're going to do is I still want to have this dependency it's just that we can't inject it through the Constructor we need to get it a different way we're going to use a service locator and this is the part that I really don't like and we're going to see another part I really don't like in just a moment so I'm going to start with an internal access modifier that's going to scope it so that we only can think about this thing within our current project I did mention that this is plug-in based so we do want to see if we can contain it all within the plugin that we're working so I'm going to have internal and then we need the service provider so I service provider and it's going to be getting set but the
other thing is that it's got to be static if I could spell it properly the static part is the thing I really don't like and the fact that this is just a mutable property that we have this is a service provider it is static and what we could do to make this a little bit more clean I suppose is we can have the service provider like that we could do a null or argument null exception and we could do a throw if null on the service provider so we could do that we can't have this Constructor with any parameters that's the limitation we're working around here but we can use the service provider as a service locator this is the anti pattern we're talking about and I'll explain why it's an anti pattern in just a moment we want to get required service and I
realize that we need to get a name space there we go by the way if you are building in WPF and using I service collection just to jump over here you will need to get this nougat package it is a Microsoft one this functionality all comes in asp.net core but we are in WPF so you're going to want to include this so that you can get dependency injection working properly here we go we're able to resolve this service off of the service provider but this is an anti- pattern and it's an anti- pattern because you have access to the entire service collection you can ask for anything you want usually when we think about depy injection we set things up so that we can pass dependencies into them right they flow one way and that's into the things that we're building this is the opposite
we're building something and then it's reaching out and grabbing the stuff that it needs it's literally the complete opposite direction so we don't want to do that in general but this can work and in situations where this might be helpful is if you have maybe a one-off converter right if you're building 10 converters more more and you're going to have to keep propagating this pattern that we really don't like to see here it's going to mean it's going to keep spreading that crappiness throughout your code and if you want to undo it and do something better later you can't really do that the other thing is that when it comes to testability I generally find that as soon as you start working with static properties and you have to mutate the state of them this gets to be really nasty because it's not something that
you reset very easily and that's because it is static something I generally shy away from but this isn't quite enough to get us working here and that's because because if I go run this thing now actually let's check over here if I go build we should hopefully see this problem is supposed to go away except it's a different one right it says value cannot be null for the provider not the same problem but this isn't going to work anyway and I just wanted to demonstrate to you when we go run it so if I press play it's going to be a new problem and that's because we never set this anywhere right this thing that this little guard that we put in place here is protecting us it's saying hey look like you're about to throw a null reference exception on this line and have
to go debug it but we're explicitly putting a guard in here saying hey look you got to go do something and you could add a nice little message onto this thing like hey by the way you know put a little fix me like we're using a service locator pattern here like we don't recommend it blah blah blah here's why we need it and here's what you have to do like you could help the person who's messing this up by calling it and setting the service provider again not a fan of this but where do we go to fix this now well there is one spot that is using dependency injection happens to be the entry point of the most of the application and it's right here on the main window so what we can do is say I service provider and we can ask for
that and pass it in and that way what we do right at the beginning is we go say on NYX cool converter we can set the service provider to be equal to this and if we do that rate at the beginning that means when we go to create that value converter inside here we should be good to go that's because this is already set up if you switch the order of these things it won't happen at the right time time and again I want you to think about why this is kind of crappy and that's because if you have more and more converters and you have to keep doing this thing you're going to have a big list of converters you have to go set this service provider on to do a service locator pattern it's just a bit of a mess but like I
said if code works and you're happy with it and you understand the tradeoffs who am I to say to not do this right I just want you to think through it if we go run this now it should hopefully work as you can see we get our form popping up so we have 4 2 then 1 3 4 after the decimal just to double check that that's doing the right thing if we go into here we can see that we have string formatting helper format double three is the number of decimal places if we scroll up here right this format string is what we're getting there and the value we passed in was 421 337 so to quickly show you that again you can see that this did round up from 1337 after the decimal to 134 what we saw in this so far is
that we can use a service locator pattern it does mean you need a public static property in this case it was inter internal no one outside of our assembly can go messing around with this but it allowed us to work around that requirement that we can't have parameters on the Constructor this is a common limitation in a lot of zaml stuff that we'll happen to see as we work with WPF and this is one way that you can work around it not a huge fan I am a bigger fan of this other way that we can go do this and if you want to check that out you can see this video next when that video is uploaded thanks and I'll see you next time
Frequently Asked Questions
What is the main issue with using dependency injection in WPF value converters?
The main issue is that WPF value converters require a parameterless constructor, which makes it difficult to use dependency injection directly. This limitation forces developers to find workarounds to pass dependencies into the converters.
What is the service locator pattern, and why is it considered an anti-pattern?
The service locator pattern allows you to retrieve dependencies from a central registry instead of passing them through constructors. It's considered an anti-pattern because it breaks the principle of dependency injection, making the code harder to test and maintain, as it creates hidden dependencies.
What alternative solution does the video suggest for handling dependencies in WPF value converters?
While I demonstrate the service locator pattern as a quick workaround, I also mention that there is a more advanced solution that I prefer. This alternative approach, which I plan to cover in a future video, would allow for better dependency management without relying on the drawbacks of the service locator pattern.
These FAQs were generated by AI from the video transcript.