Forum Xamarin.Forms

How to Xamarin.Forms Dependency Injection with dynamic constructor parameter?

Happy new year to everyone!

My question concerns how to use DI in a Xamarin.Forms View to resolve a ViewModel where the ViewModel needs a dynamic (different every time) constructor parameter which parameter needs to be constructed inside the View. If it is feasible, I would like to use ASP NET Core DI. I have read James Montemagno's post about DI, but I dont think it contains the answer to my question.

Here is my code in its current, non-DI form:
We are inside the MovieDetailPage() constructor and the Page's constructor creates its own ViewModel and assigns it to its binding context like this:

    public MovieDetailPage(MovieDetailModel movie)
    {
        ViewModel = new MovieDetailPageViewModel(
            movie,
            ((App)Application.Current).Settings,
            ((App)Application.Current).TmdbApiService,
            ((App)Application.Current).UsersMovieListsService2,
            ((App)Application.Current).MovieDetailModelConfigurator,
            ((App)Application.Current).PersonDetailModelConfigurator,
            ((App)Application.Current).VideoService,
            ((App)Application.Current).WeblinkComposer,
            **new PageService(this)**
            );

        InitializeComponent();
    }

The page extracts the dependencies from the Application by accessing the properties in ((App)Application.Current).. These dependencies have a lifetime equal to the application and do not change. My understanding is, that we could easily resolve them via DI if we register them with Singleton lifetime.

My problem is the object which the Page constructs via new PageService(this) . PageService basically provides means for the ViewModel to access some Xamarin.Forms navigation and DisplayAlert() functions by exposing some of the API of Navigation and Page through an IPageService interface.

For this the PageService object needs to be constructed dynamically with the current pages reference.

What options do I have to use Dependency Injection for resolving the ViewModel from the View's constructor in a way so that I can provide a valid PageService object. For better understanding I have copied some code from IPageService-PageService below.

====================================================================

public interface IPageService
{
    ..
Task DisplayAlert(string title, string message, string cancel);
    ...
    Task OpenWeblink(string url);
}

public class PageService : IPageService
{
    private readonly Page _currentPage;

    public PageService(Page current) => _currentPage = current;
    ...

    public async Task DisplayAlert(string title, string message, string cancel) =>
        await _currentPage.DisplayAlert(title, message, cancel);
    ...

    public async Task OpenWeblink(string url)
    {
        try
        {
            await Browser.OpenAsync(url, BrowserLaunchMode.SystemPreferred);
        }
        catch (Exception ex)
        {
            await _currentPage.DisplayAlert("Error", $"Could not open weblink: {ex.Message}", "Ok");
        }
    }
}

Best Answer

  • janosjungjanosjung Member ✭✭
    Accepted Answer

    Ok I found a solution by using Autofac.

    Dependency configuration is straightforward and below is an example how the caller can provide dependencies to the DI-container to be used when resolving a type:

    MovieDetailPage() constructor wants to resolve a MovieDetailPageViewModel object which will acts as its ViewModel:

    the ViewModel gets resolved by Resolve(....) and the parameters to the call represent dependencies from outside the container provided by the caller,

        public MovieDetailPage(MovieDetailModel movie)
        {
            using (var scope = DependencyResolver.Container.BeginLifetimeScope())
            {
                ViewModel = scope.Resolve<MovieDetailPageViewModel>( 
                        new TypedParameter(typeof(MovieDetailModel), movie),
                        new TypedParameter(typeof(IPageService), new PageService(this))
                    );
            }
    
            InitializeComponent();
        }
    

Answers

  • LeonLuLeonLu Member, Xamarin Team Xamurai

    Do you want to achieve the DI like these with constructor parameter?

     public class ProfileViewModel : ViewModelBase  
    {  
      private IPageService  _pageService;  
    
      public ProfileViewModel(IPageService pageService)  
     {  
        _pageService= _pageService;  
      }  
    
    }
    

    Here is a article about DI in xamarin forms. You can refer to it.
    https://docs.microsoft.com/en-us/xamarin/xamarin-forms/enterprise-application-patterns/dependency-injection

  • janosjungjanosjung Member ✭✭
    edited January 2020

    Hi LeonLu!

    I want to resolve the a MovieDetailPageViewModel object by DI from the View's constructor by starting dependency resolution the following way (dont look the syntax only the intention):

    ServiceProvider.GetService<MovieDetailPageViewModel>(movie, new PageService(this))

    So the DI framework would look at the ViewModel's constructor and see the following dependencies:

        public MovieDetailPageViewModel(
            MovieDetailModel movie,
            ISettings settings,
            ITmdbApiService tmdbApiService,
            UsersMovieListsService2 movieListsService2,
            IMovieDetailModelConfigurator movieDetailModelConfigurator,
            IPersonDetailModelConfigurator personDetailModelConfigurator,
            IVideoService videoService,
            IWeblinkComposer weblinkComposer,
            IPageService pageService)
    

    The dependencies:

    • ISettings,
    • ITmdbApiService,
    • UsersMovieListsService2,
    • IMovieDetailModelConfigurator,
    • IPersonDetailModelConfigurator,
    • IVideoService and
    • IWeblinkComposer
      are registered at the DI container at startup time, so the container resolves them. (The View should not provide them) These dependencies are service objects.

    There are two additional dependencies:

    • MovieDetailModel and
    • IPageService

    IPageService is a context-like dependency as it will encapsulate the reference of the current Xamarin Page. (And provides access to navigation and popupalerting for the ViewModel). PageService needs to be created by the View and handed over to the DI-resolution as parameter.

    MovieDetailModel is a filled in proper 'Model' object which contains the data the ViewModel operates on. The View sholud pass in the Model object as parameter to the resolver, so that the resolver can use the Model object when the ViewModel is created.

  • janosjungjanosjung Member ✭✭
    Accepted Answer

    Ok I found a solution by using Autofac.

    Dependency configuration is straightforward and below is an example how the caller can provide dependencies to the DI-container to be used when resolving a type:

    MovieDetailPage() constructor wants to resolve a MovieDetailPageViewModel object which will acts as its ViewModel:

    the ViewModel gets resolved by Resolve(....) and the parameters to the call represent dependencies from outside the container provided by the caller,

        public MovieDetailPage(MovieDetailModel movie)
        {
            using (var scope = DependencyResolver.Container.BeginLifetimeScope())
            {
                ViewModel = scope.Resolve<MovieDetailPageViewModel>( 
                        new TypedParameter(typeof(MovieDetailModel), movie),
                        new TypedParameter(typeof(IPageService), new PageService(this))
                    );
            }
    
            InitializeComponent();
        }
    
Sign In or Register to comment.