We've been having discussions lately as to what is acceptable to put in the code behind of a view, and what should go in the view model. I seem to be a lone voice, and wanted to hear what others have to say, as I haven't yet heard an argument that explains
why I'm wrong, other than "That's not the way we do it," or "Because it breaks the pattern," neither of which are very compelling technical reasons. I'm keen to do it right, but only for the right reasons.
As an example, suppose you want to create a quote for a customer. On the new quote details window, you click a button to open a PickCustomerWindow, from which you choose a customer, and are taken back to the quote details window with the customer selected. (I know this isn't necessarily the optimal way to do this, but it fits closely with how a lot of our windows work, so please bear with me)
Now, others in the team insist that the "right" way to do this is have the customer list window send out a message with the picked customer, and have the quote details window's view model pick up that message and set the customer. I feel that this obfuscates the code for no apparent benefit (see below for why).
I would prefer to do it as follows. The quote window's view would have code like the following in the event handler for the appropriate button...
If you wanted to simplify this code even more, you could omit the null check and have the view model do that, but I don't think you gain a great deal by that.
In the words of Laurent Bugnion (creator of the MVVM Light Toolkit)...
"Only put in the VM what should be tested by unit tests, shared with other projects, etc. If you have some view-only code, it is perfectly OK to leave it in the view only. If you need to compute some condition deciding if the child window should be opened or not, it is OK to create a method doing the calculation and returning a value, and to have the view call this method."
I would say that my approach fits very well with his words.
Now, I know that people like to keep code out of the view, and with good reason. However, the code above is so simple that there is no reason not to put it in the view, and you end up with a super-simple process that is easy to follow. If you want to know what happens when you click the button, you look at the view's code-behind, and can see the whole story. You can put your cursor on the SetCustomer() method and click "Navigate to" and you are taken directly to the code that deals with the customer.
Writing unit tests against this becomes extremely easy. You simply create a customer entity, pass it to the SetCustomer() method and test whatever property of the view model is supposed to reflect the change. Very easy.
Now, compare this approach with sending a message. Having looked at the quote window's view code to see what window gets opened, you need to go to that window, find out what its view model is called, look in the view model and find out what message is sent out when the customer is picked, find all usages of that message type in the solution, examine each one in turn to find out if it's the one that's relevant to you, and only then can you see what happens to the customer. That's a lot of messing around and a lot of wasted time to follow a simple process. In the code I showed above, you don't even need to look at the PickCustomerWindow or its view model, as you don't need to know what they do. All you need to know is that the window has a Customer property that returns the selected customer (or null if one wasn't selected).
Furthermore, in order to write unit tests against the view model, you either have to simulate sending a message, which is a messy experience, or you have to make the ProcessMessage method public, which exposes something that has no reason to be exposed.
A similar question comes up with using the event-to-command pattern, which I also feel is abused in the name of "doing it right." What is the point in the view sending a command to the view model, so that the view model can raise an event to tell the view to do something? The view already knows what it's supposed to do, so why not just let it do it? Yes, if there is testable code that is involved this needs to go in the view model, but then you just add a public method and allow the view to call that. Please re-read the end of Laurent Bugnion's words above, and you'll see that this is exactly what he suggests. What is the point of complicating the code with events that have no benefit at all?
I'm all in favour of doing it the "right" way, but only when it really is right. A method that obfuscates the code for no reason isn't what I would call right. By contrast, a method than uses clean, simple and easily testable code, that can be understood immediately is defintely what I would call right.
Does anyone have any comment one way or the other?
As an example, suppose you want to create a quote for a customer. On the new quote details window, you click a button to open a PickCustomerWindow, from which you choose a customer, and are taken back to the quote details window with the customer selected. (I know this isn't necessarily the optimal way to do this, but it fits closely with how a lot of our windows work, so please bear with me)
Now, others in the team insist that the "right" way to do this is have the customer list window send out a message with the picked customer, and have the quote details window's view model pick up that message and set the customer. I feel that this obfuscates the code for no apparent benefit (see below for why).
I would prefer to do it as follows. The quote window's view would have code like the following in the event handler for the appropriate button...
private void PickCustomer_Click(object sender, RoutedEventArgs e) { PickCustomerWindow pcw = new PickCustomerWindow(); pcw.Closing += (_, __) => { if (pcw.Customer != null) { ((QuoteDetailsViewModel)DataContext).SetCustomer(pcw.Customer); } }; pcw.ShowDialog(this); }The SetCustomer() method on the QuoteDetailsViewModel class does the same as a ProcessMessage method would do, in that it gets a customer and does whatever the view model needs to do with it. The difference is that the SetCustomer() method can be accessed directly by the view that opened the PickCustomerWindow.
If you wanted to simplify this code even more, you could omit the null check and have the view model do that, but I don't think you gain a great deal by that.
In the words of Laurent Bugnion (creator of the MVVM Light Toolkit)...
"Only put in the VM what should be tested by unit tests, shared with other projects, etc. If you have some view-only code, it is perfectly OK to leave it in the view only. If you need to compute some condition deciding if the child window should be opened or not, it is OK to create a method doing the calculation and returning a value, and to have the view call this method."
I would say that my approach fits very well with his words.
Now, I know that people like to keep code out of the view, and with good reason. However, the code above is so simple that there is no reason not to put it in the view, and you end up with a super-simple process that is easy to follow. If you want to know what happens when you click the button, you look at the view's code-behind, and can see the whole story. You can put your cursor on the SetCustomer() method and click "Navigate to" and you are taken directly to the code that deals with the customer.
Writing unit tests against this becomes extremely easy. You simply create a customer entity, pass it to the SetCustomer() method and test whatever property of the view model is supposed to reflect the change. Very easy.
Now, compare this approach with sending a message. Having looked at the quote window's view code to see what window gets opened, you need to go to that window, find out what its view model is called, look in the view model and find out what message is sent out when the customer is picked, find all usages of that message type in the solution, examine each one in turn to find out if it's the one that's relevant to you, and only then can you see what happens to the customer. That's a lot of messing around and a lot of wasted time to follow a simple process. In the code I showed above, you don't even need to look at the PickCustomerWindow or its view model, as you don't need to know what they do. All you need to know is that the window has a Customer property that returns the selected customer (or null if one wasn't selected).
Furthermore, in order to write unit tests against the view model, you either have to simulate sending a message, which is a messy experience, or you have to make the ProcessMessage method public, which exposes something that has no reason to be exposed.
A similar question comes up with using the event-to-command pattern, which I also feel is abused in the name of "doing it right." What is the point in the view sending a command to the view model, so that the view model can raise an event to tell the view to do something? The view already knows what it's supposed to do, so why not just let it do it? Yes, if there is testable code that is involved this needs to go in the view model, but then you just add a public method and allow the view to call that. Please re-read the end of Laurent Bugnion's words above, and you'll see that this is exactly what he suggests. What is the point of complicating the code with events that have no benefit at all?
I'm all in favour of doing it the "right" way, but only when it really is right. A method that obfuscates the code for no reason isn't what I would call right. By contrast, a method than uses clean, simple and easily testable code, that can be understood immediately is defintely what I would call right.
Does anyone have any comment one way or the other?
FREE custom controls for Lightswitch! A collection of useful controls for Lightswitch developers (Silverlight client only). Download from the Visual Studio Gallery.
If you're really bored, you could read about my experiments with .NET and some of Microsoft's newer technologies athttp://dotnetwhatnot.pixata.co.uk/