I have a tab control where the tabs are created at runtime. The contents of the tabs will be one of several user controls, each containing hundreds of other controls. As it takes a long time to create these user controls, I'm trying to find a way to reuse them rather than creating a new instance for each tab.
I'm setting the tab page contents using a DataTemplate as follows:
<DataTemplate>
<ScrollViewer Content="{Binding Content}" />
</DataTemplate>
Where Content is the view model for the view I want to show in the tab.
Elsewhere I use data templates to map each view model to a view, e.g.
<DataTemplate DataType="{x:Type vm:MyViewModel1}">
<ctl:CacheContentControl ContentType="{x:Type ctl:MyView1}" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MyViewModel2}">
<ctl:CacheContentControl ContentType="{x:Type ctl:MyView2}" />
</DataTemplate>
CacheContentControl is a wrapper around ContentControl that I use for the caching:
public class CacheContentControl : ContentControl
{
private static Dictionary<Type, Control> cache = new Dictionary<Type, Control>();
public CacheContentControl()
{
Unloaded += CacheContentControl_Unloaded;
}
private void CacheContentControl_Unloaded(object sender, RoutedEventArgs e)
{
Content = null;
}
private Type _contentType;
public Type ContentType
{
get { return _contentType; }
set
{
_contentType = value;
Content = GetView(_contentType);
}
}
public Control GetView(Type type)
{
if (!cache.ContainsKey(type))
{
cache.Add(type, (Control)Activator.CreateInstance(type));
}
return cache[type];
}
}
This ensures that the DataTemplate first checks the cache to see if it can reuse a control before creating a new instance.
This works for any new tabs that are created. The first tab is slow as expected since it needs to create the initial control, but all subsequent tabs using the same control load almost immediately. The problem I'm having is that when I click back to a previous tab the control no longer appears and the tab is blank.
I guess this is happening because I can't show the same control instance on the same form multiple times which makes sense. I've been able to work around it by handing the IsVisibleChanged event for my CacheContentControl as follows:
private void CacheContentControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (IsVisible && ContentType != null)
{
Control ctl = GetView(_contentType);
ctl.DataContext = DataContext;
Content = ctl;
}
else
{
Content = null;
}
}
When a tab loses focus it removes the control, the tab that receives focus can then retrieve the control from the cache which seems to work.
The issue is that the speeds have gone back to being slow again and I'm not sure why. Obviously it's possible to move a control instance from one tab to another without delay as it does this every time I create a new tab. There must be something the DataTemplate does differently to change the control's parent?