Thinking with MVVM: Data Templates + ContentControl

I’m still playing with MVVM at work and I got a new occasion to setup a dialog using the MVVM methodology.

I really love the process of creating User Interface using MVVM, the process is so much natural ! Basically, I needed to create a “setup” dialog in the application I’m currently working on. I wanted to achieve something like this:

setup-dialog

Of course, I wanted to create this dialog using the power of WPF. My primary objective when I create a new dialog is to keep the code-behind empty. Using MVVM it took me about 10 minutes to create the basic structure of the dialog withouth writting a single line of code in the xaml.cs files.

Here is the process I followed to create this dialog. This is the “thinking with MVVM” part because I’m explaining the reasoning I had :-) You can download the source code for this example here.

Setup the main dialog

When I need quick prototypes, I often don’t use Blend and type the XAML directly into Visual Studio editor. That was the very first part I did to write this XAML:

1
2
3
4
5
6
7
8
9
10
11
12
  <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
 
        <ListBox Grid.Column="0" Margin="5"/>
 
        <Border Grid.Column="1" Margin="5" BorderBrush="#FF7F9DB9" BorderThickness="1">
            <!-- Content goes here -->
        </Border>
    </Grid>

Pretty easy isn’t it. Please note the “Content goes here” comment. I knew I wanted to put the settings dialogs here, but I didn’t know how… I named this file ConfigurationDialog.xaml.

Create sub-configuration dialogs

For this example, I created 2 sub-configuration dialogs just for the demo. I just put a TextBlock into a Grid to demonstrate the principles. Of course, we should add real configuration controls such as CheckBox, TextBox, etc. Those 2 new files are GeneralSettingsView and AdvancedSettingsView and are UserControls.

Create a ViewModel class for each View

I used to use an abstract base class for all my ViewModel classes where I implement the INofityPropertyChanged interface. I also use a trick from Josh Smith to throw an exception if the property name doesn’t exist when the PropertyChanged event is raised.

That gives us 3 new files: ConfigurationDialogViewModel, GeneralSettingsViewModel and AdvancedSettingsViewModel. Because I wanted configuration dialogs to have a common Name property (to identify them in the UI), I created a base class SettingsViewModelBase.

Setup the ViewModel of the main View

I needed the main View to expose the other configuration views available. I used an ObservableCollection for that:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ConfigurationDialogViewModel : ViewModelBase
{
        private readonly ObservableCollection<SettingsViewModelBase> settings;
 
        public ObservableCollection<SettingsViewModelBase> Settings
        {
            get { return this.settings; }
        }
 
        public ConfigurationDialogViewModel()
        {
            this.settings = new ObservableCollection<SettingsViewModelBase>();
 
            this.settings.Add(new GeneralSettingsViewModel());
            this.settings.Add(new AdvancedSettingsViewModel());
        }
}

Setup the main View  to consume datas from its ViewModel

I did some changes in the XAML to databind the ListBox’s ItemsSource property to the ViewModel. Because I always set the DataContext property of a view to its associated ViewModel, there is no ambiguity:

1
ItemsSource="{Binding Settings}".

The ListBox control has no idea how a SettingsViewModelBase object (that is in the associated ObservableCollection) should be displayed. We need a simple DataTemplate to specify this information:

1
2
3
4
5
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" Padding="10"/>
                </DataTemplate>
            </ListBox.ItemTemplate>

And finish using my favourite part !

We just setup a ListBox to consume a collection of SettingsViewModelBase class to build a menu, fine. By using a very simple DataTemplate, we said: “I want to render the SettingsViewModelBase object in the ListBox using a TextBlock”.

We can take this reasoning one step further. We have a View associated to each SettingsViewModelBase. ListBox was fine to have a collection of controls. How could we display only one control ?

We can use a ContentControl of course ! Because we want do display the dialog that is currently selected in the menu, we can use a simple DataBinding (here, the ListBox has been named ListBoxMenu):

1
<ContentControl Content="{Binding ElementName=ListBoxMenu, Path=SelectedItem}"/>

One more time, we got a rendering problem. The ContentControl doens’t know how to render a SettingsViewModelBase object. Well, it’s not a big deal, just specify it:

1
2
3
4
5
6
7
8
    <Window.Resources>
        <DataTemplate DataType="{x:Type ViewModel:GeneralSettingsViewModel}">
            <View:GeneralSettingsView/>
        </DataTemplate
        <DataTemplate DataType="{x:Type ViewModel:AdvancedSettingsViewModel}">
            <View:AdvancedSettingsView/>
        </DataTemplate>
    </Window.Resources>

