microsoft / xaml-standard

XAML Standard : a set of principles that drive XAML dialect alignment
Other
803 stars 50 forks source link

Support Dependency Injection #114

Open MovGP0 opened 7 years ago

MovGP0 commented 7 years ago

It should be possible to do dependency injection on XAML controls. The DI mechanism should be registered on a per-assembly mechanism and may use the DI interfaces from ASP.NET Core. The DI should be overrideable in consuming assemblies for testing purposes.

h82258652 commented 7 years ago

That's cool. with #95 we can inject controls by interface. But what's the xaml looks like? like this?

<Grid>
    <DIContainer ResolveType="{x:Type IButton}"></DIContainer>
</Grid>
MovGP0 commented 7 years ago

@h82258652 that is not DI, but the Service Locator Antipattern. I was talking about DI.

So basically:

public IMyService MyService { get; }
public Func<IAnotherService> AnotherServiceFactory { get; }

public MyControl(IMyService myService, Func<IAnotherService> anotherServiceFactory) : UserControl 
{
    MyService = myService;
    AnotherServiceFactory  = anotherServiceFactory;
}
h82258652 commented 7 years ago

@MovGP0 Oh, you mean DI in cs code. (what I though is DI in xaml code.) But DI in control constructor, the designer will be very complex. And if we have two or more constructors like your code, which one will be call?

MovGP0 commented 7 years ago

There is the possibility to provide a default constructor for the Designer. This is how it worked in WinForms.

public partial class MyControl : UserControl 
{

public IMyService MyService { get; }
public Func<IAnotherService> AnotherServiceFactory { get; }

public MyControl(IMyService myService, Func<IAnotherService> anotherServiceFactory)  
{
    MyService = myService;
    AnotherServiceFactory  = anotherServiceFactory;
}

// called by the designer
private MyControl()
{
    MyService = Mock.Create<IMyService>();
    AnotherServiceFactory = () => default(AnotherService);
}

// ... 
}
lokitoth commented 7 years ago

Hard-coding your mocks in your control's code seems to be a bit of an anti-pattern as well. Moreover, this seems like an implementation detail of a platform supporting XAML Standard, rather than a function of the XAML standard itself.

Even with that said, this looks a bit backwards to me. Shouldn't this kind of code live inside a ViewModel class that the control binds to?

MovGP0 commented 7 years ago

@lokitoth indeed. but then the ViewModel requires the DI.

What you currently end up with is to use DI on the VM and use a resource locator on the DataContext property of the XAML control. (That is unless you know a better method)

My intention is to get rid of the ressource locator altogether and inject the ViewModel into the constructor of the control.