Search code examples
c#wpftabsuser-controlscontenttemplate

TabControl - Keeping UserControl TabItems in Memory When Switching Tabs


I had a tab control where each tab item was a custom made user control. The issue there was whenever the program would launch, each individual control would be initialized and loaded. Then, when switching to the tab, it would have to reload again.

I've since changed how the tab items are loaded as shown below. This prevents the user controls from initializing and loading until they're clicked on (which is preferred):

<TabControl>
  <TabItem>
      <TabItem.ContentTemplate>
          <DataTemplate>
              <local:ctlHome />
          </DataTemplate>
      </TabItem.ContentTemplate>
  </TabItem>
  <TabItem>
      <TabItem.ContentTemplate>
          <DataTemplate>
              <local:ctlTwo />
          </DataTemplate>
      </TabItem.ContentTemplate>
  </TabItem>
  ...
</TabControl>

When I first rolled this out, every time I would switch to a new tab, it would call the constructor -> Unload the previous tab -> Load the new tab, which is great. It would only call the constructor once, and whenever I re-selected the tab, it would just unload -> load as expected.

Now, every time I select a tab, it calls the constructor and re-initializes the entire control. I'm not sure why it's no longer keeping the tab in memory.

Is there any way I can keep the user controls in memory once they've initialized using this method?


Solution

  • If you use a DataTemplate with a ContentControl, only the initially visible control gets loaded:

    <TabControl xmlns:s="clr-namespace:System;assembly=mscorlib">
        <TabControl.Items>
            <s:String>home</s:String>
            <s:String>two</s:String>
        </TabControl.Items>
        <TabControl.ContentTemplate>
            <DataTemplate>
                <ContentControl>
                    <ContentControl.Style>
                        <Style TargetType="ContentControl">
                            <Setter Property="Content">
                                <Setter.Value>
                                    <local:ctlHome />
                                </Setter.Value>
                            </Setter>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding}" Value="two">
                                    <Setter Property="Content">
                                        <Setter.Value>
                                            <local:ctlTwo />
                                        </Setter.Value>
                                    </Setter>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ContentControl.Style>
                </ContentControl>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
    

    Both controls get initialized immediately though, but this shouldn't be an issue since you should not perform anything heavy in the constructors. Move any initialization logic to the Loaded event handler or similar.