Search code examples
xamarin.androidmvvmcross

How to upgrade MvxFragmentAttribute


I'm updating my project to MvvmCross 6.0 from 4.3

The MvxFragmentAttribute is gone and seems to have been replaced by MvxFragmentPresentationAttribute

However when using the new Attribute I get the following error:

System.NullReferenceException: FrameLayout to show Fragment not found
at MvvmCross.Droid.Support.V7.AppCompat.MvxAppCompatViewPresenter.ShowFragment (System.Type view, MvvmCross.Platforms.Android.Presenters.Attributes.MvxFragmentPresentationAttribute attribute, MvvmCross.ViewModels.MvxViewModelRequest request) [0x00090] in <8141cf24d04c4cc6ba0be8d85f7b3f82>:0 
at MvvmCross.Platforms.Android.Presenters.MvxAndroidViewPresenter.<RegisterAttributeTypes>b__21_2 (System.Type view, MvvmCross.Presenters.Attributes.MvxBasePresentationAttribute attribute, MvvmCross.ViewModels.MvxViewModelRequest request) [0x00000] in <17df0d0bdae848b7a8a12b58d710f763>:0 
at MvvmCross.Platforms.Android.Presenters.MvxAndroidViewPresenter.Show (MvvmCross.ViewModels.MvxViewModelRequest request) [0x00014] in <17df0d0bdae848b7a8a12b58d710f763>:0 
at MvvmCross.Platforms.Android.Views.MvxAndroidViewDispatcher+<>c__DisplayClass2_0.<ShowViewModel>b__0 () [0x00000] in <17df0d0bdae848b7a8a12b58d710f763>:0 
at MvvmCross.Base.MvxMainThreadAsyncDispatcher+<>c__DisplayClass0_0.<ExecuteOnMainThreadAsync>b__0 () [0x00000] in <17df0d0bdae848b7a8a12b58d710f763>:0 
at MvvmCross.Base.MvxMainThreadAsyncDispatcher+<>c__DisplayClass1_0+<<ExecuteOnMainThreadAsync>b__0>d.MoveNext () [0x00011] in <17df0d0bdae848b7a8a12b58d710f763>:0 
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state) [0x00000] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
at Android.App.SyncContext+<>c__DisplayClass2_0.<Post>b__0 () [0x00000] in <263adecfa58f4c449f1ff56156d886fd>:0 
at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in <263adecfa58f4c449f1ff56156d886fd>:0 
at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00008] in <263adecfa58f4c449f1ff56156d886fd>:0 
at (wrapper dynamic-method) System.Object.515beccd-4ae3-42be-a59f-9c8897c1459b(intptr,intptr)

Unfortunately this does not tell me which FrameLayout or even which Fragment and the stack trace contains none of my code.

However only one of the Fragments has been hit before this happens:

Old Attribute usage:

[MvxFragment(typeof(MainViewModel), Resource.Id.main_frame_layout)]

New Attribute usage:

[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_frame_layout)]

And yes main_frame_layout does exist in my main_view layout which is as follows:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/main_draw_layout"
    android:fitsSystemWindows="true">
    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/main_frame_layout"
            android:layout_centerInParent="true" />
    </android.support.design.widget.CoordinatorLayout>
    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="left|start"
        android:id="@+id/main_navigation_layout" />
</android.support.v4.widget.DrawerLayout>

Solution

  • I was calling _navigationService.Navigate from the Start method of my ViewModel to show the fragment. However the lifecycle changed in MvvmCross 5 which is detailed here. The Start method now triggers before the view is shown or at least before it has finished being shown. This means that the Activity exists but the layout hasn't been inflated yet so main_frame_layout doesn't exist yet.

    The fix was to move the Navigate call to the new ViewAppeared method.