tags:

views:

251

answers:

1

I'm using the MVVM pattern for my app. The MainWindow comprises a TabControl with the DataContext mapped to the ViewModel:

<Window.Resources>
  <ResourceDictionary>
    <DataTemplate x:Key="templateMainTabControl">
      <ContentPresenter Content="{Binding Path=DisplayName}" />
    </DataTemplate>

    <local:ViewModel x:Key="VM" />
    <local:WorkspaceSelector x:Key="WorkspaceSelector" />
    <local:TabOneView x:Key="TabOneView" />
    <local:TabTableView x:Key="TabTableView" />

    <DataTemplate x:Key="TabOne">
      <local:TabOneView />
    </DataTemplate>

    <DataTemplate x:Key="TabTable">
      <local:TabTableView />
    </DataTemplate>

  </ResourceDictionary>
</Window.Resources>


<TabControl Grid.Row="0"
            DataContext="{StaticResource VM}"
            ItemsSource="{Binding Workspaces}"
            SelectedItem="{Binding SelectedWorkspace}"
            ItemTemplate="{StaticResource templateMainTabControl}"
            ContentTemplateSelector="{StaticResource WorkspaceSelector}" />

The WorkspaceSelector looks like:

public class WorkspaceSelector : DataTemplateSelector
{
  public override DataTemplate SelectTemplate( object item, DependencyObject container )
  {
    Window win = Application.Current.MainWindow;
    Workspace w = ( Workspace ) item;
    string key = w.DisplayName.Replace( " ", "" );
    if ( key != "TabOne" )
    {
      key = "TabTable";
    }
    return win.FindResource( key ) as DataTemplate;
  }
}

so that TabOne returns the DataTemplate TabOne and the other two tabs return the DataTemplate TabTable.

If I run the application and click on each of the tabs twice (1, 2, 3, 1, 2, 3) I don't get what I expect, which is


TabOne's view is created
TabTwo's view is created
TabOne's View is created
TabTwo's view is created

that is, if the TemplateSelector returns a different value, the existing tab's controls are thrown away and the new tab's control's are created, and if the TemplateSelector returns the same value, nothing happens.

This is exactly what I don't want! I'd like the TabControl to keep all the controls on the tabs, and I would like to be able to do something about creating different controls in code for the case where I go from TabTwo to TabThree. I can live without the latter. But how do I tell the TabControl not to throw away each tab's controls when it's not selected?

+1  A: 

This is a function of the tab control and is the default behavior.

Basically, to save memory, the TabControl unloads the visual tree that is in its content area and replaces it with a newly crufted up one for the new tab. To prove this to yourself, you can listen to the Unload event on each control you template in and notice that it fires every time you switch tabs.

There are likely 2 reasons you would want to override this behavior:

  1. You believe that there would be a significant performance penalty.
  2. You are losing the state of the controls because any visual state that is being lost is not backed by a ViewModel.

As for #1, you shouldn't be concerned. CPU time is generally cheaper than RAM and the default behavior leans on the cheaper side of the resource equation.

For #2, you have two options:

  1. Make sure every property you want preserved (like IsSelected, etc) is backed by a ViewModel that preserves that state.
  2. Create a persistent UserControl for each tab that you bind to, rather than to ViewModels (Workspaces in your case). There is an example of that in the "Writer" sample for WAF: http://waf.codeplex.com/
Anderson Imes
Thanks Anderson - it was reason 1 (it just feels expensive). I'll go with the flow. --Amaca
amaca