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?
This is a function of the TabControl
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:
- You believe that there would be a significant performance penalty.
- 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. If you still feel like you REALLY don't want this behavior, you can see an example of overriding it here: https://github.com/cefsharp/CefSharp/blob/master/CefSharp.Wpf.Example/Controls/NonReloadingTabControl.cs
However, I would consider this a "smell" for potentially a future performance issue you should spend the time figuring out now, rather than delaying figuring it out.
For #2, you have two options:
- Make sure every property you want preserved (like
IsSelected
, etc) is backed by a ViewModel that preserves that state. - 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/
来源:https://stackoverflow.com/questions/2238072/tabcontrol-disposes-of-controls-on-inactive-tabs