3
votes

I've got a TabControl whose ItemsSource is bound to an observable collection of views (UserControls) each which have as its root element a TabItem. However, when it is displayed, the Header text is in content of each TabItem, as if UserControl wrapper is causing conflicts:

alt text

The TabControl is in SmartFormView.xaml:

<UserControl x:Class="TestApp.Views.SmartFormView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel
        Margin="10">
        <TextBlock Text="{Binding Title}"
            FontSize="18"/>
        <TextBlock Text="{Binding Description}"
            FontSize="12"/>

        <TabControl
            Margin="0 10 0 0"
            ItemsSource="{Binding SmartFormAreaViews}"/>
    </StackPanel>
</UserControl>

What do I have to change so that TabItems are displayed as TabItems inside the TabControl?

Here are the TabItem views called SmartFormAreaView.xaml:

<UserControl x:Class="TestApp.Views.SmartFormAreaView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <TabItem Header="This is the header">
        <StackPanel Margin="10">
            <TextBlock Text="this is the content"/>
        </StackPanel>
    </TabItem>
</UserControl>

And here is where I create and load each view into the ObservableCollection:

var areas = from area in xmlDoc.Descendants("area")
            select area;
foreach (var area in areas)
{
    SmartFormArea smartFormArea = new SmartFormArea();
    smartFormArea.IdCode = area.Attribute("idCode").Value;
    smartFormArea.Title = area.Attribute("title").Value;
    SmartFormAreaPresenter smartFormAreaPresenter = new SmartFormAreaPresenter(smartFormArea);
    SmartFormAreaViews.Add(smartFormAreaPresenter.View as SmartFormAreaView);
}
2

2 Answers

4
votes

For any ItemsControl, if the items added to its Items collection (either directly or via ItemsSource) are not instance of that control's item container, then each item is wrapped in an instance of the item container. The item container is a class such as TabItem or ListBoxItem. The item container is normally a ContentControl or HeaderedContentControl, and your actual item is assigned to its Content property, so you can use templates etc to control how the content is presented. You can also style the item container itself using the ItemControl's ItemContainerStyle property.

In this particular case, you should bind ItemsSource to a list of SmartFormAreaPresenters. Then use something like this for the tab control:

<TabControl ItemsSource="{Binding SmartFormAreaPresenters}">
  <TabControl.ItemContainerStyle>
    <Style TargetType="{x:Type TabItem}">
      <Setter Property="Header" Value="{Binding HeaderText}" />
    </Style>
  </TabControl.ItemContainerStyle>

  <TabControl.ContentTemplate>
    <DataTemplate DataType="{x:Type local:SmartFormAreaPresenter}">
      <local:SmartFormAreaView />
    </DataTemplate>
  </TabControl.ContentTemplate>
</TabControl>

where HeaderText is a suitable property on your SmartFormAreaPresenter. You should also remove the TabItem from your SmartFormAreaView definition. The DataContext of each View will automatically be set to the appropriate Presenter.

See Dr. WPF's blog for an excellent discussion of various ItemsControl related topics.

0
votes

The TabControl will accept your controls as its controls only if they can be cast to TabItem, not UserControl, or SmartFormAreaView, etc.

So you either fill regular TabItems with your visual tree, or you subclass TabItems, or you subclass the TabControl to override its IsItemItsOwnContainerOverride method, to accept your type as the container.

The method should look as follows:

protected override bool IsItemItsOwnContainerOverride(object item)
{
    return item is YourControlTypeHere || item is TabItem;
}