如何在 MVVM 中将 ObservableCollection 从 ViewModelA 传递到 ViewModelB

How to pass ObservableCollection from ViewModelA to ViewModelB in MVVM

我正在尝试将信息从 ViewModelA 传递到 ViewModelB,如下所示。当我调试代码时,我可以观察到我的 SCoordinates 中有 5 个对象,但是当我尝试在 ViewModelB 中获取这 5 个对象时,它变为空。其他信息(date,sId)不为空,只有SCoordinates为空。

ViewModelA

public ObservableCollection<SVModel> SCoordinates 
{
   get { return _sp; }
   set
   {
     _sp = value;
     RaisePropertyChanged(() => SCoordinates );
   }
 }

private void SSelected(SVModel obj)
{
   ShowViewModel<ViewModelB>(new { sId = obj.Id, date = DateTime.Now, sCoordinates = SCoordinates });
}

ViewModelB

public void Init(string sId, DateTime date, ObservableCollection<SVModel> sCoordinates)
 {
   var sp = _sService.GetService(sId);
   SVModel = new SVModel (sp);
  // the following gets null
   SCoordinates = sCoordinates;
}

正如 documentation 明确指出的那样:

Note that due to serialization requirements, the only available parameter types used within this technique are only:

  • it must contain a parameterless constructor
  • it should contain only public properties with both get and set access
  • these properties should be only of types:
    • int, long, double, string, Guid, enumeration values

换句话说,只支持值类型作为参数的成员。

我认为这是 MvvmCross 的一大设计缺陷。我并不是说应该支持这一点,但如果不遵守文档中的约束,框架应该抛出异常。因此,与其默默地接受您传递了一个不符合约束条件的 class 并传递了 null,框架应该抛出一个明确的异常。从而通知您,而不是让您在这里寻找答案。

这种哲学还有一个名字:Fail fast!

这里我可以想到 3 个选项:

1) {不好的做法但很容易}创建一个服务(在 mvvmcross 中默认为单例)来保存您的 SCoordinates 集合,而不是将它们保存在 ViewModel 中。这可能被认为是不好的做法,因为服务应该是无状态的。虽然它会起作用。

更新

作为对评论问题的回答,这里有一个例子。如果你使用 MvvmCross,你应该熟悉这个:

public class App : MvxApplication
{
    public override void Initialize()
    {
        CreatableTypes()
            .EndingWith("Service")
            .AsInterfaces()
            .RegisterAsLazySingleton();

        /// ...
    }
}

所以你创建了一个简单的class以"Service"结尾,并在你的核心项目中创建了相应的接口。

public interface ICoordinatesService
{
    ObservableCollection<SVModel> Coordinates { get; set; }
}

public class CoordinatesService : ICoordinatesService
{
    public ObservableCollection<SVModel> Coordinates { get; set; }
}

要在您的视图模型中访问服务,您可以使用 constructor injection 并在不使用方法的情况下访问服务中托管的集合,更简单的方法如下:

public class YourViewModel : MvxViewModel
{
    public ObservableCollection<SVModel> Coordinates => _coordinatesService.Coordinates;

    private readonly ICoordinatesService _coordinatesService;

    public YourViewModel(ICoordinatesService coordinatesService)
    {
        _coordinatesService = coordinatesService;
    }

    public void SaveSomeCoordinates()
    {
        Coordinates.Add(new SVModel());
    }

    public void RemoveSomeCoordinates()
    {
        Coordinates.RemoveAt(1);
    }

    public void ResetCoordinates()
    {
        _coordinatesService.Coordinates = new ObservableCollection<SVModel>();
    }
}

2) 使用本地缓存系统保存SCoordinates。您可以通过服务来完成:

public class Service
{
    public ObservableCollection<SVModel> RestoreCoordinates() 
    {
         // get from cache
    }
    public bool SaveCoordinates(ObservableCollection<SVModel> coordinates) 
    {
        // save to cache
    }
}

然后,您可以在 ViewModel.Init() 上恢复数据。

我建议使用 Akavache 作为简单的本地缓存,但您可以使用其他库或普通 SQLite table

3) Serialize your collection with Json.Net 并将其作为字符串传递给 ShowViewModel() init params。然后在 Init() 方法上反序列化它