(moving to public group)
This topic is on my todo list for my ItemsControl series. I just need to find some blog time.
I have repeatedly brought this issue up with Microsoft (including last week at the summit). I understand why they chose to implement it this way, but its not practical for most TabControl scenarios. I want them to add an option to cache the tab items.
The perf hit is during the building of the tree. Unfortunately, if you're using a typical MVVM approach with a binding on the ItemsSource property of the TabControl, the entire tree must be rebuilt each time a tab item is selected. This is usually a very expensive operation.
To be clear, I don't mind that they unload/reload the items each time. I just don't want the entire tree discarded. By doing this, all state associated with the visuals is lost. Maintaining that state in the VM is usually an unreasonable nightmare.
The discarding of items is really only a problem when using a TabControl in ItemsSource mode. In Direct mode, the visuals will still be unloaded from the tree, but they won't be discarded. (See 'C' is for Collection for a description of Direct vs ItemsSource.)
This means you can solve the problem by creating your own TabItem objects and manually adding them to the TabControl's Items collection. Yes, this adds a bunch of overhead to the view (which is why I wish Microsoft would solve the problem in the platform), but if done right, it still allows an MVVM approach. You just don't get to use an ItemsSource binding on the TabControl.
Below is what my view code typically looks like for such an approach:
private void OnLoaded(object sender, RoutedEventArgs e)
{
// set initial tabs
foreach (TabViewModel view in
TabsCollection)
{
AddTabItem(view);
}
// monitor tabs collection for changes so that tabs can be updated
appropriately
TabsCollection.CollectionChanged += new
NotifyCollectionChangedEventHandler(OnViewsCollectionChanged);
}
private void OnViewsCollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach
(TabViewModel view in e.NewItems)
{
AddTabItem(view);
}
break;
case
NotifyCollectionChangedAction.Remove:
foreach (TabViewModel
view in e.OldItems)
{
RemoveTabItem(view);
}
break;
case NotifyCollectionChangedAction.Reset:
_tabControl.Items.Clear();
foreach (TabViewModel view in
e.NewItems)
{
AddTabItem(view);
}
break;
}
}
private void
AddTabItem(TabViewModel view)
{
TabItem item = new TabItem();
item.DataContext = view;
item.Content = new ContentControl();
(item.Content as ContentControl).Focusable = false;
(item.Content
as ContentControl).SetBinding(ContentControl.ContentProperty, new Binding());
_tabControl.Items.Add(item);
}
private void
RemoveTabItem(TabViewModel view)
{
TabItem foundItem = null;
foreach (TabItem tabItem in _tabControl.Items)
{
if
(tabItem.DataContext == view)
{
foundItem =
tabItem;
break;
}
}
if
(foundItem != null)
{
_tabControl.Items.Remove(foundItem);
}
}
Quoting
"wpf-discip...@googlegroups.com"
<wpf-discip...@googlegroups.com>:
>
> Yeah
I am thinking that may have to be the way of things.
>
>
>
> Subject: RE: Question for the group
> Date: Thu, 12
Mar 2009 08:54:33 -0700
> From: ka...@littlerichie.com
> To:
wpf-discip...@googlegroups.com
>
>
>
>
>
>
>
> Sacha,
>
> You
can always roll your own, using an ItemsControl. I've actually
>
thought about doing this.
>
> As for the TabControl, Josh is
right on. It Unloads and Loads based
> on the selected tab.
>
> Cheers,
>
> k-dawg (still feeling the MVP
flu)
>
>
>
> From:
wpf-discip...@googlegroups.com on behalf of Josh Smith
> Sent:
Thu 3/12/2009 8:39 AM
> To: wpf-discip...@googlegroups.com
> Subject: Re: Question for the group
>
>
> It
virtualizes, but not in the typical sense of UI virtualization.
>
The elements living in a TabItem are unloaded when that item is
>
deselected. When the item is selected again, the UI elements in it
>
are loaded back up. Basically, the visual tree of TabControl only
>
includes the elements within the selected TabItem. As far as I
>
know, you cannot tell it not to do that. What is the exact problem
>
you're trying to solve?
>
> Josh
>
>
>
On Thu, Mar 12, 2009 at 11:33 AM, Sacha Barber
>
<sacha...@hotmail.com> wrote:
>
>
> Hello wise
group members
>
>
> I have a small question for the
group about TabControl in WPF.
>
> We are experiencing a small
lag with an app I am working on, where
> we are using Tabs where our
content panes are new TabItems. All cool.
>
> The question I
have is this
>
> Does the WPF TabControl Virtualize its
children (I think it does),
> where by only the current tab is shown
and all other tabs are not
> created until they need to be shown
again, via the selected tab
> changing.
>
> Is this
the case. And if this is the case is there a way to stop the
>
TabControl virtualizing. Can I simply use the
>
VirtualizingStackPanel.IsVirtualizing or one of the new Recycle modes.
>
> What is the best option?
>
> I guess if I
instuct the TabControl to not Virtualize, more memory
> will be used,
but the ayout may be quicker, as it is not having to
> recreate the
content each time for the newly selected TabItem.
>
> Any
ideas/thoughts?
>
>
>
> Windows Live Hotmail
just got better. Find out more!
>
>
>
>
>
>
_________________________________________________________________
> All
your Twitter and other social updates in one place
> http://clk.atdmt.com/UKM/go/137984870/direct/01/
>
/>>
>
Dr. WPF - Online Office at http://www.drwpf.com/blog/
I’m here waiting on queue… J (BTW: Great explanation doc!)
From: wpf-di...@googlegroups.com [mailto:wpf-di...@googlegroups.com] On Behalf Of Josh Smith
Sent: giovedì 12 marzo 2009 19:14
To: wpf-di...@googlegroups.com
Subject: [WPF Disciples] Re: Question for the group
The world awaits that blog post with bated breath. :)
On Thu, Mar 12, 2009 at 2:12 PM, <a...@drwpf.com> wrote: