Search code examples
xamarinmvvmcross

CustomFragment in MVVMCross


I have used the following template in my Android application which has navigation drawer has a list of options such as Settings.

https://github.com/MvvmCross/MvvmCross-Samples/tree/master/XPlatformMenus

Source code could be downloaded from the following url https://github.com/MvvmCross/MvvmCross-Samples

I wonder how could I able to make Settings page as a Dialog or CustomFragment which will look like similar to following image.

enter image description here


Solution

  • One approach that you could make use of is to create a custom implementation of Dialog. Following the XPlatformMenus sample you linked to, you could implement something as follows:

    Generic Custom Dialog

    This class inherits android Dialog control, and can be used with any XML/AXML layout you want. You could tightly couple it to a particular ViewModel/Layout or you can make it handle a generic ViewModel type. Here is an example of the generic type:

    public class CustomDialog : Dialog, IMvxBindingContextOwner
    {
        public CustomDialog(Context context, int layout, IMvxViewModel viewModel)
            : this(context, Resource.Style.CustomDialog)
        {
            this.BindingContext = new MvxAndroidBindingContext(context, (context as IMvxLayoutInflaterHolder));
            ViewModel = viewModel;
            Init(layout);
        }
    
        public CustomDialog(Context context, int themeResId)
            : base(context, themeResId)
        {
        }
    
        protected CustomDialog(IntPtr javaReference, JniHandleOwnership transfer)
            : base(javaReference, transfer)
        {
        }
    
        protected CustomDialog(Context context, bool cancelable, IDialogInterfaceOnCancelListener cancelListener)
            : base(context, cancelable, cancelListener)
        {
        }
    
        protected CustomDialog(Context context, bool cancelable, EventHandler cancelHandler)
            : base(context, cancelable, cancelHandler)
        {
        }
    
        private void Init(int layout)
        {
            SetContentView(layout);
        }
    
        public override void SetContentView(int layoutResID)
        {
            var view = this.BindingInflate(layoutResID, null);
            base.SetContentView(view);
        }
    
        public IMvxBindingContext BindingContext { get; set; }
    
        public object DataContext
        {
            get { return this.BindingContext.DataContext; }
            set { this.BindingContext.DataContext = value; }
        }
    
        public IMvxViewModel ViewModel
        {
            get { return this.DataContext as IMvxViewModel; }
            set { this.DataContext = value; }
        }
    }
    

    XML layout for modal:

    <?xml version="1.0" encoding="utf-8" ?>
    <RelativeLayout 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="wrap_content"
        android:background="@color/colorPrimary">
    
        <Button
            android:id="@+id/btn_option"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Show"
            local:MvxBind="Click ShowSettingsCommand"/>
    
        <Button
            android:id="@+id/btn_close"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/btn_option"
            android:text="CLOSE"
            local:MvxBind="Click ShowCloseCommand"/>
    </RelativeLayout>
    

    CustomDialog style:

    <resources>
      <style name="CustomDialog">
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowNoTitle">true</item>
      </style>
    </resources>
    

    Custom Presenter

    Create a custom presenter to handle the navigation to show/hide the dialog:

    public class CustomPresenter : MvxFragmentsPresenter
    {
        protected IMvxViewModelLoader MvxViewModelLoader => Mvx.Resolve<IMvxViewModelLoader>();
    
        CustomDialog _modal;
    
        public CustomPresenter(IEnumerable<Assembly> AndroidViewAssemblies) : base(AndroidViewAssemblies)
        {
        }
    
        protected override void ShowActivity(MvxViewModelRequest request, MvxViewModelRequest fragmentRequest = null)
        {
            if (!Intercept(request))
                base.ShowActivity(request, fragmentRequest);
        }
    
        protected override void ShowFragment(MvxViewModelRequest request)
        {
            if (!Intercept(request))
                base.ShowFragment(request);
        }
    
        private bool Intercept(MvxViewModelRequest request)
        {
            if (request.ViewModelType == typeof(ThirdViewModel))
            {
                var activity = Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity;
                var viewModel = MvxViewModelLoader.LoadViewModel(request, null) as ThirdViewModel;
                _modal = new CustomDialog(activity, Resource.Layout.modal_popup, viewModel);
                _modal.Show();
                return true;
            }
    
            if (_modal != null)
            {
                _modal.Dismiss();
                _modal = null;
            }
            return false;
        }
    }
    

    Register your custom presenter in the setup class:

    protected override IMvxAndroidViewPresenter CreateViewPresenter()
    {
        var mvxFragmentsPresenter = new CustomPresenter(AndroidViewAssemblies);
        Mvx.RegisterSingleton<IMvxAndroidViewPresenter>(mvxFragmentsPresenter);
        return mvxFragmentsPresenter;
    }
    

    ViewModel

    public class ThirdViewModel : BaseViewModel
    {
        private MvxCommand _showSettingsCommand;
        public MvxCommand ShowSettingsCommand =>
            _showSettingsCommand ?? (_showSettingsCommand = new MvxCommand(() => ShowViewModel<HomeViewModel>()));
    
        private MvxCommand _showCloseCommand;
        public MvxCommand ShowCloseCommand =>
            _showCloseCommand ?? (_showCloseCommand = new MvxCommand(() => ShowViewModel<SettingsViewModel>()));
    }