0

I have a View / ViewModel where a ProductList is loaded. This list is not visible on the screen.

What I need to do is show a new View/ViewModel (e.g. SelectProductView / SelectProductViewModel), pass the ProductList to them, and after a user selects a particular Product, close this view, and make use of selected product.

What is the best way to achieve this?

I am using MVVMLight, but I guess the ideas should not be restricted just to it. The easiest way is to create a view, and pass collection to it, but that doesn't sound MVVM friendly. I was thinking of creating a SelectProductViewModel from the first ViewModel and pass the collection to it, but I don't know how would I automatically create SelectProductView and bind it to created SelectProductViewModel.

Edit: in my application view structure is a bit complex. I have a main view, which basically needs to host a SelectProductView, since this view must cover whole screen. MainView contains lots of child and grandchild views (through tabs), so there could be 3 different child views or grand childViews that could issue a request for a product to be selected. Also, some view will not have products preloaded, so this task should probably be propagated to a SelectProductViewModel.

Example of Structure:

                              MainView
                        /                   \
         ChildViewA                                   ChildViewB
            /  \                                       /     \
GrandChildViewA1 GrandChildViewA2            GrandChildViewB1 GrandChildViewB2

So, GrandChildViewA1, ChildViewB and GrandChildViewB2 could issue a request for a product to be selected. Only the view that issued a request should get the selected product, others should not bother with it. GrandChildViewA1 will have loaded products in it, but GrandChildViewB2 will not have ProductList loaded in it. This means, for performance sake, that GrandChildViewA1 should pass product list to SelectProductViewModel, while GrandCHildViewB2 will not have Product list in it, so SelectProductViewModel should fetch data from database.

Goran
  • 6,328
  • 6
  • 41
  • 86

2 Answers2

0

I would create a generic viewModel which defines a contract for receiving data.

public abstract class PassDataViewModel<T> : ObservableObject
{
    public T Data { get; }
}

I would then create a more general ViewModel for your product list like so:

public class SelectProductViewModel : PassDataViewModel<Product>
{
    private Product _selectedProduct;
    private ObservableCollection<Product> _products = new ObservableCollection<Product>();

    public SelectProductViewModel(IList<Product> products)
    {
        _selectedProduct = _products.First();
    }

    public IEnumerable<Product> Products
    {
        get { return _products; }
    }

    public Product SelectedProduct
    {
        get { return _selectedProduct; }
        set
        {
            _selectedProduct = value;
            OnPropertyChanged("SelectedProduct");
            OnPropertyChanged("Data");
        }
    }

    public Product Data
    {
        get { return _selectedProduct; }
    }
}

You would use this in the following way:

  1. Your first viewModel can create an instance of the SelectProductViewModel (when a command is invoked, for example)
  2. You pass your products list to the new SelectProductViewModel instance.
  3. Use a DataTemplate to change the view on your screen (this post will show you how to do this).
  4. Have a property in the parent viewModel that returns the product returned from the data property of the SelectProductViewModel (you will need to propagate the PropertyChanged event to your parent viewModel).
Community
  • 1
  • 1
Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100
  • Hi Benjamin, I have a few questions: 1) why do I need both Data and SelectedProduct properties? 2) I have edited a question to provide more info, so can you elaborate more about how would I instantiate SelectProductViewModel and how would I notify interested view that a particular product is selected? – Goran Jun 17 '12 at 23:56
  • 1) you don't. You could get rid of the generic ViewModel and just use the SelectProductViewModel. The only reason I would use the generic ViewModel is so that the parent ViewModel does not have to know about the specific implementation details of the SelectProductViewModel although I will admit it requires a bit more work before it is completeley decoupled. The idea of this was to have a contract for parties that are interested in being notified when 'data' has been selected. 2) How / where you instantiate the ViewModel is up to you. – Benjamin Gale Jun 18 '12 at 07:18
  • The question was not just related how to instantiate a viewmodel, it is about the whole process of instantiating, waiting to do its job, closing, and getting the result. In your example. how would a "first ViewModel" as you called it, know to get the value of a Data property? – Goran Jun 20 '12 at 12:15
  • Raise a property changed event for the data property? – Benjamin Gale Jun 20 '12 at 12:16
  • are you saying that I should keep a reference to the SelectProductViewModel in the "first view"? – Goran Jun 20 '12 at 12:22
  • That is how I handle this requirement. Sorry if i'm being deliberately vague but that is because there is no right or wrong way to handle this. It really depends on the complexity of the application and whether or not you can determine when the child viewModel should be disposed. I can't answer this for you because I don't know enough about your application. This solution worked perfectly for me but may cause problems for someone else. – Benjamin Gale Jun 20 '12 at 12:44
0

the most easy way is to go the viewmodel first approach and use a dialogservice to show the selection view.

your viewmodel with ProductionList simply call the dialogservice and put a ProductSelectionViewmodel with ProductionList as parameter. because this is viewmodel first you have to create a datatemplate so WPF knows how to render your ProductSelectionViewmodel.

here is a link for a simple dialogservice.

btw: in my opinion viewmodel first approach is much easier when doing mvvm.

EDIT:

in your ProductionListViewModel in your SelectProductCommand

 var selectProductViewModel = new SelectProductViewModel(this.ProductionList);
 var result = this.uiDialogService.ShowDialog("Select Product", selectProductViewModel );

 //if result true, simple get the selected product
 this.SelectedProduct = selectProductViewModel.MySelectedProduct;

thats all - simple and easy

Community
  • 1
  • 1
blindmeis
  • 22,175
  • 7
  • 55
  • 74
  • Hi blindmeis, I was going today through your example on the thread you have linked to, and it seems that you are calling an UI View (WindowDialog) from the ViewModel? Is it not a main rule that ViewModel should not know anything about Views, and should not instantiate them directly? – Goran Jun 20 '12 at 12:10