Search code examples
c#mvvmxamarinmvvmcross

How to pass ObservableCollection from ViewModelA to ViewModelB in MVVM


I am trying to pass information from ViewModelA to ViewModelB as follows. When I was debugging the code, I could able to observe my SCoordinates has 5 objects in it, but when I try to get these 5 objects in the ViewModelB, it is coming null. The other information (date, sId) is not null, only SCoordinates is null.

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;
}

Solution

  • I can think of 3 options here:

    1) {Bad practice but easy} Create a Service (singleton by default in mvvmcross) to hold your SCoordinates collection instead of keeping them in the ViewModel. This could be considered bad practice because Services are supposed to be stateless. Though it will work.

    UPDATE

    As an answer to a comment question, here´s an example. If you work with MvvmCross you should be familiar with this:

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

    So you create a simple class ending with "Service" and the corresponding interface in your core project.

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

    To access a service in your viewmodel you can use constructor injection and to gain access to the collection hosted in the service without using methods, the easier way would be something as follows:

    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) Use a local cache system to save SCoordinates. You can do it through a Service:

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

    Then, on your ViewModel.Init() you can restore your data.

    I recommend using Akavache as an easy local cache, but you could use other libraries or a plain SQLite table

    3) Serialize your collection with Json.Net and pass it to ShowViewModel() init params as a string. Then deserialize it on the Init() method