Reputation: 102945
I have a TabControl
in my WPF application. I want my application to basically support multiple "instances" within the single program. For example, think about web browsers, they allow you to have multiple instances of websites in different tabs, I want to achieve similar functionality where my application contains several instances of "sub applications".
The problem I currently face is that I have to copy-paste the same XAML to every tab, because each tab has exactly the same markup and UI, but different data. Another problem is that I need functionality to dynamically create those tabs.
Here's a screenshot of my application at its current state. As you can see, there are 2 tabs on the top and the second has transparent background since it's inactive.
So, how do I create a tab-able system where the UI of the tab remains the same for every tab and I only need to develop with one XAML UI and duplicate that for each tab?
Requirements:
Ideally I would love a plain simple sample project/code where there is one unstyled tab control and the application upon startup dynamically creates 2-n tabs which all have the same UI, but with different data.
Upvotes: 7
Views: 3729
Reputation: 43021
As noted in another answer there are probably lots of ways to do this, but here's my simple way:
Define a DataTemplate that defines the content of each your identical tabs. The controls in the data template will bind to the view model of the currently selected tab. I've put a single TextBlock in my example but you can easily extend this.
using this Xaml:
<Page.DataContext>
<Samples:TabBindingViewModels />
</Page.DataContext>
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ContentTemplate"
DataType="{x:Type Samples:TabBindingViewModel}">
<TextBlock Text="{Binding Content}"/>
</DataTemplate>
</Grid.Resources>
<TabControl ContentTemplate="{StaticResource ContentTemplate}"
DisplayMemberPath="Header" ItemsSource="{Binding Items}" />
</Grid>
and this view model code:
public class TabBindingViewModels
{
public TabBindingViewModels()
{
Items = new ObservableCollection<TabBindingViewModel>
{
new TabBindingViewModel(1),
new TabBindingViewModel(2),
new TabBindingViewModel(3),
};
}
public IEnumerable<TabBindingViewModel> Items { get; private set; }
}
public class TabBindingViewModel
{
public TabBindingViewModel() : this(0)
{
}
public TabBindingViewModel(int n)
{
Header = "I'm the header: " + n.ToString(CultureInfo.InvariantCulture);
Content = "I'm the content: " + n.ToString(CultureInfo.InvariantCulture);
}
public string Header { get; set; }
public string Content { get; set; }
}
we get:
I quite like this tutorial on styling the tab control. You can easily put more complex content into the tab headers as well as the content.
You should examine the full template of the tab control to gain an insight into how it works. Use Blend or VS11 beta to extract the template.
In order to dynamically add/delete tabs, now all you need to do is add/delete items to the ObservableCollection of view models.
Upvotes: 3
Reputation: 22404
Without more information, my approach would be to startup a MVVM provider inside of a ApplicationDomain for each instance or tab. When you kill/close the tab, unload the app domain.
Upvotes: 1
Reputation: 15794
Take a look at Caliburn.Micro for an MVVM
framework-based solution. The exact question you're asking is solved in one of the sample solutions.
http://caliburnmicro.codeplex.com/
Upvotes: 1
Reputation: 29066
This could actually turn into a rather large answer because there are many different paths you could take.
It's important to understand that a TabControl is an ItemsControl, which means it can contain a collection of objects, such as UserControls that make up each module. As such, you could start by binding the Items property to an ObservableCollection{T} of some UserControl objects that live inside of some module project that implements an interface like IModule from Prism. The interface defines the starting point of any module that can be loaded within a tab. As modules are requested, you would simply load the assembly and add a tab that contains a reference to the a region defined in the module.
It's actually not very difficult, but I'd recommend you read up on Prism because it will handle a lot of the heavy lifting for you. I've recently gone through building an interface exactly like the one you describe using Prism.
Upvotes: 2