Is Dependency Injection possible with a WPF application?

前端 未结 8 999
忘了有多久
忘了有多久 2021-01-30 21:05

I want to start using dependency injection in my WPF application, largely for better unit testability. My app is mostly constructed along the M-V-VM pattern. I\'m looking at Aut

8条回答
  •  情歌与酒
    2021-01-30 21:51

    Glen Block (see above) mentions that a common approach is to design your MVVM solution to use the DataContext as the place where you can "resolve" your View Model in the View. Then you can use design extensions from expression blend 2008 (note that you don't need to be using the expression blend design tools to take advantage of this). For example:

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    mc:Ignorable="d" 
    d:DataContext="{d:DesignInstance Type=local:MyViewModelMock, IsDesignTimeCreatable=True}"
    

    In your view you can have a property getter that casts your DataContext to the type that you expect (just to make it easier to consume in the code-behind).

    private IMyViewModel ViewModel { get { return (IMyViewModel) DataContext; } }
    

    Don't forget to use an interface so that your views are easier to test, or to help you inject different runtime implementations.

    In general, you should not be resolving things from the container all over the place in your solution. It is actually considered bad practice to pass your container around in every constructor, or to make it globally accessible. (You should look up discussions of why "Service Locator" strategies constitute an "Anti-Pattern").

    Create a public View constructor with explicit dependencies that the container (e.g. Prism Unity or MEF) can resolve.

    If necessary, you could also create an internal default constructor to create a mock of your view model (or a real one for that matter). This protects against inadvertent use of this "design constructor" externally (in your "Shell" or wherever). Your test projects can also use such constructors using the "InternalsVisibleToAttribute" in "AssemblyInfo". But of course, that usually isn't necessary since you can inject your mocks using the full dependency constructors anyway, and because the majority of your tests should be focusing on the ViewModel in the first place. Any code in the View should ideally be quite trivial. (If your View requires a lot of testing, then you might want to ask yourself why!)

    Glen also mentions that you can inject Views into View Models, or View Models into Views. I much prefer the latter because there are perfectly good techniques for decoupling everything (use of Declarative Binding, Commanding, Event Aggregation, Mediator patterns, etc.). The View Model is where all the heavy lifting will be done to orchestrate core business logic. If all of the necessary "binding" points are provided by the View Model, it really shouldn't need to know ANYTHING about the View (which can mostly be wired up to it declaratively in the XAML).

    If we make the View Model agnostic to the source of user-interaction, that makes it much easier to test (preferably first). And it also means that you can easily plug in ANY view (WPF, Silverlight, ASP.NET, Console, etc.). In fact, to ensure that appropriate decoupling has been achieved, we can ask ourselves if a "MVM" (Model-ViewModel) architecture could work in the context of, say, a Workflow service. When you stop to think about it, most of your unit tests will probably be designed on that premise.

提交回复
热议问题