在MVVMCross中查看已经实例化的ViewModel

时间:2013-07-25 23:06:59

标签: c# mvvm mvvmcross

有谁知道如何查看现有的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,以完成此任务。我认为在名为View的MvxNavigationObject上使用受保护的方法对于使用框架的新开发人员以及性能都非常有用。我们可以跳过当前完成的所有反射来实例化视图模型。

2 个答案:

答案 0 :(得分:1)

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

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

由于“墓碑化”这一点非常重要 - 如果您的app / page / activity稍后被重新水化,那么您无法确定历史View或ViewModel对象实际上在历史“回”堆栈中。


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


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

一些更新的解释包括:


最后一件事......

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

因此,如果您愿意,可以覆盖MvvmCross导航并查看模型位置。要做到这一点,创建自己的IMvxViewModelLocator可能是一个很好的开始方式。

答案 1 :(得分:1)

经过一些测试,以下是建议的解决方案。我并不是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();

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

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

现在您可以将该命令绑定到任何UI对象,并且在调用时,它将使用该虚拟机实例启动该视图。

斯图尔特,感谢你的帮助,引导我朝着正确的方向前进。我有兴趣听取您对我提供的解决方案的反馈意见。感谢您对MVVMCross的所有工作。它真的是一个非常漂亮的代码。

干杯。