MVVM – Using ContentTemplateSelector in Tab Control View

Requirement

I have a WPF TabView Control and Two Tab Items each containing different Tab Item Contents . I need to express this using MVVM / Binding.

ContentTemplateSelector is the place to go.

In the demo, we would have one TabView with two Tabs, they are

    1. Countries Tab, and
    2. Continents Tab.

Download Sample Project here: https://app.box.com/s/3rtqxpuj6x5eamj0wqrv

Steps

1. Create two Views (user Control) i.e. CountryView.xaml and ContinentsView.xaml, one for each tab item content.
2. Create a ViewModel for the Tab Control, here Tabs represent the TabItem’s

public class MyTabControlViewModel
    {
        public List<MyTabItemModel> Tabs { get; set; }
    }

3.  Create TabItemModel Class

public class MyTabItemModel
    {
        /// <summary>
        /// Tab Header text
        /// </summary>
        public string Header { get; set; }

        /// <summary>
        /// Tab Content 
        /// </summary>
        public IMyTabContentViewModel CurrentMyTabContentViewModel { get; set; }
}

4 .Create the Content Interface and implement the Content Views

public interface IMyTabContentViewModel
    {
    }
    public class CountryTabContentViewModel : IMyTabContentViewModel
    {
    }
    public class ContinentTabContentViewModel : IMyTabContentViewModel
    {
    }

5. Define two Data Templates, one for “Country view ” and another for “Continents view

<DataTemplate x:Key="CountryContentTemplate">
  <View1:CountryView DataContext="{Binding CurrentMyTabContentViewModel}"/>
        </DataTemplate>

<DataTemplate x:Key="ContinentsContentTemplate">
            <View1:ContinentsView DataContext="{Binding CurrentMyTabContentViewModel}"/>
</DataTemplate>

6. Create a Selector Class derived from DataTemplateSelector:

public class MyViewSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            if (((MyTabItemModel)(item)).CurrentMyTabContentViewModel is CountryTabContentViewModel)
            {
                return CountryTemplate;
            }
            return ContintentsTemplate;
        }
        public DataTemplate CountryTemplate {get;set;}

        public DataTemplate ContintentsTemplate{get;set;}
   }

7. Assign the DataTemplates to the appropriate Selector properties

<ViewModel:MyViewSelector x:Key="selector" 
    CountryTemplate="{StaticResource CountryContentTemplate}"
    ContintentsTemplate="{StaticResource ContinentsContentTemplate}" />

8. Map the ContentTemplateSelector property to the Selector. Your TabControl would look like this :

<UserControl x:Class=”TabControlItemTemplateDemo.View.MyTabControl”
             xmlns:ViewModel=”clr-namespace:TabControlItemTemplateDemo”
             xmlns:View1=”clr-namespace:TabControlItemTemplateDemo.View” mc:Ignorable=”d”
             d:DesignHeight=”300″ d:DesignWidth=”300″>
    <UserControl.Resources>
        <DataTemplate x:Key=”MyTabHeaderTemplate”>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width=”*” />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column=”0″ Text=”{Binding Header}” Width=”80″ Height=”25″ FontWeight=”Bold”/>
            </Grid>
        </DataTemplate>
        <DataTemplate x:Key=”CountryContentTemplate”>
            <View1:CountryView DataContext=”{Binding CurrentMyTabContentViewModel}”/>
        </DataTemplate>
        <DataTemplate x:Key=”ContinentsContentTemplate”>
            <View1:ContinentsView DataContext=”{Binding CurrentMyTabContentViewModel}”/>
        </DataTemplate>
        <ViewModel:MyViewSelector x:Key=”selector”
                                           CountryTemplate=”{StaticResource CountryContentTemplate}”
                                           ContintentsTemplate=”{StaticResource ContinentsContentTemplate}” />
    </UserControl.Resources>
    <DockPanel>
        <TabControl ItemsSource=”{Binding Tabs}” TabStripPlacement=”Left”
                    BorderThickness=”0″ Background=”White”
                    ItemTemplate=”{StaticResource MyTabHeaderTemplate}”
                    ContentTemplateSelector=”{StaticResource selector}”>
        </TabControl>
    </DockPanel>
</UserControl>

Download Sample Project here: https://app.box.com/s/3rtqxpuj6x5eamj0wqrv

Advertisements

MVVM – Binding TreeView Item Changed to ICommand

Download Sample Project here: http://www.box.com/s/0e8rokffhp9p0pshb6b6

After browsing the web for more tha an hour there were quite some post which helped me to get a clue on how to bind a Tree View item selected.
All these posts gave me call back, on which I had to fire an event again, and add a subscription to it.

I wanted that call back subscription to be an ICommand, so that it is in sync with MVVM model.
But to bring the Call back into the View model had to go an extra mile.

I had to synchronize two dependency property.

1.  A dependency property to bind to  TreeView Item Selected .
2.  A dependency property to bind an ICommand, to  TreeView Item Selected Changed.

Dependency property to bind the TreeView Item Selected:

You need a dependency property which would hold the Tree View Item Selected value.
This property should be always updated with the New value, so that you would get an call back on SelectedItemChanged when a new item in the tree is selected.

public static readonly DependencyProperty SelectedItemProperty =
                       DependencyProperty.RegisterAttached("SelectedItem",
                                 typeof(object),
                                 typeof(TreeViewHelper),
                                 new UIPropertyMetadata(null, SelectedItemChanged));

Dependency property to bind an ICommand, when an TreeView Item Selected is changed:

You need a second dependency property which you could bind to an ICommand and execute when ever a new item in the treeview is selected.

public static readonly DependencyProperty SelectedItemChangedProperty =
                  DependencyProperty.RegisterAttached("SelectedItemChanged",
                                     typeof(ICommand),
                                     typeof(TreeViewHelper));

Code to fire the ICommand on SelectedItemProperty call back events

private static void SelectedItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    if (!(obj is TreeView) || e.NewValue == null)
        return;

    var view = obj as TreeView;
    view.SelectedItemChanged += (sender, e2) => SetSelectedItem(view, e2.NewValue);

    var command = (ICommand) (view as DependencyObject).GetValue(SelectedItemChangedProperty);
    if (command != null)
    {
        if (command.CanExecute(null))
            command.Execute(new DependencyPropertyEventArgs(e));
    }
}

Here is the xaml code to bind these two dependency properties

<UserControl x:Class="TreeViewItemChangedMvvm.View.MyTreeViewControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:ViewModelUtils="clr-namespace:TreeViewItemChangedMvvm.ViewModelUtils" mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TreeView ItemsSource="{Binding ItemsSource}"  
                  ViewModelUtils:TreeViewHelper.SelectedItem="{Binding CurrSelItem}"  
                  ViewModelUtils:TreeViewHelper.SelectedItemChanged="{Binding MySelItemChgCmd}" />
    </Grid>
</UserControl>

Thanks: http://stackoverflow.com/questions/1000040/selecteditem-in-a-wpf-treeview
Sample Project: http://www.box.com/s/0e8rokffhp9p0pshb6b6