【问题标题】:View an Already Instantiated ViewModel in MVVMCross在 MVVMCross 中查看已经实例化的 ViewModel
【发布时间】:2013-07-25 23:06:58
【问题描述】:

有人知道如何查看现有的 IMvxViewModel 吗?

在我的应用程序中,我已经在另一个视图模型中创建了一堆 ViewModel (PhotoViewModel)。它们作为父 ViewModel (AlbumViewModel) 上的属性存在。当我想查看它时,只显示 PhotoViewModel 的特定实例而不是创建该视图模型的新实例会非常好。

public class AlbumViewModel : MvxViewModel {
    public ObservableCollection<PhotoViewModel> Photos
    {
        get { return GetValue(() => Photos); }
        set { SetValue(value, () => Photos); }
    }
}

public class PhotoViewModel : MvxViewModel { }

我想知道除了创建我自己的 IMvxViewModelLocator 之外是否有其他方法来完成这项任务。我认为在 MvxNavigationObject 上有一个名为 View 的受保护方法对于使用该框架的新开发人员和性能都非常有帮助。我们可以跳过当前完成的所有反射来实例化视图模型。

【问题讨论】:

    标签: c# mvvm mvvmcross


    【解决方案1】:

    MvvmCross 中默认的 ShowViewModel 机制使用基于页面的导航 - 此导航必须在 WindowsPhone 上使用 Uris,在 Android 上使用 Intents。

    因此,MvvmCross 不允许通过“丰富”对象进行导航 - 简单的可序列化 POCO 可以,但不支持复杂的“丰富”对象。

    由于“墓碑”,这更加重要 - 如果您的应用/页面/活动稍后再水化,那么您无法确定哪些历史视图或 ViewModel 对象实际上在您的历史“返回”堆栈中。


    如果您想通过丰富的对象导航,那么最好的方法是将这些丰富的对象存储在查找服务中,然后通过一些键/索引导航到查找中。但是,我个人会将这些查找对象称为 Models 而不是 ViewModels(但边界有时会变得模糊!)


    虽然基于 MvvmCross v1 代码,但这个问题仍然提供了相当好的背景 - What is the best way to pass objects to "navigated to" viewmodel in MVVMCross?

    一些最新的解释包括:


    最后一件事....

    ... MvvmCross 宣言坚持认为 MvvmCross 对定制非常开放...

    因此,您可以根据需要覆盖 MvvmCross 导航和查看模型位置。为此,创建您自己的IMvxViewModelLocator 可能是一个不错的起点。

    【讨论】:

    • 斯图尔特,非常感谢您的帮助。我实际上是在沿着你描述的道路前进,我想我已经想出了一个效果很好的解决方案。我已经修改了我的问题以显示建议的解决方案。很想听听您对此的看法。再次感谢您的帮助。
    【解决方案2】:

    经过一些测试,以下是建议的解决方案。我不是 100% 爱上它,但它确实有效并提供了我一直在寻找的类型开发人员体验。所以让我们深入研究。

    首先,我的所有 ViewModel (VM) 都继承自基础 VM,AVM。此抽象基类支持将对象作为公共静态方法进行查找。这有点恶心,但如果你愿意啜饮 Kool-Aid,它会很有效。以下是与此问题相关的类部分:

    public abstract class AVM : MvxViewModel {
        private static readonly Dictionary<Guid, WeakReference> ViewModelCache = new Dictionary<Guid, WeakReference>();
        private static readonly string BUNDLE_PARAM_ID = @"AVM_ID";
    
        private Guid AVM_ID = Guid.NewGuid();
        private Type MyType;
    
        protected AVM()
        {
            MyType = this.GetType();
            ViewModelCache.Add(AVM_ID, new WeakReference(this));
        }
    
        public static bool TryLoadFromBundle(IMvxBundle bundle, out IMvxViewModel viewModel)
        {
            if (null != bundle && bundle.Data.ContainsKey(BUNDLE_PARAM_ID))
            {
                var id = Guid.Parse(bundle.Data[BUNDLE_PARAM_ID]);
                viewModel = TryLoadFromCache(id);
                return true;
            }
    
            viewModel = null;
    
            return false;
        }
    
        private static IMvxViewModel TryLoadFromCache(Guid Id)
        {
            if (ViewModelCache.ContainsKey(Id))
            {
                try
                {
                    var reference = ViewModelCache[Id];
                    if (reference.IsAlive)
                        return (IMvxViewModel)reference.Target;
                }
                catch (Exception exp) { Mvx.Trace(exp.Message); }
            }
    
            return null;
        }
    
    
        protected void View()
        {
            var param = new Dictionary<string, string>();
            param.Add(BUNDLE_PARAM_ID, AVM_ID.ToString());
            ShowViewModel(MyType, param);
        }
    

    为了将这一切都连接起来,您必须创建一个自定义视图模型定位器。这是自定义定位器:

    public class AVMLocator : MvxDefaultViewModelLocator
    {
        public override bool TryLoad(Type viewModelType, IMvxBundle parameterValues, IMvxBundle savedState, out IMvxViewModel viewModel)
        {
            if (AVM.TryLoadFromBundle(parameterValues, out viewModel))
                return true;
            return base.TryLoad(viewModelType, parameterValues, savedState, out viewModel);
        }
    }
    

    最后你必须接线。为此,请进入您的 App.cs 并像这样覆盖 CreateDefaultViewModelLocator:

        protected override IMvxViewModelLocator CreateDefaultViewModelLocator()
        {
            return new AVMLocator();
        }
    

    你已经准备好了。现在,在任何已经存在且运行良好的派生 ViewModel 中,您可以执行以下操作:

    myDerivedVM.View();
    

    我还需要做一些其他事情(比如确保 WeakReference 完成它们的工作,并且我没有内存泄漏和一些额外的错误处理),但至少这是我想要的体验。我做的最后一件事是将以下命令添加到 AVM 基类:

    public MvxCommand ViewCommand
    {
        get { return new MvxCommand(View); }
    }
    

    现在您可以将该命令绑定到任何 UI 对象,当被调用时,它将使用该 VM 实例启动该视图。

    Stuart,感谢您帮助我朝着正确的方向前进。我很想听听您对我提供的解决方案的反馈。感谢您为 MVVMCross 所做的所有工作。这确实是一段非常漂亮的代码。

    干杯。

    【讨论】:

      猜你喜欢
      • 2017-10-18
      • 2014-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-08
      • 1970-01-01
      • 2017-02-05
      • 2019-06-16
      相关资源
      最近更新 更多