单个 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;
每个 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
查找方面做了一些单例魔术,所以你可能需要调整这个答案以匹配任何东西......
我知道,为了实现这一点,我需要创建一个自定义演示者并通过覆盖 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;
每个 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
查找方面做了一些单例魔术,所以你可能需要调整这个答案以匹配任何东西......