WPF MVVM: How to load views “on demand” without using plug-in architecture

mvvmwpf

I have an Outlook-like WPF UI with navigation on the left, a toolbar on top, and a status bar at the bottom, all within a DockPanel.

The "main" area of the application is within a DataGrid wich is the LastChild and therefore fills the remaining space and it currently looks like this:

<Grid DockPanel.Dock="Right">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition />
    </Grid.RowDefinitions>
    <views:View1 Grid.Column="0" Grid.Row="0"/>
    <views:View2 Grid.Column="0" Grid.Row="0"/>
    <views:View3 Grid.Column="0" Grid.Row="0"/>
    <views:View4 Grid.Column="0" Grid.Row="0"/>
</Grid>

These views are all user controls that have their own ViewModels with Visibility property bound to a property in its ViewModel and controlled by the navigation pane selections. When a navigation pane item is selected the main ViewModel sends a message to ViewModels of the views and upon receiving the message the ViewModels of the views set their Visibility properties accordingly…

Now, this works fine but doesn't feel right since all the ViewModels of these views are being instantiated on the app start instead of when the related navigation pane is selected bringing up questions about performance and memory usage…

My question is if there is a way of loading each view and its ViewModel "on demand", when the related navigation pane is selected and unloading these views and ViewModels when a different navigation pane is selected without resorting to PRISM/MEF or some other plugin architecture… and, more general how would you approach building an application like this with multiple views/ViewModels "embedded" within the main view?

Best Answer

Here's what I do:

First, on your main ViewModel (the one that's servicing the main window), add a Content property:

object _content;
public object Content
{
    get { return _content; }
    set
    {
        _content = value;
        RaisePropertyChanged("Content");
    }
}

When the user switches from view to view, set the Content property to the appropriate ViewModel for that view they've selected.

In the main window, then, you can display the current Content ViewModel using a ContentControl:

<ContentControl Content="{Binding Content}" />

... and somewhere above that, define a DataTemplate for each of the various ViewModels that Content might be set to:

<DataTemplate DataType="{x:Type vm:FooViewModel}">
    <my:FooUserControl />
</DataTemplate>

<DataTemplate DataType="{x:Type vm:BarViewModel}">
    <my:BarUserControl />
</DataTemplate>

So now the ContentControl will be responsible for initializing and displaying each of your UserControls depending on which ViewModel is currently "active".

Related Topic