问题
We're using a Tabcontrol to display a number of items with rather expensive content, and the issue we're having is that as you iterate over the tabs (selecting them one by one), the responsiveness of the application becomes slower and slower.
This behavior is unexpected as from what I understand, as the selected tab changes, the previously selected tabs content is unloaded first, so that you're only paying the price for one tabs content at a time.
I've managed to simulate the behaviour with the code below. To reproduce :
- Run the application
- Launch the selected tabs contextmenu (the tabs header contextmenu), it will be responsive
- From left to right, go through each tab, selecting one by one
By the time you reach tab ~10, the responsiveness of its contextmenu is now very laggy, as you click a checkbox, its animation takes a few seconds to run through
<Window x:Class="WpfApplication4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" WindowStartupLocation="CenterScreen"> <Window.Resources> <Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource {x:Type TabItem}}"> <Setter Property="ContextMenu"> <Setter.Value> <ContextMenu> <CheckBox Content="CheckBox" /> <CheckBox Content="CheckBox" /> <CheckBox Content="CheckBox" /> <CheckBox Content="CheckBox" /> <CheckBox Content="CheckBox" /> </ContextMenu> </Setter.Value> </Setter> </Style> </Window.Resources> <TabControl Name="tabControl" />
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); for (int i = 0; i < 25; i++) { CreateTab(); } } void CreateTab() { var itemsControl = new ItemsControl(); for (int i = 0; i < 1000; ++i) { itemsControl.Items.Add(new TextBox()); } tabControl.Items.Add(new TabItem() { Header = string.Format("Tab{0}", tabControl.Items.Count), Content = itemsControl }); } }
回答1:
I am not sure about your complex scenario what you have but for posted sample, issue is not in tabControl but instead in ItemsControl
.
ItemsControl by default does not support UI virtualization, you have to make it UI virtualized i.e. whenever TabItem gets loaded, all UI containers to host items will be created i.e. 1000 items will be created.
You can verify that by replacing ItemsControl
with ListBox
and you can see considerable increase in performance because ListBox by default support UI virtualization and only containers for visible items will be created (may be 100 at a time).
Replace
var itemsControl = new ItemsControl();
with
var itemsControl = new ListBox();
and you will see difference in performance.
In case you want some performance with ItemsControl, you have to make it UI virtualized. Refer to the answer here to make it UI virtualized.
UPDATE
For comment:
The problem is that the application becomes slower and slower as you select different tabs. This is unexpected. Due to each item being unloaded before loading a new item and due to each item having the same content, I'd expect the responsiveness to remain constant.
Yeah you are right that Unloaded
event gets called for content of last selected tab item but it only disconnect ItemsControl from Visual Tree. However its containers remains intact and remains in memory. So, with every switch new containers are getting created in memory. That I guess is fair reason for sluggishness of your application.
That you can verify by hooking onto StatusChanged
event:
itemsControl.ItemContainerGenerator.StatusChanged += (s, e) => { };
You will see that it gets called twice on every switch to new tabItem but doesn't gets called on switch to already visited tabItem.
来源:https://stackoverflow.com/questions/22579798/tabcontrol-becoming-very-laggy