0

I have a TabControl that I want to have 2 data bindings. One is to have a TabItem for each item in a list and another single TabItem that is a Summary and uses an ItemsControl to show a template of controls for each item in another list.

I can set a TabControl to either one of these two types of data binding but not both of them at the same time. How could I get a TabControl to do both? Could I somehow add an extra TabItem to the TabControl that has ItemSource, Binding set? Then I could load the new extra TabItem with XAML.

thatguy
  • 21,059
  • 6
  • 30
  • 40
xav2075
  • 11
  • 4
  • I think it'll be an interesting idea. And can you use code or Xaml to abstract the purpose you want? If so, it would be very helpful. – jamesnet214 Jan 07 '21 at 23:55
  • 1
    Can you just add the summary `TabItem` to the `List` that is being bound to? In theory that should show all the different items in the `TabView` – Wellerman Jan 08 '21 at 06:44

1 Answers1

0

Expose a Combined Collection

You can expose a collection in your view model that combines both the collection and the addtional item like below (if you do not modify the collection, you can also use any other enumerable type).

public class MyViewModel : INotifyPropertyChanged
{
   public MyViewModel()
   {
      var myTabItemCollection = // ...get the items collection.
      var mySummaryTabItem = // ...get the summary item.

      TabItems = new ObservableCollection<string>(myTabItemCollection)
      {
         mySummaryTabItem
      };
   }

   public ObservableCollection<MyTabItemViewModel> TabItems { get; }

   // ...other properties, methods, ...
}

Using a Composite Collection

There is a dedicated CompositeCollection type for combining one or multiple collections and single items in XAML. However, this type has severe shortcommings in terms of data-binding. It does neither derive from FrameworkElement, nor is it a Freezable, which makes it very difficult, cumbersome and error-prone to work with. I will present one solution with this type for the sake of completeness. Let's assume this view model:

public class MyViewModel : INotifyPropertyChanged
{
   public ObservableCollection<MyTabItemViewModel> TabItems { get; }

   public MyTabItemViewModel Summary { get; }

   // ...other properties, methods, ...
}

In theory this would be easy if CompositeCollection would be to data-bindable (this does not work):

<TabControl>
   <TabControl.ItemsSource>
      <CompositeCollection>
         <CollectionContainer Collection="{Binding TabItems}" />
         <TabItem DataContext="{Binding Summary}"
                  Header="{Binding}"
                  Content="{Binding}" />
      </CompositeCollection>
   </TabControl.ItemsSource>
</TabControl>

In order to make it work, your can apply various approaches like in these related questions:

As an example, I will use a binding proxy type to enable binding inside CompositeCollection. It will bind to the DataContext of the TabControl and expose it with the Data property. Since the proxy is a resource, the CompositeCollection can refer to it as source for binding with StaticResource.

<TabControl x:Name="TabControl">
   <TabControl.Resources>
      <local:BindingProxy x:Key="TabControlBindingProxy" Data="{Binding}" />
   </TabControl.Resources>
   <TabControl.ItemsSource>
      <CompositeCollection>
         <CollectionContainer Collection="{Binding Data.TabItems, Source={StaticResource TabControlBindingProxy}}" />
         <TabItem DataContext="{Binding Data.Summary, Source={StaticResource TabControlBindingProxy}}"
                  Header="{Binding}"
                  Content="{Binding}" />
      </CompositeCollection>
   </TabControl.ItemsSource>
</TabControl>

This will also work for multiple collections and single items, but be aware that this and the other approaches from the related questions - although they compile and run just fine - might crash the Visual Studio designer, which is unfortunate. Consequently, I recommend you to take the approach of exposing a combined collection, which is more reliable, easier to comprehend and simple.

thatguy
  • 21,059
  • 6
  • 30
  • 40