What I’m saying here is that GeneralSettingsViewModel should be rendered using a GeneralSettingsView. That’s exactly what we need ! Because the Views are created using a DataTemplate, we do not need to setup the DataContext, it will be automatically registered to the templated object, the ViewModel.

Conclusion

The example I describe here is very simple. The goal was to show the process of creating a dialog thinking in the “MVVM” way. It was very natural to use the concepts I explain here. The code-behinds are completely empty and ViewModel classes are testable ! Because the View are XAML only, I can give them to a designer that will tweak them to make them have a nice look.

You can download the source code for this example here.

14 thoughts on “Thinking with MVVM: Data Templates + ContentControl

  1. Simple et clair…
    Je commence juste à utiliser MVVM, et c’est vrai que quand on s’y met ça devient vite assez naturel. J’avais encore quelques doutes sur la manière de présenter le contenu, et l’utilisation de ContentControl et DataTemplate règle mon problème… merci !

  2. Jeremy,

    I see that you are using a very similar approach to one described by Josh Smith in his MSDN article (http://msdn.microsoft.com/en-us/magazine/dd419663.aspx). In your application (and in Josh’s sample as well) the views are rendered as templates for view models. This is an elegant approach, but not without its drawbacks.

    One issue I have is that the view is recreated each time you switch between sections of your settings dialog (and each time you switch between tabs in Josh’s example). If the view is not trivial it may take some time for the runtime to render it. You will also lose your UI state (if you’ve scrolled down the list you’ll be back at row 1).

    I wonder if you have any ideas on how to make the views stay alive when switching between sections of your dialog?

    Thanks,

    Mike

  3. Michael,

    I’ve been discussing this issue with Josh. Apparently, there is no simple workaround. Using DataTemplate-driven approach has its pros and cons… If your views are too expensive to throw them away and recreate them, you should try another approach (more “classical”).

  4. Hello Jeremy,

    how would you your sample project work if you would use buttons in a stackpanel instead of a listbox? A button has no SelectedmItem property… so how would you use the button click? event to show the appropiate usercontrol in the contentcontrol?

  5. I think you could adapt the code by attaching a command to the Button. When this command gets executed, update a property that is databound to the ContentControl. In this case,you would create your own “SelectedItem” property since you don’t have it if you’re not using a ListBox. HTH

  6. Hello Jeremy,

    and what data/datatype is assigned to that SelectedItem property? I have 10 ViewModels, CustomerViewModel,ProductViewModel,… I can not make 10 SelectedItem properties in the MainViewModel. So what datatype has the return value of the SelectedItem property if I assign 10 different ViewModel instances…? You get me?

    Help is really appreciated, like your blog although you could write more ;-)

  7. Thank you for this nice example of Configuration Dialog – it was really helpful but I have still one more question: how can I pass my DataContext from main config window (app settings) to ContentControl that every single SettingsView will be able to use it and bind it directly? For example I want to achieve something like this:

    —- ConfigurationDialog.xaml

    xmlns:properties=”clr-namespace:ConfigurationDialogExample.Properties”


    —- GeneralSettingsView.xaml

    MyNiceColor is of course one of base app settings from “Properties.Settings.Default”. The problem is that the ContentControl doesn’t pass corectly new DataContext and as I have seen every child control from that ContentControl has only data from e.g. GeneralSettingsViewModel.cs that is the current ViewModel which is binded its “Content” property.

    Although if I pass DataContext during creating a UserControl object (e.g.

    ) everything works fine, but it’s little bit not elegant solution I think, because I must repeat this for every UserControl (SettingsView) in ConfigurationWindow.

    Any suggestions will be appreciated :)

  8. It has been a while since you published this entry, but it helped me solve a problem that I was having when using triggers to select a data template when a list selection was changed. It mostly worked, but the combobox controls in the templates would not initialize their selected item from the bound object in the viewmodel. I had wanted to bind to an enum. When that did not work I changed it to a list of strings. That did not work so I changed it to a list of objects. The objects implemented IEquatable to allow the correct comparison.

    When I temporarily moved the controls out of the data template and put them directly into the view, they worked, so I know I basically had things right.

    Finally, when I refactored based on your example, everything worked, and I am able to replace one set of controls with another set, as the selected item changes.

    Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>