Search code examples
androidmvvmcross

Mvvmcross: cannot create preference activity


I am working on a Xamarin project that runs with MvvmCross. I am trying to add a preference view inside Android but I cannot get it to work.

When I try to navigate to the MvxPreferenceFragment, I get the following errors:

[0:] mvx:Diagnostic: 12.11 Activity host with ViewModelType PreferenceTest.Droid.Activities.MainPreferenceActivity is not CurrentTopActivity. Showing Activity before showing Fragment for PreferenceTest.Core.ViewModels.Fragments.PreferenceViewModel
An unhandled exception occured.

09-26 13:54:45.753 I/MonoDroid(12779): UNHANDLED EXCEPTION:
09-26 13:54:45.810 I/MonoDroid(12779): System.Collections.Generic.KeyNotFoundException: Could not find view for PreferenceTest.Droid.Activities.MainPreferenceActivity
09-26 13:54:45.810 I/MonoDroid(12779):   at MvvmCross.Core.Views.MvxViewsContainer.GetViewType (System.Type viewModelType) [0x0006a] in C:\projects\mvvmcross\MvvmCross\Core\Core\Views\MvxViewsContainer.cs:72 
09-26 13:54:45.810 I/MonoDroid(12779):   at MvvmCross.Droid.Support.V7.AppCompat.MvxAppCompatViewPresenter.ShowHostActivity (MvvmCross.Droid.Views.Attributes.MvxFragmentPresentationAttribute attribute) [0x00000] in C:\projects\mvvmcross\MvvmCross-AndroidSupport\MvvmCross.Droid.Support.V7.AppCompat\MvxAppCompatViewPresenter.cs:166 
09-26 13:54:45.810 I/MonoDroid(12779):   at MvvmCross.Droid.Support.V7.AppCompat.MvxAppCompatViewPresenter.ShowFragment (System.Type view, MvvmCross.Droid.Views.Attributes.MvxFragmentPresentationAttribute attribute, MvvmCross.Core.ViewModels.MvxViewModelRequest request) [0x0007d] in C:\projects\mvvmcross\MvvmCross-AndroidSupport\MvvmCross.Droid.Support.V7.AppCompat\MvxAppCompatViewPresenter.cs:196 
09-26 13:54:45.810 I/MonoDroid(12779):   at MvvmCross.Droid.Views.MvxAndroidViewPresenter.<RegisterAttributeTypes>b__39_2 (System.Type view, MvvmCross.Core.Views.MvxBasePresentationAttribute attribute, MvvmCross.Core.ViewModels.MvxViewModelRequest request) [0x00000] in C:\projects\mvvmcross\MvvmCross\Droid\Droid\Views\MvxAndroidViewPresenter.cs:188 
09-26 13:54:45.810 I/MonoDroid(12779):   at MvvmCross.Droid.Views.MvxAndroidViewPresenter.Show (MvvmCross.Core.ViewModels.MvxViewModelRequest request) [0x00055] in C:\projects\mvvmcross\MvvmCross\Droid\Droid\Views\MvxAndroidViewPresenter.cs:295 
09-26 13:54:45.810 I/MonoDroid(12779):   at MvvmCross.Droid.Views.MvxAndroidViewDispatcher+<>c__DisplayClass2_0.<ShowViewModel>b__0 () [0x00000] in C:\projects\mvvmcross\MvvmCross\Droid\Droid\Views\MvxAndroidViewDispatcher.cs:26 
09-26 13:54:45.810 I/MonoDroid(12779):   at MvvmCross.Droid.Views.MvxAndroidMainThreadDispatcher.RequestMainThreadAction (System.Action action, System.Boolean maskExceptions) [0x00020] in C:\projects\mvvmcross\MvvmCross\Droid\Droid\Views\MvxAndroidMainThreadDispatcher.cs:20 
09-26 13:54:45.811 I/MonoDroid(12779):   at MvvmCross.Droid.Views.MvxAndroidViewDispatcher.ShowViewModel (MvvmCross.Core.ViewModels.MvxViewModelRequest request) [0x00014] in C:\projects\mvvmcross\MvvmCross\Droid\Droid\Views\MvxAndroidViewDispatcher.cs:26 
09-26 13:54:45.811 I/MonoDroid(12779):   at MvvmCross.Core.Navigation.MvxNavigationService+<Navigate>d__32.MoveNext () [0x0003a] in C:\projects\mvvmcross\MvvmCross\Core\Core\Navigation\MvxNavigationService.cs:179 
09-26 13:54:45.811 I/MonoDroid(12779): --- End of stack trace from previous location where exception was thrown ---
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult () [0x00000] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at MvvmCross.Core.Navigation.MvxNavigationService+<Navigate>d__40.MoveNext () [0x00078] in C:\projects\mvvmcross\MvvmCross\Core\Core\Navigation\MvxNavigationService.cs:293 
09-26 13:54:45.811 I/MonoDroid(12779): --- End of stack trace from previous location where exception was thrown ---
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.CompilerServices.TaskAwaiter.GetResult () [0x00000] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at PreferenceTest.Core.ViewModels.FirstViewModel+<ExecutePreferences>d__8.MoveNext () [0x00026] in C:\git\PreferenceTest\PreferenceTest\PreferenceTest\PreferenceTest.Core\ViewModels\FirstViewModel.cs:28 
09-26 13:54:45.811 I/MonoDroid(12779): --- End of stack trace from previous location where exception was thrown ---
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state) [0x00000] in <896ad1d315ca4ba7b117efb8dacaedcf>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at Android.App.SyncContext+<>c__DisplayClass2_0.<Post>b__0 () [0x00000] in <2e14bb2dd93a405e81838369ed72695b>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in <2e14bb2dd93a405e81838369ed72695b>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00008] in <2e14bb2dd93a405e81838369ed72695b>:0 
09-26 13:54:45.811 I/MonoDroid(12779):   at (wrapper dynamic-method) System.Object:a278159c-f25c-4da4-ac29-d4bae0d2de73 (intptr,intptr)

This is the code I am trying to run (for the full code please refer to the github link below): First I created a view inside the xml folder (named preference.xml)

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
  <EditTextPreference android:title="Your Name"
                      android:key="username"
                      android:summary="Please provide your username"></EditTextPreference>
  <CheckBoxPreference android:title="Application Updates"
                      android:defaultValue="false"
                      android:summary="This option if selected will allow the application to check for latest versions."
                      android:key="applicationUpdates" />
  <ListPreference     android:title="Download Details"
                      android:summary="Select the kind of data that you would like to download"
                      android:key="downloadType"
                      android:defaultValue="1"
                      android:entries="@array/listArray"
                      android:entryValues="@array/listValues" />
  <CheckBoxPreference
    android:key="prefer wifi"
    android:title="Prefer WiFi" />
</PreferenceScreen>

Also the fragment code:

[MvxFragmentPresentation(typeof(MainPreferenceActivity), Resource.Id.content_frame, true)]
[Register(nameof(PreferenceFragment))]
public class PreferenceFragment : MvxPreferenceFragment<PreferenceViewModel>
{
    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        base.OnCreateView(inflater, container, savedInstanceState);
        var view = this.BindingInflate(Resource.Xml.preferences, container, false);
        return view;
    }
}

The call is made with the navigation service:

private async void ExecutePreferences()
{
    await _navigationService.Navigate<PreferenceViewModel>();
}

I cannot find what is wrong with this code :).

Here is also a github link to the sample.


Solution

  • The first issue is with the use of the MvxFragmentPresentation the first parameter is the type of the ViewModel being used for the host activity and not the type of the activity itself. Which in your case would be MainPreferenceViewModel.

    The next issue is that as you are using the MvxAppCompatViewPresenter presenter and so you will need to use MvxPreferenceFragmentCompat. MvxPreferenceFragmentCompat can be found in the MvvmCross.Droid.Support.V7.Preference NuGet package.

    [MvxFragmentPresentation(typeof(MainPreferenceViewModel), Resource.Id.content_frame)]
    [Register(nameof(PreferenceFragment))]
    public class PreferenceFragment : MvxPreferenceFragmentCompat<PreferenceViewModel>
    {
        public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
        {
            SetPreferencesFromResource(Resource.Xml.preferences, rootKey);
        }
    }
    

    Lastly you would need to specify a theme for the preferences in your activity

    <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
    

    I have gone with a material design theme from the Xamarin.Android.Support.v14.Preference but you could have also used one of the other ones:

    • @style/PreferenceThemeOverlay
    • @style/PreferenceThemeOverlay.v14

    See pull request for full details of changes.

    MvxPreferenceFragmentCompat example