2012-04-20 53 views
4

我試圖使用與MvvmCross一個TabActivity但我正在逐漸的框架代碼一個NullReferenceException因爲viewModelLoader傳遞到OnViewCreate使用的是空如何使用MonoDroid的TabActivity 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; 
     } 

我懷疑這是因爲我試圖加載視圖,而不是通過ViewModel。我的TabHost Activity中的代碼如下所示:

[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 

我將如何解決此問題?

此外,我的ViewModels設置,以便TabHostViewModel具有每個標籤頁ViewModel的屬性。這是懶惰的,因爲它們只在調用屬性的get訪問器時才從模型中獲取數據。

因此,如果我有我的標籤頁axml佈局的數據綁定,大概路徑必須假定TabHostViewModel是上下文(根)?

非常感謝, 傑森

回答

6

在一個微不足道的水平我想你可能能夠解決當前的問題,通過詢問框架來創建你的意圖:

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

在更完整的水平...


有不止一種方法處理選項卡式頁面 - 無論是在Android還是MvvmCross。

在Android中,您選擇通過使用直接包含每個單獨標籤中視圖的所有axml的佈局來解決TabActivity。如果你使用這種方法,那我認爲,你可以直接使用數據綁定,就像你會「正常」 - 每個單獨的標籤應該就像其正常的孩子Android Widget/View一樣工作...我已經讀過那裏在Android標籤中以這種方式工作是性能優勢,但通常我不會像這樣工作。其次 - 我通常的工作方式 - 您可以選擇通過將每個選項卡視爲單獨的活動並將每個活動鏈接到主選項卡ViewModel的子ViewModel來解決TabActivity問題。 (我會試着畫出這種結構的圖片,今天晚些時候上傳吧!)

如果你選擇這樣做,然後一個很好的例子,遵循的是會議一個 - https://github.com/slodge/MvvmCross/blob/master/Sample%20-%20CirriousConference/Cirrious.Conference.UI.Droid/Views/HomeView.cs

在此會發生什麼會議例子,是標籤規格是spec.SetContent(intent),其中使用標籤活動的基類方法CreateIntentFor創建的意圖初始化 - 這裏的相關代碼:

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

,其中相應的頂層視圖模型有點像:

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

...

作爲第二個替代方案的變體(它看起來像這個就是你在這個問題中要做的),如果你的單個選項卡ViewModel沒有真正相互關聯,或者不與'父級'相關聯TabHost ViewModel ,那麼您可以將每個選項卡鏈接到按需創建的新ViewModel。我不認爲任何市民樣品做到這一點的,但如果你需要它,那麼這種格式是:

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

注意,有一個基本理念怎麼回事 - 這是MvvmCross的的一部分意見 - 在mvx中的導航/鏈接總是關於ViewModels,而不是Views。這個想法是,應用程序是建立在ViewModel優先 - 每個客戶端平臺只是決定如何在屏幕上表示這些ViewModel。在iPad和WinRT的開發中,這可能會繼續並進一步發展,在這種情況下,分割視圖,彈出窗口等將很常見。

還有一點需要注意......如果您有複雜的ViewModel構造,或者您需要爲您的某些ViewModel(例如singletons)使用特殊的生命週期,那麼也可以在您的「核心應用程序」中使用自定義的ViewModelLocators - 這些將允許你改變/控制動態ViewModel的創建,如果你想......但我懷疑這不是這個問題的情況。


感謝您的問題中的細節 - 遺憾的是投入高級選項。我會盡力在今天晚些時候或本週末提供更好的解釋來加強這個答案。


MVX也是觀念仍然是開放......這樣我們就可以修改代碼與你原來的代碼工作... ...它不會是很難做到的,我可以看到一些理由吧。 ..我會想一想...

+0

太棒了。這與使用CreateIntentFor 一樣簡單。我使用ViewModels作爲返回時間字符串的標籤頁來證明他們正在被加載 - 它們是什麼。但是,當我在選項卡之間滑動時,時間不會被刷新(即,當您重新訪問選項卡頁時,bindiung不會再次發生)。你認爲什麼是解決這個問題的最好方法?再次感謝! – 2012-04-20 11:17:01

+0

這是一個不同的問題 - 這是一個普通的mvvm! – Stuart 2012-04-20 11:30:36

+0

Stackoverflow讓我懶! :( 爲了回答我自己以利於他人,我向ViewModel添加了一個Refresh()方法,並在View中從OnResume()中調用它,以便每次查看該選項卡時都會調用它。Refresh會更新ViewModel屬性需要調用FirePropertyChanged()。 – 2012-04-20 12:55:53