Clean way to Injecting dependency to wpf behavior
Asked Answered
D

1

5

I need to create custom WPF Behavior with some dependencies injected in custructor, I can't use constructor injection because I need to apply that in XAML, WPF creates instance with default constructor. One way I can do is use Container in default constructor or service locator but I don't want to those approaches. I can use [Improt] on property and use Container.SatisfyImportOnce but not really neat way as I will still be using container in constructor. Any suggestion?

I am using MEF for dependency injection in my WPF project.

Neat way: It should be testable, I should be able to inject dependencies And should be instantiated in WPF XAML.

Dhow answered 13/10, 2015 at 8:13 Comment(4)
Not specific enough. I believe you need to define the meaning of "neat way" in your definitionHousley
I think I understand what is your problem. Although it might be possible to do it like what they say in the DI books but I recommend not to overcomplicate things just because it is more 'neat'. There are situations and APIs where you cannot simply build up object graphs as you could with your own classes (ctor injection). At the end of the day much of these hacky DI workarounds are just for the sake of 'proper' DI. But DI is not a goal but a tool.Mullane
It is indeed part of view, because we are using workspaces managed by workspace manager and keeping track of windows, we need manager to register each windows to Workspace manager. Also at one place I needed to save size of widget to my application setting and I wanted to use same behaviour at multiple places. User might come and say that we want our UI setting to be persisted in database, I might need your suggestion here.Dhow
I suspect there's a problem with your overall architecture design. Behaviours are part of the view and in MVVM are used to assist the view in displaying non-application-specific data. Having your behaviours use application-specific data structures is essentially the same thing as code-behind, and usually a good indication that your view model isn't doing its job properly. A good example of this is in unit testing: DI makes it possible to do it in your VMs but it's wasted effort if you're just going to use it in a behaviour that's required by your view!Carnahan
O
8

As you already know you cannot do constructor injection with Wpf but you can have property injection defined in the Xaml. What I consider to be a "neat" solution is to use a MarkupExtension such as this:

public sealed class Resolver : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var provideValueTarget = (IProvideValueTarget)serviceProvider
            .GetService(typeof(IProvideValueTarget));

        // Find the type of the property we are resolving
        var targetProperty = provideValueTarget.TargetProperty as PropertyInfo;

        if (targetProperty == null)
        {
            throw new InvalidProgramException();
        }

        // Find the implementation of the type in the container
        return BootStrapper.Container.Resolve(targetProperty.PropertyType);
    }
}

And with a behaviour such as this:

public sealed class InitialiseWebBrowser : Behavior<MyVM>
{
    public IQueryHandler<Query.Html, string> HtmlQueryHandler { private get; set; }

    // ...
}

We can configure the property injection in the Xaml like so:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
    xmlns:inf="clr-namespace:abc"
    xmlns:behaviours="clr-namespace:def"
    x:Class="MyVM">
    <i:Interaction.Behaviors>
        <behaviours:InitialiseWebBrowser HtmlQueryHandler="{inf:Resolver}"/>
    </i:Interaction.Behaviors>

The Resolver MarkupExtension will:

  • analyse the type of the property it is defined against (in the example HtmlQueryHandler is an IQueryHandler<Query.Html, string>)
  • resolve the type from whatever underlying Container/Resolver you are using
  • inject it into the behaviour.
Omnirange answered 13/10, 2015 at 21:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.