如何在 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() 方法上反序列化它
我正在尝试将信息从 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() 方法上反序列化它