c# - 从 View 模型导航到mvvmcross中的另一个 View 模型时崩溃

标签 c# xamarin mvvm xamarin.ios mvvmcross

我想使用mvvmcross中的viewmodels从一个屏幕移动到下一个屏幕,并将一些模型传递给下一个ViewModel。但是我正在崩溃以下:

MvvmCross.Platform.Exceptions.MvxException: Failed to construct and initialize ViewModel for type iManage.ViewModels.LoginViewModel from locator MvxDefaultViewModelLocator - check InnerException for more information ---> MvvmCross.Platform.Exceptions.MvxException: Problem creating viewModel of type LoginViewModel ---> MvvmCross.Platform.Exceptions.MvxIoCResolveException: Failed to resolve parameter for parameter item of type SchoolModel when creating iManage.ViewModels.LoginViewModel at MvvmCross.Platform.IoC.MvxSimpleIoCContainer.GetIoCParameterValues (System.Type type, System.Reflection.ConstructorInfo firstConstructor) [0x00066] in <6adc0d5857264558a9d45778a78ae02a>:0 at MvvmCross.Platform.IoC.MvxSimpleIoCContainer.IoCConstruct (System.Type type) [0x0002c] in <6adc0d5857264558a9d45778a78ae02a>:0 at MvvmCross.Platform.Mvx.IocConstruct (System.Type t) [0x00006] in <6adc0d5857264558a9d45778a78ae02a>:0 at MvvmCross.Core.ViewModels.MvxDefaultViewModelLocator.Load (System.Type viewModelType, MvvmCross.Core.ViewModels.IMvxBundle parameterValues, MvvmCross.Core.ViewModels.IMvxBundle savedState) [0x00000] in :0 --- End of inner exception stack trace --- at MvvmCross.Core.ViewModels.MvxDefaultViewModelLocator.Load (System.Type viewModelType, MvvmCross.Core.ViewModels.IMvxBundle parameterValues, MvvmCross.Core.ViewModels.IMvxBundle savedState) [0x00029] in :0 at MvvmCross.Core.ViewModels.MvxViewModelLoader.LoadViewModel (MvvmCross.Core.ViewModels.MvxViewModelRequest request, MvvmCross.Core.ViewModels.IMvxBundle savedState) [0x00035] in :0 --- End of inner exception stack trace --- at MvvmCross.Core.ViewModels.MvxViewModelLoader.LoadViewModel (MvvmCross.Core.ViewModels.MvxViewModelRequest request, MvvmCross.Core.ViewModels.IMvxBundle savedState) [0x00068] in :0 at MvvmCross.iOS.Views.MvxViewControllerExtensionMethods.LoadViewModel (MvvmCross.iOS.Views.IMvxIosView iosView) [0x0005f] in <6f99728979034e579bc72f6d53e5bc35>:0 at MvvmCross.Core.Views.MvxViewExtensionMethods.OnViewCreate (MvvmCross.Core.Views.IMvxView view, System.Func`1[TResult] viewModelLoader) [0x00012] in :0 at MvvmCross.iOS.Views.MvxViewControllerExtensionMethods.OnViewCreate (MvvmCross.iOS.Views.IMvxIosView iosView) [0x00001] in <6f99728979034e579bc72f6d53e5bc35>:0 at MvvmCross.iOS.Views.MvxViewControllerAdapter.HandleViewDidLoadCalled (System.Object sender, System.EventArgs e) [0x00007] in <6f99728979034e579bc72f6d53e5bc35>:0 at at (wrapper delegate-invoke) :invoke_void_object_EventArgs (object,System.EventArgs) at MvvmCross.Platform.Core.MvxDelegateExtensionMethods.Raise (System.EventHandler eventHandler, System.Object sender) [0x00003] in <6adc0d5857264558a9d45778a78ae02a>:0 at MvvmCross.Platform.iOS.Views.MvxEventSourceViewController.ViewDidLoad () [0x00006] in <4467c42ffcc4478e847227b8e4af47fe>:0 at MvvmCross.iOS.Views.MvxViewController.ViewDidLoad () [0x00001] in <6f99728979034e579bc72f6d53e5bc35>:0 at iManage.iOS.Views.LoginView.ViewDidLoad () [0x00001] in /Users/pankajsachdeva/Projects/iManage/iOS/Views/LoginView.cs:18 at at (wrapper managed-to-native) UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr) at UIKit.UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate) [0x00005] in /Users/builder/data/lanes/5665/f70a1348/source/xamarin-macios/src/UIKit/UIApplication.cs:79 at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00038] in /Users/builder/data/lanes/5665/f70a1348/source/xamarin-macios/src/UIKit/UIApplication.cs:63 at iManage.iOS.Application.Main (System.String[] args) [0x00001] in /Users/pankajsachdeva/Projects/iManage/iOS/Main.cs:17



从ViewModel代码:
public class SchoolSelectionViewModel : BaseViewModel
{
    private readonly ISchoolNames _schoolService;
    public SchoolSelectionViewModel(ISchoolNames schoolService)
    {
        _schoolService = schoolService;
    }
    public override void Start()
    {
        IsLoading = true;
        _schoolService.GetFeedItems(OnDilbertItems, OnError);
    }

    private void OnDilbertItems(List<SchoolModel> list)
    {
        IsLoading = false;
        Items = list;
    }

    private void OnError(Exception error)
    {
        // not reported for now
        IsLoading = false;
    }

    private List<SchoolModel> _items = new List<SchoolModel>();
    public List<SchoolModel> Items
    {
        get { return _items; }
        set { _items = value; RaisePropertyChanged(() => Items); }
    }
    private MvxCommand<SchoolModel> _itemSelectedCommand;
    public ICommand ItemSelectedCommand
    {
        get
        {
            _itemSelectedCommand = _itemSelectedCommand ?? new MvvmCross.Core.ViewModels.MvxCommand<SchoolModel>(DoSelectItem);
            return _itemSelectedCommand;
        }
    }

    private void DoSelectItem(SchoolModel item)
    {
        //ShowViewModel<LoginViewModel>(item);
        ShowViewModel<LoginViewModel>(new LoginViewModel(item));
    }
}

要查看模型代码:
public class LoginViewModel : BaseViewModel
{
    private readonly ILoginService _loginService;

    private readonly IDialogService _dialogService;

    public LoginViewModel(SchoolModel item)
    {
        //_loginService = loginService;
        //_dialogService = dialogService;
        School = item;
        Username = "TestUser";
        Password = "YouCantSeeMe";
        IsLoading = false;
    }

    private SchoolModel _school;
    public SchoolModel School
    {
        get
        {
            return _school;
        }

        set
        {
            SetProperty(ref _school, value);
            RaisePropertyChanged(() => School);
        }
    }

    private string _username;
    public string Username
    {
        get
        {
            return _username;
        }

        set
        {
            SetProperty(ref _username, value);
            RaisePropertyChanged(() => Username);
        }
    }

    private string _password;
    public string Password
    {
        get
        {
            return _password;
        }

        set
        {
            SetProperty(ref _password, value);
            RaisePropertyChanged(() => Password);
        }
    }

    private IMvxCommand _loginCommand;
    public virtual IMvxCommand LoginCommand
    {
        get
        {
            _loginCommand = _loginCommand ?? new MvxCommand(AttemptLogin, CanExecuteLogin);
            return _loginCommand;
        }
    }

    private void AttemptLogin()
    {
        if (_loginService.Login(Username, Password))
        {
            ShowViewModel<DashboardStdViewModel>();
        }
        else
        {
            _dialogService.Alert("We were unable to log you in!", "Login Failed", "OK");
        }
    }

    private bool CanExecuteLogin()
    {
        return (!string.IsNullOrEmpty(Username) || !string.IsNullOrWhiteSpace(Username))
               && (!string.IsNullOrEmpty(Password) || !string.IsNullOrWhiteSpace(Password));
    }
}

编辑1:
在我从ViewModel修改以下内容:
        private async void DoSelectItem(SchoolModel item)
    {
        await _navigationService.Navigate<LoginViewModel,SchoolModel>(item);
    }

更改了下一个ViewModel声明,如下所示:
public class LoginViewModel : MvxViewModel<SchoolModel>

当我尝试显示带有以下错误的下一个viewmodel时,它仍然崩溃:
你调用的对象是空的。
编辑2:
完成错误:

System.NullReferenceException: Object reference not set to an instance of an object at iManage.ViewModels.SchoolSelectionViewModel+d__19.MoveNext () [0x0000f] in /Users/pankajsachdeva/Projects/iManage/iManage/ViewModels/SchoolSelectionViewModel.cs:67 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:152 at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.b__6_0 (System.Object state) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1018 at UIKit.UIKitSynchronizationContext+c__AnonStorey0.<>m__0 () [0x00000] in /Users/builder/data/lanes/5665/f70a1348/source/xamarin-macios/src/UIKit/UIKitSynchronizationContext.cs:24 at Foundation.NSAsyncActionDispatcher.Apply () [0x00000] in /Users/builder/data/lanes/5665/f70a1348/source/xamarin-macios/src/Foundation/NSAction.cs:163 at at (wrapper managed-to-native) UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr) at UIKit.UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate) [0x00005] in /Users/builder/data/lanes/5665/f70a1348/source/xamarin-macios/src/UIKit/UIApplication.cs:79 at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00038] in /Users/builder/data/lanes/5665/f70a1348/source/xamarin-macios/src/UIKit/UIApplication.cs:63 at iManage.iOS.Application.Main (System.String[] args) [0x00001] in /Users/pankajsachdeva/Projects/iManage/iOS/Main.cs:17

最佳答案

MvvmCross.Platform.Exceptions.MvxIoCResolveException: Failed to resolve parameter for parameter item of type SchoolModel when creating iManage.ViewModels.LoginViewModel at MvvmCross.Platform.IoC.MvxSimpleIoCContainer.GetIoCParameterValues (System.Type type, System.Reflection.ConstructorInfo firstConstructor)



问题在于SchoolModel的构造函数中使用的参数LoginViewModel的类型。尝试构造MvxSimpleIoCContainer时,LoginViewModel无法解析类型。

所以我要谈两点
  • 通过查看MvvmCross如何构造 View 模型,为什么会遇到此异常。
  • 如何在MvvmCross 5生命周期中使用navigation service introduced in MvvmCross 5和传统ShowViewModel在 View 模型之间传递参数。


  • 在MvvmCross中查看模型IoC使用情况

    MvvmCross view models are constructed via IoC中。这意味着 View 模型类的公共(public)构造函数的参数需要向IoC容器注册,以便容器构造该类。

    用作构造参数的类型也很重要,因为默认情况下MvvmCross使用Service Locator pattern支持仅针对实现类型注册的接口(interface)类型。针对实现类型Foo和接口(interface)类型IFoo注册的示例。
    Mvx.ConstructAndRegisterSingleton<IFoo, Foo>();
    

    由于MvvmCross对 View 模型使用了构造函数注入(inject),因此无法将 View 模型类的公共(public)构造函数用于传递给 View 模型类实例的参数。构造函数参数必须能够来自IoC容器。

    MvvmCross在 View 模型之间传递5个参数(Navigation Service)

    MvvmCross 5 the Navigation Service中,它是在MvvmCross中编写导航逻辑的首选方法。导航服务还提供一种在 View 模型之间传递参数的方法,以避免通过 View 模型构造函数传递参数。 MvvmCross documentation中有一个很好的示例,显示了如何传递参数。这是摘录
    public class MyViewModel : MvxViewModel
    {
        private readonly IMvxNavigationService _navigationService;
    
        public MyViewModel(IMvxNavigationService navigationService)
        {
            _navigationService = navigationService;
        }
    
        public async Task SomeMethod()
        {
            await _navigationService.Navigate<NextViewModel, MyObject>(new MyObject());
        }
    }
    
    public class NextViewModel : MvxViewModel<MyObject>
    {
        private MyObject _myObject;
    
        public override void Prepare(MyObject parameter)
        {
            // receive and store the parameter here
            _myObject = parameter;
        }
    }
    

    快速说明。从NextViewModel( View 模型导航)开始,您需要在继承的MvxViewModel基类泛型中指定要传递的参数的类型,然后重写Prepare来传递参数。在MyViewModel方法的调用类navigate中,您包括导航到的 View 模型的类型以及要传递的参数的类型。然后,将参数作为navigate方法的第一个参数传递。

    在 View 模型(ShowViewModel)之间传递参数

    在示例代码中,您仍在使用较旧的ShowViewModel模式进行导航,因此我将介绍如何使用ShowViewModel方法传递参数。有关传递ShowViewModel参数的MvvmCross文档,可以找到here for the complex parameter objecthere for the simple parameter object

    复杂参数对象

    对于复杂的参数对象,MvvmCross要求您已安装MvvmCross Json插件。这种方法将序列化和反序列化通过的参数
    public class SchoolSelectionViewModel : BaseViewModel
    {
        public void DoSelectItem(SchoolModel item)
        {
            ShowViewModel<LoginViewModel, SchoolModel>(item);
        }
    }
    
    public class LoginViewModel : BaseViewModel<SchoolModel>
    {
        private SchoolModel _parameter;
    
        public override Task Prepare(SchoolModel parameter)
        {
            // receive and store the parameter here
            _parameter = parameter;
        }
    }
    

    如果您无法继承IMvxViewModel<TParameter>LoginViewModel,则可能还需要使用MvxViewModel<TParameter>契约实现自定义基类。
    public abstract class BaseViewModel<TParameter> : BaseViewModel, IMvxViewModel<TParameter>
    {
        public async Task Init(string parameter)
        {
            if (!string.IsNullOrEmpty(parameter))
            {
                IMvxJsonConverter serializer;
                if (!Mvx.TryResolve(out serializer))
                {
                    throw new MvxIoCResolveException("There is no implementation of IMvxJsonConverter registered. You need to use the MvvmCross Json plugin or create your own implementation of IMvxJsonConverter.");
                }
    
                var deserialized = serializer.DeserializeObject<TParameter>(parameter);
                Prepare(deserialized);
                await Initialize();
            }
        }
    
        public abstract void Prepare(TParameter parameter);
    }
    

    简单参数对象

    对于不需要序列化的基本类型,可以使用此方法,但会损失类型安全性参数。
    public class SchoolSelectionViewModel : BaseViewModel
    {
        public void DoSelectItem(SchoolModel item)
        {
            ShowViewModel<LoginViewModel>(item);
        }
    }
    
    public class LoginViewModel : BaseViewModel
    {
        private SchoolModel _parameter;
    
        public override void Init(SchoolModel parameter)
        {
            // receive and store the parameter here
            _parameter = parameter;
        }
    }
    

    注意用于Simple参数对象的类型的规则

    此处使用的类必须是仅用于以下导航的“简单”类:
  • 它必须包含无参数构造函数
  • 它应仅包含具有getset访问权限的公共(public)属性
  • 这些属性只能是以下类型:
  • bool
  • 整数类型:sbyteshortintlongbyteushortuintulong
  • 浮点类型:floatdouble
  • decimal
  • char
  • string
  • DateTime
  • Guid
  • 枚举值


  • 最后说明

    在早期版本的MvvmCross v5.x.x中,对 View 模型生命周期进行了一些更改,但是从v5.5+开始,上述模式应适用。

    关于c# - 从 View 模型导航到mvvmcross中的另一个 View 模型时崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48252248/

    相关文章:

    c# - OOP:理解抽象

    c# - ASP.NET MVC - 将多个变量传递给模型?

    javascript - JSON.NET 属性作为 Javascript 函数

    c# - 使用 SimpleInjector 的二进制 PowerShell Core cmdlet 导致 FileNotFoundException

    c# - 找不到引用 'RelativeSource FindAncestor, AncestorType=' System.Windows.Controls.UserControl',AncestorLevel ='1'' 的绑定(bind)源

    .net - MVVM/ViewModels 和处理授权

    Xamarin Forms ContextAction MenuItem 文本颜色

    android - Xamarin Android服务崩溃

    c# - Xamarin - 在 C# 中设置 Azure 离线同步

    c# - 使用 mvvm 在 xamarin 上绑定(bind)数据收集