I seem to be confused at how to make MvvmCross detect my ViewModels that are associated with views in Mono for Android. I tried to follow TwitterSearch in implementing the navigation but it just won't work.
RequestNavigate<LoginViewModel>();
here's my view:
[Activity(Label = "Login")]
public class LoginActivity : MvxBindingActivityView<LoginViewModel>
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
App.InitializeRestConnection(this);
}
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.LoginBindable);
}
}
here's the exception:
I/ActivityManager( 307): Displayed com.desco.pendulum/pendulum.androidapp.SplashScreenActivity: +1s373ms (total +4s420ms)
I/Navigation( 8643): 0.50 Navigate to LoginViewModel with args
I/mono-stdout( 8643): Navigation:Diagnostic: 0.50 Navigate to LoginViewModel with args
I/MonoDroid( 8643): UNHANDLED EXCEPTION: System.Collections.Generic.KeyNotFoundException: Could not find view for dESCO.Mobile.ViewModels.LoginViewModel
I/MonoDroid( 8643): at Cirrious.MvvmCross.Views.MvxViewsContainer.GetViewType (System.Type) <0x001a4>
I/MonoDroid( 8643): at Cirrious.MvvmCross.Droid.Views.MvxAndroidViewsContainer.GetIntentFor (Cirrious.MvvmCross.Views.MvxShowViewModelRequest) <0x00023>
I/MonoDroid( 8643): at Cirrious.MvvmCross.Droid.Views.MvxAndroidViewPresenter.Show (Cirrious.MvvmCross.Views.MvxShowViewModelRequest) <0x00037>
I/MonoDroid( 8643): at Cirrious.MvvmCross.Droid.Views.MvxAndroidViewDispatcher/<>c__DisplayClass1.<RequestNavigate>b__0 () <0x0002f>
I/MonoDroid( 8643): at Java.Lang.Thread/RunnableImplementor.Run () <0x0003f>
I/MonoDroid( 8643): at Java.Lang.IRunnableInvoker.n_Run (intptr,intptr) <0x00037>
I/MonoDroid( 8643): at (wrapper dynamic-method) object.7060b187-418d-4bca-ad4f-8b9cae936501 (intptr,intptr) <0x0003b>
any suggestions?
I can't see anything obviously wrong with your setup.
The default Droid framework setup looks for classes which implement IMvxAndroidView in the ExecutingAssembly using the logic in: https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross/Platform/MvxBaseSetup.cs#L200
So I can't see any obvious reason why your LoginActivity View isn't being picked up.
One quick thing you could do is to add some debug code to your Setup.cs to see what views are being detected:
protected override IDictionary<Type, Type> GetViewModelViewLookup()
{
var toReturn = base.GetViewModelViewLookup();
MvxTrace.Trace("Tracing viewModels and views");
foreach (var kvp in toReturn)
{
MvxTrace.Trace("Seen pair {0} : {1}", kvp.Key.Name, kvp.Value.Name);
}
return toReturn;
}
If that shows the list is empty or missing your activity, then I'm afraid we'd need to step through the reflection logic to find out why it's not picking up your Activity.
In real desperation, you could also fake the results:
protected override IDictionary<Type, Type> GetViewModelViewLookup()
{
return new Dictionary<Type,Type>() { { typeof(LoginViewModel), typeof(LoginActivity) } };
}
But this probably isn't the right long-term solution for you
If this is caused by some kind of 'solution setup' difference - e.g. if you have somehow put your Setup.cs in a different project to your views - then it may be that somehow overriding the default convention will help you.
To achieve this, in you Setup.cs, override the code:
public virtual Assembly ExecutableAssembly
{
get { return GetType().Assembly; }
}
protected override IDictionary<System.Type, System.Type> GetViewModelViewLookup()
{
return GetViewModelViewLookup(ExecutableAssembly, typeof (IMvxAndroidView));
}
in the base setup class - https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Droid/Platform/MvxBaseAndroidSetup.cs
This can be done either by overriding the ExecutingAssembly property (although this seems a bit wrong given it's name) or by overriding the GetViewModelViewLookup method. For example, if you wanted to look in several separate assemblies you could use:
public List<Assembly> MyViewAssemblies { get; set; }
protected override IDictionary<System.Type, System.Type> GetViewModelViewLookup()
{
var toReturn = new Dictionary<Type, Type>();
foreach (var assembly in MyViewAssemblies)
{
var contributions = base.GetViewModelViewLookup(assembly, typeof (IMvxAndroidView));
foreach (var kvp in contributions)
{
toReturn[kvp.Key] = kvp.Value;
}
}
return toReturn;
}