5

当从缓存中重新加载 ViewModel 时,我需要能够拦截框架并执行重新初始化。由于没有重新创建 ViewModel,我既不能使用 Init()、MvxViewModel.InitFromBundle 也不能使用 MvxViewModel.ReloadFromBundle 方法。

我正在尝试调试单击后退按钮会恢复状态不一致的 ViewModel 的情况。某种 MvxViewModel.OnReloading() 会有所帮助。

有没有办法在 v3 中做到这一点?

编辑:

假设我有 FirstPageViewModel,它公开了一个导航到 SecondPageViewModel 的命令。根据我的观察,如果您在 SecondPageView 上单击模拟器的后退按钮,则不会构造 FirstPageViewModel。相反,我相信它是从某个缓存中检索的,然后绑定到视图。此缓存可能是 IMvxSingleViewModel 缓存的实现。

因此,在 ViewModel 构建之后调用 Init()、InitFromBundle() 和 ReloadFromBundle() 的常规流程不适用于这种情况。换句话说,我需要一种重新初始化 ViewModel 的方法,无论它是刚刚构建的,还是从缓存中复活的。如果是前者,我可以使用 Init() 方法。如果后者为真,则无法在 ViewModel 本身内执行此操作。

这就是问题:

我有一个从 FirstViewModel 传递到 SecondViewModel 的 ICollectionService 实例。FirstView 还包含一个绑定到此 CollectionService 的 ListView。因为 CollectionService 不是强类型的,所以我可以传递它并使用适当的项目模板在视图中呈现它的项目。

在显示 SecondViewModel 之前,FirstViewModel 检索一些远程数据并填充 CollectionService。当显示 SecondViewModel 时,它的视图使用不同的项模板显示来自 CollectionService 的数据。但是,如果我向后导航,由于 FirstViewModel 仍在引用 CollectionService,FirstView 将呈现 SecondViewModel 使用的数据,除非可以重新初始化 FirstViewModel,在此过程中清除 CollectionService。也许方法是错误的,但这是我问题的症结所在。

我不知道平台是否会有所不同,因为我希望 Windows Phone 和 iOS 上的行为相同,因为这种重新初始化将发生在 Core 模块中。尽管如此,这些都是对 Android 的观察。

TIA。

4

2 回答 2

6

感谢您更新您的问题以提供更多信息。

使用 MvvmCross 方法进行跨平台开发使您能够利用原生 UI 平台。这确实意味着您 - 开发人员 - 确实需要了解一些关于这些平台的知识 - 要理解的关键内容之一是“视图”生命周期,包括在导航堆栈中使用时。

默认情况下,MvvmCross 不会在任何显着的时间长度内缓存视图模型。在屏幕转换(例如旋转)期间偶尔会出现短期缓存,但不再存在长期缓存。相反,当创建一个新视图时,默认情况下 mvx 会创建一个新的视图模型来配合它——并且这对“终生”在一起——生命的结束由视图决定。

我鼓励您花一些时间阅读每个平台上的基本生命周期和导航范例。

  • 在 iOS 上,这意味着特别要了解 UiNavigationController,它在内存中维护一堆 UiViewController(在 mvx 中,每个都将与单个视图模型结合)

  • WindowsPhone 中的情况类似,RootFrame 在 RAM 中维护一堆页面。这里有一个复杂性 - 墓碑 - 但在你确定基本生命周期之前忘记它。

  • 在 Android 上,情况类似,但略有不同。对于基本应用程序,您可能可以假设 Android 将在您的应用程序的生命周期中在 RAM 中维护一堆 Activity 页面。然而,Android 实际上远比这复杂得多——当操作系统决定回收内存时,它可以从 RAM 中删除 backstack 项。在这些情况下,有时会担心一些“墓碑”和脱水 - 但是,我再次建议您忽略这一点,直到您掌握了基本知识。

  • 在 winrt 上,默认情况下,情况实际上是您在描述中所理解的 - 后台堆栈只保存状态信息 - 视图本身不会缓存在 RAM 中。

以上故事希望能让您了解每个平台上导航堆栈中的视图生命周期 - 因此也为您提供视图模型生命周期。


现在,如果您希望您的视图模型(或其他一些应用程序级别的对象)了解视图可见性状态,那么您需要在每个平台上拦截一些视图事件并将这些事件传递给视图楷模。

例如,您的 FirstViewModel 可以将 OnMadeVisible() 公开为自定义 Api。在这种情况下,您可以确保它是从 Windows 上的 OnNavigatedTo、Android 上的 OnResume 和 iOS 上的 ViewDidAppear 调用的


或者,如果您正在查看 ViewModel-ViewModel 通信的一般机制,那么我建议您查看类似


笔记:

显然,导航堆栈不是唯一的导航范例——如果您的应用程序还使用弹出窗口、选项卡、拆分视图、汉堡包等,那么您也需要了解这些视图生命周期。

如果您对 View 生命周期有疑问,那么向其构造函数和关键生命周期事件添加跟踪是一个很好的第一步,


最后一点,如果您决定默认视图模型位置和视图模型生命周期不是您的应用程序所需要的 - 例如,如果您想使用单例视图模型,那么这很容易实现 - 查看在 App.cs 中覆盖视图模型定位器班级。

于 2013-07-25T20:06:56.697 回答
0

即使知道这个问题已经 3 年了,我不确定在当前版本中是否有办法做到这一点,但我自己做了。在此示例中,我将创建一个静态类,该类包含应用程序中 ViewModel 的所有实例。它以 null 静态变量开始,并在每个 ViewModel 实例化时获取每个值(在构造函数方法上)。

public static class ViewStackService
{
    //Stack
    private static exmp1ViewModel exmp1 = null;
    private static exmp2ViewModel exmp2 = null;
    private static exmp3ViewModel exmp3 = null;

    public static void addStackLevel(exmp1ViewModel _parent)
    {
        exmp1 = _parent;
    }

    public static void addStackLevel(exmp2ViewModel _parent)
    {
        exmp2 = _parent;
    }

    public static void addStackLevel(exmp3ViewModel _parent)
    {
        exmp3 = _parent;
    }


    public static async void burnAll()
    {

        if (exmp3 != null)
        {
            exmp3.DoBackCommand();
            await Task.Delay(250);
            exmp3 = null;
        }
        if (exmp2 != null)
        {
            //the OnResume method can be implemented here
            exmp2.DoBackCommand();
            await Task.Delay(250);
            exmp2 = null;
        }
        if (exmp1 != null)
        {
            //the OnResume method can be implemented here
            exmp1.DoBackCommand();
            await Task.Delay(250);
            exmp1 = null;
        }
    }
}

当每个 ViewModel 的构造函数启动时,这些用作变量的 ViewModel 会接收实例:

public class exmp1ViewModel 
    : MvxViewModel
{
    public exmp3ViewModel (){
        ViewStackService.addStackLevel (this);
    }
}

方法 burnAll() 将在调用时关闭所有 ViewModel。这是有问题的,因为当我手动设置线程等待的时间时,它可能在某些不同的设备中存在错误,这取决于它的性能。但是使用这个类,你可以做一些其他的事情,比如检查一个 ViewModel 之前是否已经实例化来实例化一个新的,或者使用这个类来实现一个 OnResume 方法,以便在再次显示 ViewModel 时调用。请记住,实例只能在未暂停时使用,也就是说,您只能在应用程序使用 ViewModel 时调用它的方法。

于 2016-03-24T17:12:51.343 回答