2017-08-07 3 views
0

J'essaie de passer une liste d'objets en tant que paramètre entre différentes instances du même viewmodel (récursif) en utilisant ShowViewModel<ViewModel, Type>(parameter). J'ai cette erreur dans mon projet de formulaires MVVMCross/xamarin.MvxException: Impossible de construire et d'initialiser ViewModel

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 

pour expliquer plus, voici mon 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); 
     } 
    } 
} 

Pour expliquer plus: chaque objet a une liste de ObjectItems. Et chaque ObjectItem a également une liste d'ObjectItems. L'objet hérite de ObjectItem. Voici un exemple:

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"}, 
        } 
       } 
      } 

Pour charger ces articles, j'ai créé un viewmodel de classe, d'où ObjectViewModel hérite

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

     } 
    } 
} 
+0

Juste une observation de la performance - vous avez beaucoup de mises en page de pile imbriquées inutiles sur votre page. À la racine de la page, il y en a une dans l'autre, et il y en a une qui encapsule la vue de la liste. Jetez un oeil à la section Réduire la taille de l'arborescence sur cette page https://developer.xamarin.com/guides/xamarin-forms/deployment-testing/performance/ –

Répondre

1

Tout ce que vous mettez dans le cteur d'un ViewModel, MvvmCross traite comme quelque chose qu'il doit obtenir via Dependency Injection. Donc dans votre cas, puisque ObjectItem n'est pas enregistré dans le conteneur IvC de MvvmCross, ShowViewModel ne peut pas résoudre et mettre dans le constructeur.

Comment cela est généralement résolu, est d'utiliser la méthode Init dans votre ViewModel au lieu de passer un objet en tant que paramètre dans l'appel ShowViewModel.

Si vous utilisez MvvmCross 5.x ou plus récent, cela s'est déplacé dans Initialize, lors de l'utilisation du nouveau NavigationService, mais les bases sont plus ou moins les mêmes.

Je pense que le moyen le plus rapide dans ce cas dans votre cas, est d'ajouter un constructeur vide à votre ObjectViewModel. Ajoutez ensuite une méthode Init dans laquelle vous affectez votre accessoire Obj et appelez le loadItems() similaire au ctor non vide. Donc, quelque chose comme:

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

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

    // rest of code 
} 

Retirez le cteur de ObjectItemViewModel, puis faire ObjectViewModel ceci:

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

n'est pas ce que Initialize est là pour? – hanaa

+0

J'ai déjà implémenté Initialize (regardez à la fin de ObjectItemViewModel). Je ne comprends vraiment pas ce que vous avez écrit ... Quel constructeur vous proposez de supprimer? – hanaa

+0

Initialize est seulement appelé si vous utilisez NavigationService, vous n'utilisez pas cela ici, par conséquent il ne sera pas appelé. Vous avez juste besoin d'ajouter un ctor vide et la méthode Init ... Comment cela n'est-il pas clair? – Cheesebaron