Reputation: 1257
I am aware that in order to achieve this, I need to create a custom presenter and map the ViewModel to my Views manually by overriding the InitializeViewLookup
method. I successfully did this on Android and so far it works fine, but I can't seem to do it on iOS. Here's what I tried:
public override void Show(MvxViewModelRequest request)
{
// check if there are any presentation values
if (request.PresentationValues != null)
{
// if yes, check if one of them is SelectedView
if (request.PresentationValues.ContainsKey(SingletonViewModelLocator.SelectedView))
{
UIViewController viewController;
switch (request.PresentationValues[SingletonViewModelLocator.SelectedView])
{
// The ViewModel requested the First View, load that one
case SingletonViewModelLocator.FirstViewValue:
viewController = new FirstView();
MasterNavigationController.PushViewController(viewController, true);
return;
// The ViewModel requested the Second View, load that one
case SingletonViewModelLocator.SecondViewValue:
viewController = new SecondView();
MasterNavigationController.PushViewController(viewController, true);
return;
// wrong view requested
default:
throw (new InvalidEnumArgumentException(request.PresentationValues[SingletonViewModelLocator.SelectedView] +
" does not exist."));
}
}
}
// otherwise run the default method which means 1 ViewModel is mapped to 1 View
base.Show(request);
}
Here's InitializeViewLookup
:
protected override void InitializeViewLookup()
{
var container = Mvx.Resolve<IMvxViewsContainer>();
container.Add(typeof(MainViewModel), typeof(MainView));
// TheViewModel is mapped to two Views
container.Add(typeof(TheViewModel), typeof(FirstView));
container.Add(typeof(TheViewModel), typeof(SecondView));
}
This gives an "Object reference not set to an instance of an object" exception somewhere inside Mvx libraries on the ViewDidLoad
method when the navigation occurs.
Just for reference, here's how I'm doing it on Android which works fine.
public override void Show(MvxViewModelRequest request)
{
// check if there are any presentation values
if (request.PresentationValues != null)
{
// if yes, check if one of them is SelectedView
if (request.PresentationValues.ContainsKey(SingletonViewModelLocator.SelectedView))
{
var activity = Activity;
Intent intent;
switch (request.PresentationValues[SingletonViewModelLocator.SelectedView])
{
// The ViewModel requested the First View, load that one
case SingletonViewModelLocator.FirstViewValue:
intent = new Intent(activity, typeof (FirstView));
Show(intent);
return;
// The ViewModel requested the Second View, load that one
case SingletonViewModelLocator.SecondViewValue:
intent = new Intent(activity, typeof (SecondView));
Show(intent);
return;
// wrong view requested
default:
throw (new InvalidEnumArgumentException(request.PresentationValues[SingletonViewModelLocator.SelectedView] +
" does not exist."));
}
}
}
// otherwise run the default method which means 1 ViewModel is mapped to 1 View
base.Show(request);
}
EDIT
Here's the stack trace:
0x0 in Cirrious.MvvmCross.ViewModels.MvxViewModelLoader.LoadViewModel
0x65 in Cirrious.MvvmCross.Touch.Views.MvxViewControllerExtensionMethods.LoadViewModel
0x13 in Cirrious.MvvmCross.Views.MvxViewExtensionMethods.OnViewCreate
0xE in Cirrious.MvvmCross.Touch.Views.MvxViewControllerExtensionMethods.OnViewCreate
0x7 in Cirrious.MvvmCross.Touch.Views.MvxViewControllerAdapter.HandleViewDidLoadCalled
0xB in Cirrious.CrossCore.Core.MvxDelegateExtensionMethods.Raise
0xD in Cirrious.CrossCore.Touch.Views.MvxEventSourceViewController.ViewDidLoad
0x2 in Demo.iOS.FirstView.ViewDidLoad at c:[path]\View\FirstView.cs:34,-1
0xA6 in UIKit.UIApplication.UIApplicationMain
0xB in UIKit.UIApplication.Main at /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:62,4
0x3B in UIKit.UIApplication.Main at /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:46,4
0x8 in Demo.iOS.Application.Main at c:[path]\Main.cs:17,-1
Upvotes: 1
Views: 1299
Reputation: 66882
The default Container in an Mvx Touch application creates views using code like:
CurrentRequest = request;
var viewType = GetViewType(request.ViewModelType);
if (viewType == null)
throw new MvxException("View Type not found for " + request.ViewModelType);
var view = CreateViewOfType(viewType, request);
view.Request = request;
return view;
Each View
then loads/locates its own ViewModel
using instructions hidden in the Request
.
If the Request
is empty, then Mvx defaults to some heuristics to create a default ViewModel of a type based on a convention using the View
's class name.
In your case, I think the best solution is to set the Request
property on your ViewController - e.g.
case SingletonViewModelLocator.SecondViewValue:
viewController = new SecondView() { Request = request };
MasterNavigationController.PushViewController(viewController, true);
return;
However, you could also do this by providing a hint in SecondView
about the type of ViewModel that is expected... you can do that by overriding the ViewModel
property with a type, by inheriting from MvxViewController<T>
, by providing an attribute hint for the view model type (MvxViewFor
), or by adding some special lookups during Setup
Note - your question also suggests to me that your doing some singleton magic on the ViewModel
lookup side, so you may need to adjust this answer to match whatever that is...
Upvotes: 1