Constructor in ViewModel

13,481

One of the unusual (opinionated) features of MvvmCross is that by default it uses ViewModel constructor parameters as part of the navigation mechanism.

This is explained with an example in my answer to Passing on variables from ViewModel to another View (MVVMCross)

The basic idea is that when a HomeViewModel requests a navigation using:

private void DoSearch()
{
    RequestNavigate<TwitterViewModel>(new { searchTerm = SearchText });
}

then this will cause a TwitterViewModel to be constructed with the searchTerm passed into the constructor:

public TwitterViewModel(string searchTerm)
{
    StartSearch(searchTerm);
}

At present, this means that every ViewModel must have a public constructor which has either no parameters or which has only string parameters.

So the reason your ViewModel isn't loading is because the MvxDefaultViewModelLocator can't find a suitable constructor for your ViewModel.


For "services", the MvvmCross framework does provide a simple ioc container which can be most easily accessed using the GetService<IServiceType>() extension methods. For example, in the Twitter sample one of the ViewModel contains:

public class TwitterViewModel
    : MvxViewModel
    , IMvxServiceConsumer<ITwitterSearchProvider>
{
    public TwitterViewModel(string searchTerm)
    {
        StartSearch(searchTerm);
    }

    private ITwitterSearchProvider TwitterSearchProvider
    {
        get { return this.GetService<ITwitterSearchProvider>(); }
    }

    private void StartSearch(string searchTerm)
    {
        if (IsSearching)
            return;

        IsSearching = true;
        TwitterSearchProvider.StartAsyncSearch(searchTerm, Success, Error);
    }

    // ...
}

Similarly, you can see how the conference service data is consumed in the Conference BaseViewModel


If your preference is to use some other IoC container or some other construction mechanism for your ViewModels, then you can override the ViewModel construction within MvvmCross.

Take a look at this question (and answers) for ideas on how to do this - How to replace MvxDefaultViewModelLocator in MVVMCross application

e.g. if you want to, then it should be fairly easy for you to adjust the MyViewModelLocator example in that question to construct your ViewModel with your service.

Share:
13,481
Martin
Author by

Martin

Updated on June 28, 2022

Comments

  • Martin
    Martin almost 2 years

    Is it possible to have a constructor in the ViewModel, which initializes the data service?

    My data service is accessing the web-service of the data storage in a manner similar to this:

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Collections.ObjectModel;
        using Cirrious.MvvmCross.ViewModels;
        using Cirrious.MvvmCross.Commands;
        using MobSales.Logic.DataService;
        using MobSales.Logic.Base;
        using MobSales.Logic.Model;
    
        namespace MobSales.Logic.ViewModels
        {
            public class CustomersViewModel:MvxViewModel
            {
                ICustomerService custService;
            public CustomersViewModel(ICustomerService custService)
            {
                this.custService = custService;
                if (custService != null)
                {
                    custService.LoadCustomerCompleted += new EventHandler<CustomerLoadedEventArgs>(custService_LoadCustomerCompleted);
                }
                loadCustomerCommand = new MvxRelayCommand(LoadCustomer);
                loadCustomerCommand.Execute();
            }
    
    
        private ObservableCollection<Customer> customers;
    
        public ObservableCollection<Customer> Customers
        {
            get { return customers; }
            set
            {
                customers = value;
                FirePropertyChanged("Customers");
            }
        }
    
    
        private CustomerViewModel customer;
    
        public CustomerViewModel Customer
        {
            get { return customer; }
            set
            {
                customer = value;
                FirePropertyChanged("Customer");
            }
        }
    
    
        private MvxRelayCommand loadCustomerCommand;
    
        public MvxRelayCommand LoadCustomerCommand
        {
            get { return loadCustomerCommand; }
        }
    
        public void LoadCustomer()
        {
            custService.LoadCustomer();
        }
    
        void custService_LoadCustomerCompleted(object sender, CustomerLoadedEventArgs e)
        {
            if (e.Error != null)
            {
                return;
            }
    
            List<Customer> loadedCustomers = new List<Customer>();
            foreach (var cust in e.Customers)
            {
                loadedCustomers.Add(new Customer(cust));
            }
    
            Customers = new ObservableCollection<Customer>(loadedCustomers);
        }
    
    }
    

    I am getting an exception but can only see the following partial description:

    Cirrious.MvvmCross.Exceptions.MvxException: Failed to load ViewModel for type MobSales.Logic.ViewModels.CustomersViewModel from locator MvxDefau…

    The binding from View to ViewModel is realized as I've shown in this post: MVVMCross Bindings in Android

    Thanks!