简体   繁体   中英

MvxException: Failed to construct and initialize ViewModel

I am trying to pass a List of objects as a parameter between different instances of the same viewmodel (recursive) using ShowViewModel<ViewModel, Type>(parameter) . I am having this error in my MVVMCross/xamarin forms project.

 MvvmCross.Platform.Exceptions.MvxException: Failed to construct and 
initialize ViewModel for type Project.Core.ViewModels.ObjectViewModel from locator MvxDefaultViewModelLocator - check InnerException for more information ---> MvvmCross.Platform.Exceptions.MvxException: Problem creating viewModel of type ObjectViewModel ---> MvvmCross.Platform.Exceptions.MvxIoCResolveException: Failed to resolve parameter for parameter obj of type ObjectItem when creating Project.Core.ViewModels.ObjectViewModel
  at MvvmCross.Platform.IoC.MvxSimpleIoCContainer.GetIoCParameterValues (System.Type type, System.Reflection.ConstructorInfo firstConstructor) [0x00036] in C:\projects\mvvmcross\MvvmCross\Platform\Platform\IoC\MvxSimpleIoCContainer.cs:502 
  at MvvmCross.Platform.IoC.MvxSimpleIoCContainer.IoCConstruct (System.Type type) [0x0002c] in C:\projects\mvvmcross\MvvmCross\Platform\Platform\IoC\MvxSimpleIoCContainer.cs:312 
  at MvvmCross.Platform.Mvx.IocConstruct (System.Type t) [0x00006] in C:\projects\mvvmcross\MvvmCross\Platform\Platform\Mvx.cs:169 
  at MvvmCross.Core.ViewModels.MvxDefaultViewModelLocator.Load (System.Type viewModelType, MvvmCross.Core.ViewModels.IMvxBundle parameterValues, MvvmCross.Core.ViewModels.IMvxBundle savedState) [0x00000] in C:\projects\mvvmcross\MvvmCross\Core\Core\ViewModels\MvxDefaultViewModelLocator.cs:33 

to explain more, here is my code:

ObjectPage.xaml

<mvx:MvxContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:mvx="clr-namespace:MvvmCross.Forms.Core;assembly=MvvmCross.Forms"
         x:Class="Project.Core.Pages.ObjectPage"
         Title="Object">
<StackLayout>
    <StackLayout>
        <StackLayout Padding="5,5,5,5">
            <Label Text="{Binding SubTitle}" FontSize="21" VerticalOptions="End"></Label>
        </StackLayout>
        <StackLayout>
            <ListView 
                ItemsSource="{Binding ObjectItems}"
                SelectedItem="{Binding SelectedListItem, Mode=TwoWay}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Frame HasShadow="True" Margin="10">
                                <StackLayout Margin="8">
                                    <Label Text="{Binding Title}"></Label>
                                </StackLayout>
                            </Frame>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackLayout>
    </StackLayout>
</StackLayout>

ObjectPage.xaml.cs

namespace Project.Core.Pages
{
    public partial class ObjectPage
    {
        public ObjectPage (ObjectItem obj)
        {
            InitializeComponent ();
            BindingContext = new ObjectViewModel(obj);
        }
    }
}

ObjectViewModel

namespace Project.Core.ViewModels
{
    public class ObjectViewModel : ObjectItemViewModel
    {

        public ObjectViewModel(ObjectItem obj) : base(obj)
        {
        }

        public override void ShowItem()
        {
                ShowViewModel<ObjectViewModel, ObjectItem>(Obj);
        }
    }
}

To explain more: each Object has a list of ObjectItems. And each ObjectItem has also a list of ObjectItems. Object inherits from ObjectItem. Here is an example:

Objects = new List<Object>
            {
                new Object
                {
                    Title = "Title",
                    SubTitle="subtitle",
                    ObjectItems = new List<ObjectItem>
                    {
                        new ObjectItem{Title = "title goes here" },
                        new ObjectItem{Title = "another objectItem title"},
                    }
                }
             }

To load these items, I created a class viewmodel, from where ObjectViewModel inherits

ObjectItemViewModel.cs

 namespace Project.Core.ViewModels
{
    public class ObjectItemViewModel : MvxViewModel<ObjectItem>
{
    public string Title => (this.Obj == null ? "/!\\ Titre" : this.Obj.Title);
    public string SubTitle => (this.Obj == null ? "/!\\ Sous Titre" : this.Obj.SubTitle);
    private List<ObjectItemViewModel> _ObjectItems;
    public List<ObjectItemViewModel> ObjectItems
        {
            get => _ObjectItems;

            set
            {
                _ObjectItems = value;
                RaisePropertyChanged(() => ObjectItems);
            }
        }
        private ObjectItem _Obj;
        public ObjectItem Obj
        {
            get => _Obj;
            set
            {
                _Obj= value;
                RaisePropertyChanged(() => Obj);
            }
        }
        public ObjectItemViewModel _selectedListItem;
        public ObjectItemViewModel SelectedListItem
        {
            get
            {
                return _selectedListItem;
            }
            set
            {
                if (SetProperty(ref _selectedListItem, value))
                    OnSelectedChangedCommand.Execute(value);
            }
        }

        public ObjectItemViewModel(ObjectItem obj)
        {
            this.Obj = obj;
            loadItems();
        }

        private MvxCommand<ObjectItemViewModel> _onSelectedChangedCommand;

        private ICommand OnSelectedChangedCommand
        {
            get
            {
                return _onSelectedChangedCommand ?? (_onSelectedChangedCommand = new MvxCommand<ObjectItemViewModel>((item) =>
                {
                    if (item == null)
                    {
                        Debug.WriteLine("Item null");

                        return;
                    }
                    item.ShowItem();
                }));
            }
        }


        public virtual void ShowItem()
        {
            //DO nothing


        }

        public void loadItems()
        {
            if (this.Obj != null &&
                this.Obj.ObjectItems != null &&
                this.Obj.ObjectItems.Count > 0)
            {
                ObjectItems = new List<ObjectItemViewModel>();
                foreach (ObjectItem objectItem in this.Obj.ObjectItems)
                {
                    if (objectItem.MemoItems != null && objectItem.ObjectItems.Count == 1)
                    {
                        ObjectItems.Add(new InfoViewModel(objectItem));
                    }
                    else
                    {

                        ObjectItems.Add(new ObjectViewModel(objectItem));
                        Debug.WriteLine("Item crée" + objectItem.Title);
                    }
                }

            }
        }

        public override Task Initialize(ObjectItem parameter)
        {
            Obj = parameter;
            return Task.FromResult(0);

        }
    }
}

Everything you put in the ctor of a ViewModel, MvvmCross treats as something it needs to get via Dependency Injection. So in your case, since ObjectItem isn't registered in the MvvmCross IoC container, ShowViewModel cannot resolve and put into the constructor.

How this is usually solved, is to use the Init method in your ViewModel instead to pass along an object as a parameter in the ShowViewModel call.

If you use MvvmCross 5.x or newer this has moved into Initialize , when using the new NavigationService , but basics are more or less the same.

I think the quickest way around this in your case, is to add an empty constructor to your ObjectViewModel . Then add a Init method, in which you assign your Obj prop and call loadItems() similar to the non-empty ctor. So something like:

public class ObjectItemViewModel : MvxViewModel<ObjectItem>
{
    public ObjectItemViewModel() { }

    public void Init(ObjectItem obj)
    {
        Obj = obj;
        loadItems();
    }

    // rest of code
}

Remove the ctor from ObjectItemViewModel , then make ObjectViewModel this:

public class ObjectViewModel : ObjectItemViewModel
{
    public override void ShowItem()
    {
        ShowViewModel<ObjectViewModel, ObjectItem>(Obj);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM