Search code examples
c#wpfbindingtabcontrolcompositecollection

bounding tab control items to a PROPERTY of an ObservableCollection, with a twist


I have a TabControl

I followed the answer chosen here to the letter. The thing is that in my case the ExistingTabs is not an ObservableCollection, but the property of an ObservableCollection:

Public Class TestData : INotifyPropertyChanged // and the required event handler is there also, not shown here
{
    public TabItem tiTestTab {get; set;}
    // another dozen properties
}

and

public class ReportData
{
    public static ObservableCollection<TestData> testData {get;set;}
    // another dozen properties
}

Here's what I did:

<Window.Resources>
   <CollectionViewSource x:Key="ExistingTabs" Source="{Binding Path=(local:ReportDataSet.testData), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Window.Resources>
<TabControl>
    <TabControl.ItemsSource>
        <CompositeCollection>
             <TabItem>SomeSpecialItem</TabItem>
             <CollectionContainer Collection="{Binding Source={StaticResource ExistingTabs}}"/>
         </CompositeCollection>
    </TabControl.ItemsSource>
</TabControl>

This, of course, places the testData in the tabs and not the tiTestTab property.

I am at a loss.

Preferably, XAML-only. Using C# and Visual Studio 2013.

Thanks.


Solution

  • Xaml code:

    <Window.Resources>
        <local:CollectionConverter x:Key="collectionConverter" />
        <CollectionViewSource x:Key="ExistingTabs" Source="{Binding Path=(local:ReportDataSet.testData), Converter={StaticResource collectionConverter}, UpdateSourceTrigger=PropertyChanged}"/>
    </Window.Resources>
    <TabControl>
        <TabControl.ItemsSource>
            <CompositeCollection>
                <TabItem Header="test">
                    <StackPanel>
                        <Button Content="Add new item" Click="AddNewTabItem"></Button>
                        <Button Content="Remove last item" Click="RemoveLastItem"></Button>
                    </StackPanel>
                </TabItem>
                <CollectionContainer Collection="{Binding Source={StaticResource ExistingTabs}}" >
                </CollectionContainer>
            </CompositeCollection>
        </TabControl.ItemsSource>
    </TabControl>
    

    Converter:

    public class CollectionConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is ObservableCollection<TestData>)
            {
                return new ObservableCollection<TabItem>(((ObservableCollection<TestData>)value).
                    Select(q => q.tiTestTab));
            }
            return null;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    ReportDataSet:

    public class ReportDataSet
    {
        public static ObservableCollection<TestData> testData { get; set; }
    
        static ReportDataSet()
        {
            testData = new ObservableCollection<TestData>();
            testData.Add(new TestData()
            {
                tiTestTab = new TabItem()
                {
                    Header = "test 1"
                }
            });
    
            testData.CollectionChanged += (s, e) => { OnStaticPropertyChanged("testData"); };
        }
        public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
    
        protected static void OnStaticPropertyChanged(string propertyName)
        {
            var handler = StaticPropertyChanged;
            if (handler != null) handler(null, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    Code-behind (for test purposes):

        private void AddNewTabItem(object sender, RoutedEventArgs e)
        {
            ReportDataSet.testData.Add(new TestData()
            {
                tiTestTab = new TabItem()
                {
                    Header = "test " + (ReportDataSet.testData.Count + 1)
                }
            });
        }
    
        private void RemoveLastItem(object sender, RoutedEventArgs e)
        {
            if (ReportDataSet.testData.Count == 0) return;
    
            ReportDataSet.testData.Remove(ReportDataSet.testData.Last());
        }
    

    Hope, it helps