2
votes

Let's say I have a Customer Window showing information about a customer, like Name, address and phone number. On the bottom there is a DataGrid of their orders. Of course the Customer has an Orders property, so if you're using MVVM, you would just set:

ItemsSource = "{Binding Customer.Orders}"

However, now let's say that the data grid is now part of a user control, which also includes controls for editing/adding/removing Orders. I want to use this same set of controls in multiple places, and I would like all the logic for editing/adding/removing Order objects to be encapsulated in the user control. And because I want to use commands, rather than event handlers, I would like the user control to have its own view model.

So now the question is: how do I pass the orders from the Customer view model to the Orders user control's view model? Because the Orders user control will be bound to a view model, I can't say:

<local:OrdersUserControl DataContext="{Binding Customer.Orders}" />

because the user control has it's own view model. It would expect to see Customer.Orders there, and of course it's not.

I guess this is kind of a chicken or the egg situation.

Your help is always appreciated.

Aaron

2

2 Answers

8
votes

And now for my weekly "don't do that" answer...

I can't say ... because the user control has it's own view model.

To which I say

Creating a ViewModel for your UserControl is a code smell.

You're experiencing this issue because of that smell, and it should be an indication that you're doing something wrong.

The solution is to ditch the VM built for the UserControl. If it contains business logic, it should be moved to an appropriate location in another ViewModel.

You should think of a UserControl as nothing more than a more complex control. Does the TextBox have its own ViewModel? No. You bind your VM's property to the Text property of the control, and the control shows your text in its UI.

MVVM doesn't mean no codebehind. Put your UI logic for your user control in the codebehind. If it is so complex that you need business logic inside the user control, that suggests it is too encompassing. Break it down into two or more.

Think of UserControls in MVVM like this--For each model, you have a UserControl, and it is designed to present the data in that model to the user. You can use it anywhere you want to show the user that model. Does it need a button? Expose an ICommand property on your UserControl and let your business logic bind to it. Does your business logic need to know something going on inside? Add a routed event.

Normally, in WPF, if you find yourself asking why it hurts to do something, it's because you shouldn't do it.

0
votes

There are many ways to skin a cat. The way I've been doing this is by having my CustomerViewModel have a property of type OrdersViewModel. Then in the constructor (or wherever you are setting the customer) have it set the "OrdersContext" with a new OrdersViewModel passing in the customer.orders.

XAML:

<local:OrdersUserControl DataContext="{Binding OrdersContext}" />

ViewModel:

public CustomerViewModel(Customer customer)
{
    Customer = customer;
    OrdersContext = new OrdersViewModel(customer.Orders);
}