2012-04-20 14 views
4

Je suis en train d'utiliser un TabActivity avec MvvmCross mais je reçois un NullReferenceException dans le code-cadre parce que le viewModelLoader passé dans la OnViewCreate est nulleComment utiliser un MonoDroid TabActivity utilisant le framework MvvmCross

namespace Cirrious.MvvmCross.ExtensionMethods 
{ 
    public static class MvxViewExtensionMethods 
    { 
     public static void OnViewCreate<TViewModel>(this IMvxView<TViewModel> view, Func<TViewModel> viewModelLoader) 
      where TViewModel : class, IMvxViewModel 
     { 
      if (view.ViewModel != null) 
       return; 

      var viewModel = viewModelLoader(); 
      viewModel.RegisterView(view); 
      view.ViewModel = (TViewModel)viewModel; 
     } 

I Je soupçonne que c'est parce que j'essaie de charger les vues directement plutôt que via un ViewModel. Le code dans mon activité TabHost ressemble à:

[Activity(Label = "TabHost")] 
    public class TabHostView : MvxBindingTabActivityView<TabHostViewModel> 
    { 
     protected override void OnViewModelSet() 
     { 
      SetContentView(Resource.Layout.Page_TabHostView); 
      var tabHostWidget = this.TabHost; 

      TabHost.TabSpec spec;  // Resusable TabSpec for each tab 
      Intent intent;   // Reusable Intent for each tab 
      // Create an Intent to launch an Activity for the tab (to be reused) 
      intent = new Intent(this, typeof(HomeView)); 
      intent.AddFlags(ActivityFlags.NewTask); 

      // Initialize a TabSpec for each tab and add it to the TabHost 
      spec = tabHostWidget.NewTabSpec("home"); 
      spec.SetIndicator("Home", Resources.GetDrawable(Resource.Drawable.icon_home)); 
      spec.SetContent(intent); 
      tabHostWidget.AddTab(spec); 
//... more tabs 

Comment est-ce que je pourrais contourner ce problème?

Mes ViewModels sont également configurés de sorte que TabHostViewModel possède une propriété pour chaque page à onglet ViewModel. Ils sont paresseux en ce sens qu'ils n'obtiennent les données du modèle que lorsque l'accesseur get de la propriété est appelé.

Donc, si j'ai des liaisons de données dans ma page d'onglets axml, probablement le chemin doit supposer que le TabHostViewModel est le contexte (racine)?

Un grand merci, Jason

Répondre

6

Au niveau trivial je pense que vous pourriez être en mesure de résoudre votre problème actuel en posant le cadre pour créer l'intention pour vous:

 TabHost.TabSpec spec;  // Resusable TabSpec for each tab 
     Intent intent;   // Reusable Intent for each tab 
     // Create an Intent to launch an Activity for the tab (to be reused) 
     intent = base.CreateIntentFor<HomeViewModel>(); 
     intent.AddFlags(ActivityFlags.NewTask); 

A un niveau plus complet ...


Il y a plus d'une façon de aborder les pages à onglets - à la fois dans Android et dans MvvmCross.

Dans Android, vous choisissez d'aborder une TabActivity en utilisant une mise en page qui contient directement tous les axml pour les vues dans chaque onglet. Si vous travaillez avec cette méthode, alors je crois que vous pouvez simplement utiliser la liaison de données directement comme vous le feriez "normalement" - chaque onglet individuel devrait juste fonctionner comme son enfant normal Android Widget/View ... J'ai lu qu'il sont des avantages de performance en travaillant de cette façon avec les onglets Android, mais en général je ne travaille pas comme ça. En second lieu - la façon dont je travaille généralement - vous pouvez choisir d'aborder une TabActivity en traitant chaque onglet comme une activité distincte, et en reliant chacune de ces Activités à un ViewModel enfant de l'onglet principal ViewModel. (Je vais essayer de dessiner une image de cette structure et télécharger plus tard aujourd'hui!)

Si vous choisissez de faire cela, alors un bon exemple à suivre est la conférence un - https://github.com/slodge/MvvmCross/blob/master/Sample%20-%20CirriousConference/Cirrious.Conference.UI.Droid/Views/HomeView.cs

Qu'est-ce qui se passe dans ce exemple de conférence, est que les spécifications de l'onglet sont initialisés à l'aide spec.SetContent(intent) où l'intention est créée en utilisant une méthode de classe de base d'activité onglet CreateIntentFor - voici le code correspondant:

protected override void OnViewModelSet() 
    { 
     SetContentView(Resource.Layout.Page_Home); 

     TabHost.TabSpec spec;  // Resusable TabSpec for each tab 
     Intent intent;   // Reusable Intent for each tab 

     // Initialize a TabSpec for each tab and add it to the TabHost 
     spec = TabHost.NewTabSpec("welcome"); 
     spec.SetIndicator(this.GetText("Welcome"), Resources.GetDrawable(Resource.Drawable.Tab_Welcome)); 
     spec.SetContent(CreateIntentFor(ViewModel.Welcome)); 
     TabHost.AddTab(spec); 

     spec = TabHost.NewTabSpec("sessions"); 
     spec.SetIndicator(this.GetText("Sessions"), Resources.GetDrawable(Resource.Drawable.Tab_Sessions)); 
     spec.SetContent(CreateIntentFor(ViewModel.Sessions)); 
     TabHost.AddTab(spec); 

     spec = TabHost.NewTabSpec("favorites"); 
     spec.SetIndicator(this.GetText("Favorites"), Resources.GetDrawable(Resource.Drawable.Tab_Favorites)); 
     spec.SetContent(CreateIntentFor(ViewModel.Favorites)); 
     TabHost.AddTab(spec); 

     spec = TabHost.NewTabSpec("tweets"); 
     spec.SetIndicator(this.GetText("Tweets"), Resources.GetDrawable(Resource.Drawable.Tab_Tweets)); 
     spec.SetContent(CreateIntentFor(ViewModel.Twitter)); 
     TabHost.AddTab(spec); 
    } 

où le ViewModel haut niveau correspondant est un peu comme:

public class HomeViewModel 
    : MvxBaseViewModel 
{ 
    public HomeViewModel() 
    { 
     Welcome = new WelcomeViewModel(); 
     Sessions = new SessionsViewModel();    
     Twitter = new TwitterViewModel(); 
     Favorites = new FavoritesViewModel(); 
    } 

    public FavoritesViewModel Favorites { get; private set; } 
    public WelcomeViewModel Welcome { get; private set; } 
    public SessionsViewModel Sessions { get; private set; } 
    public TwitterViewModel Twitter { get; private set; } 
} 

...Comme une variation sur cette deuxième alternative (et il semble que c'est ce que vous essayez de faire dans cette question), si votre onglet individuel ViewModel ne sont pas vraiment liés les uns aux autres ou au «parent» TabHost ViewModel , vous pouvez ensuite lier chaque onglet à un nouveau ViewModel créé à la demande. Je ne pense pas que des échantillons publics font cela, mais si vous en avez besoin, le format pour ce serait:

 spec = TabHost.NewTabSpec("newTab"); 
     spec.SetIndicator("new tab text"), Resources.GetDrawable(Resource.Drawable.Tab_Icon)); 
     spec.SetContent(CreateIntentFor<NewViewModel>(new { constructorParameter1 = "value1", constructorParameter2 = "value2" })); 
     TabHost.AddTab(spec); 

Notez qu'il existe une philosophie sous-jacente qui se passe ici - cela fait partie de MvvmCross de opinion - La navigation/liaison dans mvx est toujours à propos de ViewModels, jamais à propos de Views. L'idée est que les applications sont construites ViewModel-first - chaque plate-forme client décide simplement comment représenter ces ViewModels sur l'écran. Ceci est susceptible de continuer et d'évoluer encore plus loin dans le développement iPad et WinRT où les splitviews, popups, etc seront communs. Une autre remarque ... si jamais vous avez une construction ViewModel compliquée ou si vous avez besoin d'utiliser des durées de vie spéciales pour certains de vos ViewModels (par exemple des singletons), il est également possible d'utiliser ViewModelLocators personnalisés dans votre application principale. ceux-ci vous permettraient alors de changer/contrôler la création dynamique de ViewModel si vous voulez ... mais je soupçonne que ce n'est pas le cas pour cette question.


Merci pour le détail de votre question - désolé de jeter dans les options avancées. Je vais essayer d'améliorer cette réponse avec une meilleure explication plus tard aujourd'hui ou ce week-end.


MVX est toujours ouvert aux idées ... afin que nous puissions modifier le code pour travailler avec votre code d'origine ... il ne serait pas difficile à faire et je peux voir justification. .. Je vais y réfléchir ...

+0

Fantastique. C'était aussi simple que d'utiliser CreateIntentFor . J'ai utilisé ViewModels pour les pages à onglet qui renvoyaient une chaîne de temps pour prouver qu'ils étaient en train d'être paresseux - ce qu'ils étaient. Cependant, les temps ne sont pas rafraîchis quand je feuillette entre les onglets (c'est-à-dire que le bindiung ne se produit plus lorsque vous revisitez une page à onglets). À votre avis, quelle est la meilleure façon de gérer cela? Merci encore! –

+0

c'est une question différente - et c'est un général mvvm! – Stuart

+0

Stackoverflow me rend paresseux! :( Pour me répondre pour le bénéfice des autres, j'ai ajouté une méthode Refresh() à mon ViewModel et l'ai appelée depuis OnResume() dans la vue pour qu'elle soit appelée à chaque fois que l'on consulte l'onglet. besoin d'appeler FirePropertyChanged(). –

Questions connexes