Question

I have been using MvvmCross on a cross platform mobile project and have 2 different views in a MonoTouch project that are using the same shared viewmodel and not sure how to go about structuring my code to navigate to different views using the same viewmodel in MvvmCross.

Was it helpful?

Solution

The default convention used by the MvvmCross platform is to automatically register all views using reflection.

This is done in the base Setup class - in https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross/Platform/MvxBaseSetup.cs:

    protected virtual void InitializeViews()
    {
        var container = this.GetService<IMvxViewsContainer>();

        foreach (var pair in GetViewModelViewLookup())
        {
            Add(container, pair.Key, pair.Value);
        }
    }

where GetViewModelViewLookup returns a dictionary of ViewModel type to View type:

    protected virtual IDictionary<Type, Type> GetViewModelViewLookup(Assembly assembly, Type expectedInterfaceType)
    {
        var views = from type in assembly.GetTypes()
                    let viewModelType = GetViewModelTypeMappingIfPresent(type, expectedInterfaceType)
                    where viewModelType != null
                    select new { type, viewModelType };

        return views.ToDictionary(x => x.viewModelType, x => x.type);
    }

In universal iPad/iPhone apps you do occasionally want to include multiple views for each viewmodel - using one view in the iPad and one view in the iPhone.

To do this, there are now (literally just now!) some attributes available to mark your views as being "unconventional" - these are:

The last of these is probably what you want in this case - you could implement simple iPhone/iPad switching for a MainViewModel using two views declared like:

[MvxFormFactorSpecificView(MvxTouchFormFactor.Phone)]
public class MyIPhoneView : BaseView<MainViewModel>
{
    // iphone specific view ...
}

[MvxFormFactorSpecificView(MvxTouchFormFactor.Pad)]
public class MyIPadView : BaseView<MainViewModel>
{
    // ipad specific view ...
}

Alternatively if you want a very custom configuration, you can override all 'convention-based' behaviour - you can implement your own override of GetViewModelViewLookup - e.g.:

protected override IDictionary<Type, Type> GetViewModelViewLookup(Assembly assembly, Type expectedInterfaceType)
{
    if (IsIPad)
    {
        return new Dictionary<Type, Type>() 
        {
            { typeof(HomeViewModel), typeof(IPadHomeView) },
            { typeof(DetailViewModel), typeof(IPadDetailView) },
            { typeof(AboutViewModel), typeof(SharedAboutView) },
        };
    }
    else
    {
        return new Dictionary<Type, Type>() 
        {
            { typeof(HomeViewModel), typeof(IPhoneHomeView) },
            { typeof(DetailViewModel), typeof(IPhoneDetailView) },
            { typeof(AboutViewModel), typeof(SharedAboutView) },
        };
    }
}

Note that eventually you may decide that you need additional ViewModels as well as Views for the iPad app - the iPad has, after all, a much bigger screen - in this case you can add them manually. Ultimately, when your app hits a few million users, you may even decide to completely branch the tablet code away from the phone code - but that can generally wait until you hit that few million mark...

OTHER TIPS

Another way to do it is to go ahead and create 2 ViewModels, but have them both subclass an abstract ViewModel, as follows:

FirstViewViewModel : BaseViewModel
SecondViewViewModel : BaseViewModel

With the corresponding views named:

FirstView.xaml
SecondView.xaml

This way, you are able to place some shared behavior in BaseViewModel, while the 2 subclasses are really just there to satisfy MvvmCross' view fetching conventions.

I recently started with MvvmCross and I am using v4.2.1. It seems that some names have changed. I am using one ViewModel with seperate iPhone and iPad views with the following:

[MvxFormFactorSpecific(MvxIosFormFactor.Phone)]
public class MyIPhoneView : BaseView<MainViewModel>
{
    // iphone specific view ...
}

[MvxFormFactorSpecific(MvxIosFormFactor.TallPhone)]
public class MyTallIPhoneView : BaseView<MainViewModel>
{
    // tall iphone specific view ...
}

[MvxFormFactorSpecific(MvxIosFormFactor.Pad)]
public class MyIPadView : BaseView<MainViewModel>
{
    // ipad specific view ...
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top