I am in a bind (no pun intended) when it comes to my current binding needs in WPF. I have spent the better part of the day trying to research my problem and I cannot find a solid solution to my problem. here it is:
I am trying to create a User Control that represents what I am calling a workspace (a reference from a blog by Josh Smith). The workspaces will be displayed in a tab control. I am aiming to use a tabbed interface to manage various documents that I have open much like in a browser of an excal work book.
Each time a user ooens a new workspace, that workspace should be displayed in the tab control. Each Workspace takes the form of a user control, and each workspace has its own view model. I would like for the Tab Header to display a property from my view model which I think will likely have to be exposed as a property through my user control.
So far, the cleanest solution that I liked the best until I ran into numerous issues was by using datatemplates. Basically I Did the following:
<DataTemplate x:Key="WorkspaceItem">
<DockPanel Width="120">
<ContentPresenter
Content="{Binding Title}"
VerticalAlignment="Center"
/>
</DockPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type CustomerViewModel}">
<workspace:CustomerWorkspace />
</DataTemplate>
<TabControl ItemsSource="{Binding Workspaces}" ItemTemplate="{StaticResource WorkspaceItem}"/>
The TabControl.ItemsSource is bound to an observablecollection(of Object) which contains all of my workspaces.
This works great except for 2 things:
If I open multiple customers, then I have multiple workspaces open. Because of DataTemplate Recycling, I lose state when i swap from one tab to another. So everything that is not bound will lose state.
The performance of swapping between Different workspaces (that use different datatemplates) is terribly slow.
So... I found a suggestion from another user on SO to add the user controls to the ObservableCOllection and ditch the data templates. that now solves one of the problems of losing state. however, now I am faced with 2 remaining problems:
- How do i set the TabItem.Header property without using a DataTemplate
- The speed of swapping back and forth between tabs is still slow unless they are of the same DataTemplate.
I then proceeded to Actually add a TabItem to the ObservableCollection in my codebehind and Set the TabItem.Content Property to that of the user control. The speed issue was now eliminated as is the losing state issue since I have removed the use of the DataTemplates. However, I am now stuck with the issue of binding a TabItem.header to the Custome "Title" Property of my usercontrol that should be displayed in the Tab Header.
So after this terribly long post, my questions are:
Is there any way to use datatemplates and force them to create a new Instance for each item in the collection to prevent recycling and state loss.
1a. Is there a better alternative than what I mentioned in the post above?
is there a way to do all of this through the Xaml instead of through back end code construction of Tab Items?
The default behavior of WPF is to unload items which are not visible, which includes unloading
TabItems
which are not visible. This means when you go back to the tab, theTabItem
gets re-loaded, and anything not bound (such as a scroll position, control states, etc) will get reset.There was a good site here which contains code to extend the TabControl and stop it from destroying its
TabItems
when switching tabs, however it no longer seems to exist now.Here's a copy of the code, although I've made some changes to it. It preserves the
ContentPresenter
of TabItems when switching tabs, and uses it to redraw theTabItem
when you go back to the page. It takes up a bit more memory, however I find it better on performance since the TabItem no longer has to re-create all the controls that were on it.The TabControl template I usually use looks something like this:
You can also simplify your XAML by using an implicit
DataTemplate
instead of anItemTemplate
since your ViewModel will be placed in yourTabItem.Content
. I'm also not too sure what you're asking about the header, but if I understand you correctly you can just set the header in another implicit style for theTabItem