WPF: Clearing Data Context with MVVM Light
I recently ran into an issue with a WPF application that is a continuous operation Kiosk application. The users log in and are presented with a survey. If the Kiosk is ignored for a time period, a timeout occurs and the screen is reset. However, I discovered that when using MVVM light and View Model (context) injection, even though the view is disposed of, the view model is not. This presented itself when a second user was presented the survey after a timeout had occurred, the survey started on page two or wherever the previous user had left off.
I tried multiple scenarios of inheriting IDisposable and disposing of the view manually. I also tried several ways of clearing the view’s internal context. It was after none of my efforts were successful, I realized that the context was being stored for re-use by the ServiceLocator and could not be disposed of by the view.
Wait a minute… Inversion of Control… The view should not be able to control the view model. It’s working exactly as designed.
How does it get there?
Normal MVVM Light injection takes place from a ViewModelLocator class that is declared as a resource in the App.xaml. There is plenty of documentation of MVVM Light framework on their website, so I won’t go into the details of setup. But, in a nutshell, the view has its context injected in the XAML page as an attribute like so:
DataContext="{Binding Source={StaticResource Locator}, Path=SurveyPage}"
The path corresponds to a method in the Locator class which returns an instance of the view model from the service locator if one exists or creates a new one if not present.
public SurveyViewModel SurveyPage { get { return ServiceLocator.Current.GetInstance<SurveyViewModel>(); } }
If an instance doesn’t exist, one is instantiated during the InitializeComponant() method of the view. However, here is where the problem arises. If you dispose or close the view in normal operations, WPF disposes all of its internal resources associated with the view, but the instance of the view model remains in the ServiceLocator container.
How to clear out the view model
After a fair amount of unsuccessful research on the Google box, necessity (panic?) presented a really simple solution. As one would expect, closing the view fires the build in Unloaded event. Here is our jump in point. Our Locator class is a static method, so the simplest solution was to add a method to unregister and re-register the view model. As I was happy to find out, this cleared the model from the container and registered a new version. Since the container did not have an instantiation of my view model after the unregister process, the next time my view ran through the InitializeComponent () method, Bingo! A new view model is instantiated and injected as context for the view. Here is the very simple method I created to remove the view model:
public static void UnRegisterSurveyViewModel() { SimpleIoc.Default.Unregister<SurveyViewModel>(); SimpleIoc.Default.Register<SurveyViewModel>(); } And I simply call it from the unloaded event: private void MyView_Unloaded(object sender, RoutedEventArgs e) { MvvmViewModelLocator.UnRegisterSurveyViewModel(); }