I have an application which shows a single View at a time in a ContentControl. I have a current solution, but was curious if there is a better one for memory management.
My current design creates new objects when they need to be displayed, and destroys them when they are no longer visible. I'm curious if this is the better approach, or maintaining references to each view and swapping between those references is better?
Here is a little more explanation of my application layout:
A very simplified version of my MainWindow.xaml looks like this:
<Window ... >
<Window.Resources>
<DataTemplate DataType="{x:Type vm:SplashViewModel}">
<view:SplashView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MediaPlayerViewModel}">
<view:MediaPlayerView />
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding ActiveModule}" />
</Grid>
</Window>
In my MainViewModel.cs I swap the ActiveModule parameter with a newly initialized ViewModels. For example, my pseudo-code logic check for swapping content would be something like:
if (logicCheck == "SlideShow")
ActiveModule = new SlideShowViewModel();
else if (logicCheck == "MediaPlayer")
ActiveModule = new MediaPlayerViewModel();
else
ActiveModule = new SplashScreenViewModel();
But, would just maintaining a reference be more appropriate in speed and memory usage?
Alt Option 1: Create static references to each ViewModel and swap between them...
private static ViewModelBase _slideShow = new SlideShowViewModel();
private static ViewModelBase _mediaPlayer = new MediaPlayerViewModel();
private static ViewModelBase _splashView = new SplashScreenViewModel();
private void SwitchModule(string logicCheck) {
if (logicCheck == "SlideShow")
ActiveModule = _slideShow;
else if (logicCheck == "MediaPlayer")
ActiveModule = _mediaPlayer;
else
ActiveModule = _splashView;
}
I'm not constantly creating/destroying here, but this approach appears to me to be wasteful of memory with unused modules just hanging out. Or... is there something special WPF is doing behind the scenes that avoids this?
Alt Option 2: Place each available module in the XAML and show/hide them there:
<Window ... >
<Grid>
<view:SplashScreenView Visibility="Visible" />
<view:MediaPlayerView Visibility="Collapsed" />
<view:SlideShowView Visibility="Collapsed" />
</Grid>
</Window>
Again, I'm curious about what memory management might be happening in the background that I'm not familiar with. When I collapse something, does it go fully into a sort of hibernation? I've read that some stuff does (no hittesting, events, key inputs, focus, ...) but what about animations and other stuff?
Thanks for any input!
I ran into that kind of situation once where my Views were pretty expensive to create, so I wanted to store them in memory to avoid having to re-create them anytime the user switched back and forth.
My end solution was to reuse an extended
TabControl
that I use to accomplish the same behavior (stop WPF from destroying TabItems when switching tabs), which stores theContentPresenter
when you switch tabs, and reloads it if possible when you switch back.The only thing I needed to change was I had to overwrite the
TabControl.Template
so the only thing displaying was the actualSelectedItem
part of the TabControlMy XAML ends up looking something like this:
and the actual code for the extended
TabControl
looks like this:Also I'm not positive, but I think my blank TabControl template looked something like this:
Another option to consider: Is this a scenario for using something like an IoC container and a Dependency Injection framework? Often times the DI framework supports container managed lifetimes for objects. I suggest looking at Unity Application Block or at MEF if they strike your fancy.
You could choose to continue with your current approach; provided :
One of the disadvantages of keeping viewmodels cached in memory is their binding. If you have to stop binding notifications to flow between view and viewmodel when view goes out of scope, then set viewmodel to null. If viewmodel construction is light weighted, you can quickly construct the viewmodels and assign back to view datacontext.
You can then cache the views as shown in approach 2. I believe there is no point constructing view repeatedly if viewmodels are plugged with proper data. When you set viewmodel to null, due to binding datacontext view will get all bindings cleanedup. Later on setting new viewmodel as datacontext, view will load with new data.
Note: Make sure viewmodels get disposed properly without memory leaks. Use SOS.DLLto keep check on viewmodel instance count through visual studio debugging.