xamarin / Xamarin.Forms

Xamarin.Forms is no longer supported. Migrate your apps to .NET MAUI.
https://aka.ms/xamarin-upgrade
Other
5.62k stars 1.87k forks source link

[Enhancement] Dependency Injection support for creating ContentPage in Shell. #8359

Open PhilippCh opened 5 years ago

PhilippCh commented 5 years ago

Summary

Please add the capability to use an IoC container of our choice when creating new pages via (shell) XAML files.

API Changes

Instead of using Activator.CreateInstance() in ElementTemplate, create a way for us to provide our own callback for creation of new instances.

Note: I know this currently already works with Xamarin.Forms by simply creating the page types from the IoC container for example within App for the first page, but Shell doesn't provide a coherent way to integrate an IoC container as of now.

Intended Use Case

This would allow us to use for example an IoC framework of our choice.

marinasundstrom commented 4 years ago

I have been asking for the same thing.

There should be a factory type that instantiates these templates and that can be extended. For instance, to inject a IServiceProvider through the constructor and get the page instance from there.

I also think that Microsoft.Extension.DependencyInjection should be used in all of Xamarin.Forms (in one place) instead of the confusing integration points today.

In that way you could use whatever IoC container you want too.

marinasundstrom commented 4 years ago

I'm curious, has someone already investigated how to implement this without breaking the current integration points?

pstricks-fans commented 4 years ago

@robertsundstrom : Maybe this one is what you are looking for.

jmoralesv commented 4 years ago

I would also like to see Shell supporting the feature of being able to be instantiated using dependency injection. As of now, I have to have my content pages with parameterless constructors, and having a static reference to my IServiceProvider to resolve the page's dependencies. It works but doesn't follow the DI pattern used in the rest of classes in my Xamarin.Forms project.

Hopefully Shell allows us to instantiate it using DI soon.

PureWeen commented 4 years ago

We have some discussions around this going on

I also think that Microsoft.Extension.DependencyInjection should be used in all of Xamarin.Forms (in one place) instead of the confusing integration points today.

would be my preference.

If we add our own DI then that's just going to scope creep into a lot of work maintaining our own IoC framework

The current DependencyResolver is fairly specific in its intent

So the question becomes if there's enough value add to make Microsoft.Extension.DependencyInjection default? or do we just build a separate nuget you can use that to plugin with?

marinasundstrom commented 4 years ago

I sometimes prefer to pass models and services into the constructors of Pages.

I prefer that to the ViewModelLocator pattern since you can wire up instances on the fly from code without depending on a single class that also grows in size.

Second thought, it is kind of weird aesthetically to instantiate a XAML element by injecting an object into a constructor.

Property injection seems OK.

DI seems to suit Blazor better.

diegosasw commented 4 years ago

Firstly, you guys are far ahead of me here as I'm quite new to Xamarin. After working with master view for some days I've just discovered Xamarin Shell today and was doing a quick app to play with the navigation.

You've mentioned there is already a "way" to achieve a view model first navigation with shell (and maybe use a DI container to instantiate the view models) and you've mentioned ElementTemplate for that. Would you mind sharing a link or sample where this is being used?

I don't mind if I have to manually use the DI container to instantiate view models prior to binding them at some "central point" (AppShell.xaml.cs ?), but I'm keen on seeing what's the best approach as per today. Thanks

payoff commented 3 years ago

If you create ShellItems with code, and the class comes from IoC you can add you service provider and call: ` new ShellContent { ContentTemplate = new DataTemplate(()=> _provider.GetService(menu.Type)) }

`

marinasundstrom commented 3 years ago

@payoff And when I think about it, you could create a markup extension that wraps this functionality in a generic way.

<ShellContent ContentTemplate="{local:DIDataTemplate Type={x:Type MyPage}}" />

It would require you to either assign a static IServiceProvider to the Markup Extension, or have a standard Service Locator.

public class App : Application
{
    public App() 
    {
        DIDataTemplate.AppServiceProvider = // your service provider;
    }
}
public class DIDataTemplate : IMarkupExtension<DataTemplate>
{
    public static IServiceProvider AppServiceProvider { set; get; }

    public Type Type { set; get; }

    public DataTemplate ProvideValue(IServiceProvider serviceProvider)
    {
        return new DataTemplate(()=> AppServiceProvider.GetService(Type))
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
    {
        return (this as IMarkupExtension<DataTemplate>).ProvideValue(serviceProvider);
    }
}
brunck commented 2 years ago

This will find its way into MAUI, but won't be showing up in Xamarin.Forms. See https://github.com/xamarin/Xamarin.Forms/issues/10804#issuecomment-981494921