单个 ViewModel 的多个视图 (iOS)

Multiple Views for single ViewModel (iOS)

我知道,为了实现这一点,我需要创建一个自定义演示者并通过覆盖 InitializeViewLookup 方法手动将 ViewModel 映射到我的视图。我在 Android 上成功地做到了这一点,到目前为止它工作正常,但我似乎无法在 iOS 上做到这一点。这是我的尝试:

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);
    }

这里是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));
    }

当导航发生时,这会在 ViewDidLoad 方法的 Mvx 库中某处给出一个 "Object reference not set to an instance of an object" 异常。

仅供参考,以下是我在 Android 上的做法,效果很好。

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);
    }

编辑

这是堆栈跟踪:

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

Mvx Touch 应用程序中的默认容器使用如下代码创建视图:

            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;

来自 https://github.com/MvvmCross/MvvmCross/blob/1eeea41e110934e52bf7e6682d8751b885206844/Cirrious/Cirrious.MvvmCross.Touch/Views/MvxTouchViewsContainer.cs#L31

每个 View 然后 loads/locates 它自己的 ViewModel 使用隐藏在 Request.

中的指令

如果 Request 为空,则 Mvx 默认使用一些试探法来创建默认的 ViewModel,其类型基于使用 View 的 class 名称的约定。


对于您的情况,我认为最好的解决方案是在您的 ViewController 上设置 Request 属性 - 例如

                case SingletonViewModelLocator.SecondViewValue:
                    viewController = new SecondView() { Request = request };
                    MasterNavigationController.PushViewController(viewController, true);
                    return;

但是,您也可以通过在 SecondView 中提供有关预期的 ViewModel 类型的提示来做到这一点...您可以通过覆盖 ViewModel 属性使用类型,通过从 MvxViewController<T> 继承,通过为视图模型类型 (MvxViewFor) 提供属性提示,或者通过在 Setup

期间添加一些特殊查找

注意 - 你的问题还向我暗示你在 ViewModel 查找方面做了一些单例魔术,所以你可能需要调整这个答案以匹配任何东西......