views:

673

answers:

1

I'm working in WPF using the MVVM pattern, and generally things seem to be working pretty well, as I wrap my brain around the nuances of both WPF and MVVM. However, I'm currently stuck on a particular issue. My actual application is fairly complex, so to simplify, let's take Josh Smith's near-defining article on the pattern and use the application therein.

Consider Figure 2, and imagine that the user has typed some stuff in the first and last name fields. Then the user clicks away from the workspace (viewmodel) entirely, either by clicking a different customer tab, or possibly a completely different viewmodel in the same application. In this case, what I'd like to have happen is for the application to ask "Hey, did you want to save your changes? Yes/No/Cancel" and respond appropriately. This has presented... challenges.

Because I'd like the user to be able to 'cancel' that first-pass suggests needing PreviewLostKeyboardFocus (since I could set Handled=true and cancel the focus shift). However, several user actions (such as clicking the tab of a different workspace) don't shift keyboard focus. LostFocus covers me better in that respect, but that's only after the focus has already been lost (though of course I could switch it back?) and there are issues with determining if the event was from the view itself (i.e., we're leaving the whole view) or if it's simply bubbled up from some contained object.

And big picture on all this - this seems to be an issue for the view, but then that implies writing code in the view rather than the magic viewmodel. Which makes me think i'm not looking at this correctly.

So I'm hoping I'm missing some big conceptual a-ha that will make all this clear. Help?

A: 

You need to concentrate on your model rather than your view. That is, what does change that should trigger your logic? In this case, I'd say it's when an attempt is made to change the active tab.

So you need an overarching view model whose responsibilities are:

  • Expose a collection of all sub view models (each of which appears in its own tab)
  • Track the active (selected) sub view model (ie. the active tab)

Your view would bind to these properties in the usual way:

<TabControl ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab}"/>

The SelectedTab property would apply your logic as follows:

  1. Is the current tab dirty?
  2. If so, prompt the user via a service
  3. If the user cancels, don't change the active tab
  4. If the user saves or discards changes, then change the active tab

I think the key thing you're missing is the overarching view model. Working your way through my ActiveAwareCommand sample project may help increase your understanding.

HTH, Kent

Kent Boogaart