Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best Way to Pass Data to new ViewModel when it is initiated

I am working on an WPF database application using the MVVM-Light Framework. I am using SimpleIOC to inject my Data Services, and my ViewModels are connected to the view from the XAML file. However, when opening new View Models I usually also need to pass another object to the View Model (lets say I am passing a integer of a CarId so I can grab the actual Car from a database).

Right now I am passing this by using a messenger on the View, after InitializeComponent(). This is then caught by the View Model. This currently does work, however it has some set backs.

First, the Message does not get sent until after the Constructor of the View Model is called. Lets say I had a ThisCar object Property in the ViewModel, and I had secondary properties like a string called Brand, that returns ThisCar.Brand.

    public const string ThisPersonPropertyName = "ThisCar";
    private Model.Car _thisCar;
    public Model.Car ThisCar
    {
        get
        {
            return _thisCar;
        }

        set
        {
            if (_thisCar== value)
            {
                return;
            }

            RaisePropertyChanging(ThisCarPropertyName);
            _thisCar= value;
            RaisePropertyChanged(ThisCarPropertyName);
        }
    }

    public const string BrandPropertyName = "Brand";
    public string Brand
    {
        get
        {
            return ThisCar.Brand;
        }

        set
        {
            if (ThisCar.Brand == value)
            {
                return;
            }

            RaisePropertyChanging(BrandPropertyName);
            ThisCar.Brand = value;
            RaisePropertyChanged(BrandPropertyName);
        }
    }

Now when the constructor gets called, I have to initialize ThisCar in the View Model Constructor Like so, otherwise I get a null error on ThisCar.Brand when the ViewModel gets created.

ThisCar = new CarObject();

Then, after it is initialized to an empty object, the Message will get called, and I will pull the real Car from the Database based on the Car ID.

ThisCar = webService.GetCar(carId);

However, when I do this, the Brand Property does not get updated, because nothing is calling the OnPropertyChanged for the Brand.

I have two work arounds, but they feel more like hacks to me. First on is manually calling the OnPropertyChanged for any properties that need to be updated. Second is I remove all the small properties, and bind everything directly to the ThisCar property (which means I have to implement INotifyPropertyChanged in my model).

Please let me know if I am just being picky, or if there is a better way to do this. The way I am doing it works, but just doesn't sit right with me. Thank you.

like image 911
Daryl Behrens Avatar asked Oct 21 '22 02:10

Daryl Behrens


2 Answers

Ok, so here is what I came up with, please let me know if this is a horrible idea.

I created an Interface called IRepository since I am using Dependency Injections to create View Models. Here is the implementation:

public interface IRepository
{
    void GetCarId(Action<int> callback);
    void SetCarId(int value);
}

Next I create a class which inherits from IRepository

public class Repository : IRepository
{
    private int CarId;
    public void GetCarId(Action<int> callback)
    {
        callback(CarId);
    }

    public void setDictionary(int carId)
    {
        CarId = carId;
    }
}

Now to pass the value into the repository, I put this into the parent View Model

 r.SetCarId(13);

And to recieve in the new view Model I put this in the View Models Constructor:

    public View2ViewModel(IChildView i, IRepository r)
    {
        int CarId;
        r.GetCarId((id) => CarId = id);
        thisCar = GetCarFromDatabase(CarId);
    }

This seems to me to fix the problem, without breaking the MVVM pattern, and since the IOC Container I am using creates all the Interface Members as staic, I will have this data throughout the rest of my Views.

The main problem I have with this is that it feels like a global variable, but this is the best I could come up with on it. Please let me know if this is a really bad idea. Thank you.

like image 93
Daryl Behrens Avatar answered Oct 29 '22 22:10

Daryl Behrens


I will vote for making models able to notify there changes i.e implementing INotifyPropertyChanged in models. Two reasons:

  1. There may be multiple places/views you might need to use the same model and you would not like to take care in each ViewModel as which property change should raise which other property changes. Frankly thats messy and buggy.

  2. Sometimes, in the VM layer we might need to listen to changes in models, so in that case also self notifying model helps.

Thanks

like image 23
Nitin Avatar answered Oct 30 '22 00:10

Nitin