我正在尝试使用TabActivity和MvvmCross,但我在框架代码中得到NullReferenceException,因为传入OnViewCreate的viewModelLoader为null
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(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
我如何解决这个问题?
此外,我的ViewModel也已设置好,以便TabHostViewModel具有每个标签页ViewModel的属性。这些是懒惰的,因为当调用属性的get访问器时,它们只从Model获取数据。
所以如果我的标签页中有数据绑定的axml布局,大概是路径必须假设TabHostViewModel是Context(root)?
非常感谢, 杰森
答案 0 :(得分: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标签以这种方式工作有性能优势,但通常我不会这样工作。
其次 - 我通常的工作方式 - 您可以选择通过将每个Tab视为单独的Activity来处理TabActivity,并将每个Activity链接到主选项卡ViewModel的子ViewModel。 (我将尝试绘制这种结构的图片,并在今天晚些时候上传!)
如果你选择这样做,那么一个很好的例子就是会议一 - 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);
}
其中相应的顶级ViewModel有点像:
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(例如单例)使用特殊生命周期,那么也可以在“核心应用程序”中使用自定义ViewModelLocators - 这些然后允许你改变/控制动态ViewModel创建,如果你想...但我怀疑这不是这个问题的情况。
感谢您提问中的详细信息 - 抱歉投入高级选项。我会尝试在今天晚些时候或本周末更好地解释这个答案。
Mvx仍然对创意持开放态度......所以我们可以修改代码以使用原始代码......这样做并不难,我可以看到一些理由......我我会考虑一下......