简体   繁体   中英

How to pass an object to a ViewModel using dependency injection?

I need to pass an object from one view model to another. In my current implementation I created a static instance of the ProductVM, following this example , then accessed it's property from the instance . But passing a static instance doesn't seem like a solid design in the long run.

private static ProductVM _instance = new ProductVM();
public static ProductVMInstance { get { return _instance; } }

When researching alternatives to providing a static view model instance, I came across, constructor injection as an option.

Question:

Does anyone have any example, on how to implement ctor injection for passing of objects? (Preferably not using a third party framework)

ProductsVM: (view model that holds property to be sent)

    public ProductModel SelectedProduct { get; set; }

CustomerOrdersVM: (view model that the SelectedProduct needs to be passed into)

public class CustomerOrdersViewModel : IPageViewModel
{

    public CustomerOrdersViewModel()
    {                  

    }
}

Rowbear covered the case of EventAggregators. They can't be used in all situations.

EventAggregator pattern is useful only if the target ViewModel (and it's corresponding view) is already instantiated, because only then the ViewModel will have registered to the event to react on.

This is not suitable for cases where the ViewModel/View has to be instantiated/navigated too before it can be displayed. One example is a Smartphone App, where you click on an product and receive it's details or something like that.

In this case you need an navigation service, where you can pass a certain parameter ( productId , orderId etc.) to the navigation call, like navigationService.Navigate("OrderDetails", orderId); and have your ViewModels implement some kind of INavigationAware interface, that is called by your navigation service in case the ViewModel implements this interface.

public class OrderDetailsViewModel : ViewModelBase, INavigationAware
{
    public async void OnNavigatedFrom(object parameter) 
    {
        var orderId = (int)parameter;
        var order = await orderRepository.GetOrderByIdAsync(orderId);

        // display your order in the ViewModel here
    }
}

There are many frameworks that come with a navigation service, most popular for enterprise application is the Prism MVVM framework (initially developed by Microsoft)

The accepted answer in the example link you provided actually describes constructor injection. Both answers 1 and 2 are technically describing "constructor injection". In order to implement constructor injection without a third party framework for your case, you would essentially need to either pass a SelectedProduct or a ProductsVM instance to CustomerOrdersVM's constructor. The only thing left is an overarching class or viewmodel that serves as the "injector", which controls the construction of the CustomerOrdersVM instance and the ProductsVM instance (or at least has a reference to an instance that it can pass to CustomerOrdersVM's ctor):

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        ProductsViewModel = new ProductsVM();
        OrdersViewModel = new CustomerOrdersVM(ProductsViewModel);
    }
    public CustomerOrdersVM OrdersViewModel { get; private set; }

    public ProductsVM ProductsViewModel { get; private set; }
}

I worked on a MVVM (mostly) project that used constructor injection without a third party injector throughout. It made sense because we had an overarching object (it served as a primary parent viewmodel and an injector, technically) that contained multiple viewmodels as its properties. However, it eventually became somewhat unwieldy because some viewmodels would require a seemingly random other viewmodel in its constructor. Depending on how cleanly separated your viewmodels are from your Models or databases/external services, unit testing the viewmodels could also potentially become more difficult because you might start having logic in your viewmodel that requires the dependency to be fully constructed with production data.

This leads me to ask you... why do you need the SelectedProduct in CustomerOrderVM? Is it because a user interaction will set the SelectedProduct property in ProductsVM, and your CustomerOrdersVM will need to know about it? If that's the case, have you considered implementing the EventAggregator Pattern ? When someone on my team implemented this, it made passing information between viewmodels a complete breeze. The viewmodels will have a reference to an eventaggregator, and subscribe to events upon construction. This way, when your user selects a product, your ProductsVM can take an action, and publish an event that your CustomerOrdersVM can subscribe to and take action on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM