none
Data Binding - Tab Control

    Question

  • Has anyone had luck with data binding with a Tab Control? I get a stack trace error when I add the TabControl in the ItemsPanelTemplate content area. When I place the TabControl in the ItemsTemplate, it works but displays the tabs as different entities (because it is rendering them one per item).

    I want to have dynamic tabs based on the categories in the xml file. This is what I have. Is there a way to have it work in the ItemsPanelTemplate so it would be a single TabControl with data binded elements? I also tried to use TabControl, rather than ItemsControl but then I got an error "unable to cast from <class> to TabItem".

    <ItemsControl.ItemTemplate>

        <DataTemplate>

            <y:TabControl>

                <y:TabItem Header="{Binding Name}" />

            </y:TabControl>

        </DataTemplate>

    </ItemsControl.ItemTemplate>

     

    This is a similar issue. The problem with the workaround is that my content is purely in xml (category for tabs and children data for tab items).

    http://silverlight.net/forums/t/17713.aspx

    Sunday, June 15, 2008 12:45 PM

Answers

  • Hello, you need a Converter. Try this:

    Suppose your data source is something like this:

    public class TabSource
    {
    public object Header { get; set; }
    public object Content { get; set; }
    }

     

    You need a Converter to convert a list of your data source to a list of TabItem. This is because currently TabControl doesn't override PrepareContainerForItemOverride, so it won't automatically wrap your data source in TabItems.

    public class TabConverter : IValueConverter
    {
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
    List<TabSource> source = value as List<TabSource>;
    if (source != null)
    {
    List<TabItem> result = new List<TabItem>();
    foreach (TabSource tab in source)
    {
    result.Add(new TabItem()
    {
    Header = tab.Header,
    Content = tab.Content
    });
    }
    return result;
    }
    return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
    throw new NotImplementedException();
    }
    }

     

    Now you can write this:

    <UserControl.Resources>
    <local:TabConverter x:Key="tc"/>
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" Background="White">
    <y:TabControl x:Name="tab" ItemsSource="{Binding Converter={StaticResource tc}}"/>
    </Grid>

    List<TabSource> source = new List<TabSource>();
    source.Add(new TabSource()
    {
    Header = "a",
    Content = new Button() { Content = "First Tab" }
    });
    source.Add(new TabSource()
    {
    Header = new TextBox() { Text = "b" },
    Content = new CheckBox() { Content = "Second Tab" }
    });
    tab.DataContext = source;

     

    Note this won't auto select the first tab. You need to manually select it, or modify the Converter, so the first TabItem is selected.

    Tuesday, June 17, 2008 3:25 AM
  • Suppose you have defined a DataTemplate with x:Key="dt" in the UserControl.Resources section. In code, you can write something like this:

    DataTemplate dt = (DataTemplate)this.Resources["dt"];

    Then you an assign this DataTemplate to anything you want.

    Sunday, June 22, 2008 11:57 PM

All replies

  • Hello, you need a Converter. Try this:

    Suppose your data source is something like this:

    public class TabSource
    {
    public object Header { get; set; }
    public object Content { get; set; }
    }

     

    You need a Converter to convert a list of your data source to a list of TabItem. This is because currently TabControl doesn't override PrepareContainerForItemOverride, so it won't automatically wrap your data source in TabItems.

    public class TabConverter : IValueConverter
    {
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
    List<TabSource> source = value as List<TabSource>;
    if (source != null)
    {
    List<TabItem> result = new List<TabItem>();
    foreach (TabSource tab in source)
    {
    result.Add(new TabItem()
    {
    Header = tab.Header,
    Content = tab.Content
    });
    }
    return result;
    }
    return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
    throw new NotImplementedException();
    }
    }

     

    Now you can write this:

    <UserControl.Resources>
    <local:TabConverter x:Key="tc"/>
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" Background="White">
    <y:TabControl x:Name="tab" ItemsSource="{Binding Converter={StaticResource tc}}"/>
    </Grid>

    List<TabSource> source = new List<TabSource>();
    source.Add(new TabSource()
    {
    Header = "a",
    Content = new Button() { Content = "First Tab" }
    });
    source.Add(new TabSource()
    {
    Header = new TextBox() { Text = "b" },
    Content = new CheckBox() { Content = "Second Tab" }
    });
    tab.DataContext = source;

     

    Note this won't auto select the first tab. You need to manually select it, or modify the Converter, so the first TabItem is selected.

    Tuesday, June 17, 2008 3:25 AM
  • Thanks. This solved the issue. With this approach, is there a way to create a DataTemplate through C#?

    Wednesday, June 18, 2008 1:06 AM
  • Unfortunately no. DataTemplate is designed to be created in XAML. Unless you use XamlReader, there's no way to create DataTemplate in code. A better approach is to create the DataTemplate in XAML in UserControl.Resources section, and access it in code. Then you can assign the template to anything you want.

    Friday, June 20, 2008 2:36 AM
  • How can we access the UserControl.Resources in C#? I did find a workaround by manually coding the children objects into the tab control, but it would be nice to use the DataTemplate.
    Friday, June 20, 2008 5:18 PM
  • Suppose you have defined a DataTemplate with x:Key="dt" in the UserControl.Resources section. In code, you can write something like this:

    DataTemplate dt = (DataTemplate)this.Resources["dt"];

    Then you an assign this DataTemplate to anything you want.

    Sunday, June 22, 2008 11:57 PM
  • From your earlier example:
    Content = new CheckBox() { Content = "Second Tab" }

    Question is, I want to have a ListBox in the Tab Content, what should I do?  I did something like this:

    Content = new ListBox() { ItemsSource = tab.myList }

    But how can I assign DataTemplate to it (binding to the elements of "myList")?  And is this the best way to do it?  Since I am planning to have multiple items and a few layers in the Content.  (e.g. several other titles + tabs under each tab, then ListBox under second tab layer)  Seems like I am messing up something here...o.O

    Tuesday, July 29, 2008 1:49 PM
  •  If I have a class of tabSource:

     

    public class TabSource
        {
            public object header { get; set; }
            public object content { get; set; }
        }
     
    And converter:
     
    public class TabConverter : IValueConverter
        {
            public object Convert(object value, Type TargetType, object parameter, System.Globalization.CultureInfo culture)
            {
                List zdroj = value as List;
    
                if (zdroj != null)
                {
                    List vysledek = new List();
                    foreach (TabSource ts in zdroj)
                    {
                        vysledek.Add(new TabItem()
                            {
                                Header = ts.header,
                                Content = ts.content
                            });
                    }
                    return vysledek;
                }
                return null;
            }
    
            
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
      
    XAML
     
    <UserControl.Resources>
            <projekt:TabConverter x:Key="Converter"/>
        </UserControl.Resources>
        
        <Grid x:Name="LayoutRoot" Background="White">        
            
            
            <basics:TabControl x:Name="tc_auta"
                               ItemsSource="{Binding Converter={StaticResource Convert}}"/>
            
        </Grid>
      
     and Page.xaml.cs
     
    List zdroj = new List();
    
    zdroj.Add(new TabSource()
                    {
                        header = "string",
                        content = new TextBlock() { Text = "ahoj" }
                    });
    
                zdroj.Add(new TabSource()
                {
                    header = "ping",
                    content = new TextBlock() { Text = "ahoj" }
                });
    
                tc_auta.DataContext = zdroj;
      
     
     tc_auta.DataContext = zdroj; throw me en exception -->
     
    Unable to cast object of type 'TabControl.TabSource' to type 'System.Windows.Controls.TabItem'.
     
    I do not understand why. Does anybody know where could be a problem?
    Thanks for answer. 
    Sunday, January 25, 2009 5:26 AM
  • currently TabControl doesn't override PrepareContainerForItemOverride, so it won't automatically wrap your data source in TabItems.

    So, this is a more interest question here. How would I go about extending the TabControl to override the PrepareContainerForItemOverride myself? That is, until Silverlight implements it in the future. How do we program this to automatically wrap our data source in TabItems? I want to see an example here, because this is *exactly* what I'm trying to do today at work.
    Monday, August 24, 2009 7:30 PM
  •  I have a problem can you please solve that. I have trying to create Dynamic TAB control from XMLfile (Tab control source). Here I am giving all my code as follows.

     MainPage.xaml :

    ----------------------------

    1    <UserControl x:Class="SampleDynamicTabControl.MainPage"
    2        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    3        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    5        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    6        xmlns:y="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" 
    7        xmlns:local="clr-namespace:SampleDynamicTabControl" 
    8        xmlns:UCViews="clr-namespace:SampleDynamicTabControl.Views" 
    9        xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" 
    10       mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480" 
    11       Loaded="UserControl_Loaded">
    12       <!--<UserControl.Resources>
    13           <local:TabConverter x:Key="tc"/>
    14       </UserControl.Resources>-->
    15       
    16       
    17       <Grid x:Name="LayoutRoot" Margin="10">
    18           <Grid.Resources>
    19               <local:TabCollectionList x:Key="nl"/>
    20           </Grid.Resources>
    21           
    22           <!--<y:TabControl x:Name="tab" ItemsSource="{Binding Converter={StaticResource tc}}"/>-->
    23   
    24           <local:MyTabControl Name="tc1" MyItemsSource="{Binding Converter={StaticResource nl}">
    25               <local:MyTabControl.TabItemTemplate>
    26                   <DataTemplate>
    27                       <StackPanel>
    28                           <navigation:Frame Name="cf1" Source="{Binding TabFrameUri}"/>
    29                           <!--<Button Content="{Binding TabFrameUri}"/>-->
    30                           
    31                       </StackPanel>
    32                   </DataTemplate>
    33               </local:MyTabControl.TabItemTemplate>
    34               
    35               <local:MyTabControl.TabHeaderItemTemplate>
    36                   <DataTemplate>
    37                       <TextBlock Text="{Binding Title}"/>
    38                   </DataTemplate>
    39               </local:MyTabControl.TabHeaderItemTemplate>
    40           </local:MyTabControl>
    41       </Grid>
    42   
    43   </UserControl>
    
     
    MyTabControl.cs:
    ---------------------------------
      
    1    using System;
    2    using System.Net;
    3    using System.Windows;
    4    using System.Windows.Controls;
    5    using System.Windows.Documents;
    6    using System.Windows.Ink;
    7    using System.Windows.Input;
    8    using System.Windows.Media;
    9    using System.Windows.Media.Animation;
    10   using System.Windows.Shapes;
    11   using System.Collections;
    12   
    13   namespace SampleDynamicTabControl
    14   {
    15       public class MyTabControl : TabControl
    16       {
    17           public MyTabControl()
    18               : base()
    19           {
    20           }
    21   
    22           public DataTemplate TabHeaderItemTemplate
    23           {
    24               get { return (DataTemplate)GetValue(TabHeaderItemTemplateProperty); }
    25               set { SetValue(TabHeaderItemTemplateProperty, value); }
    26           }
    27           public static readonly DependencyProperty TabHeaderItemTemplateProperty =
    28               DependencyProperty.Register("TabHeaderItemTemplate", typeof(DataTemplate), typeof(MyTabControl), new PropertyMetadata(
    29                   (sender, e) =>
    30                   {
    31                       ((MyTabControl)sender).InitTabs();
    32                   }));
    33   
    34           public DataTemplate TabItemTemplate
    35           {
    36               get { return (DataTemplate)GetValue(TabItemTemplateProperty); }
    37               set { SetValue(TabItemTemplateProperty, value); }
    38           }
    39           public static readonly DependencyProperty TabItemTemplateProperty =
    40               DependencyProperty.Register("TabItemTemplate", typeof(DataTemplate), typeof(MyTabControl), new PropertyMetadata(
    41                   (sender, e) =>
    42                   {
    43                       ((MyTabControl)sender).InitTabs();
    44                   }));
    45   
    46           public IList MyItemsSource
    47           {
    48               get { return (IList)GetValue(MyItemsSourceProperty); }
    49               set { SetValue(MyItemsSourceProperty, value); }
    50           }
    51           public static readonly DependencyProperty MyItemsSourceProperty =
    52               DependencyProperty.Register("MyItemsSource", typeof(IList), typeof(MyTabControl), new PropertyMetadata(
    53                   (sender, e) =>
    54                   {
    55                       ((MyTabControl)sender).InitTabs();
    56                   }));
    57   
    58           internal void InitTabs()
    59           {
    60               Items.Clear();
    61               if (MyItemsSource != null)
    62               {
    63                   for (int i = 0; i < MyItemsSource.Count; i++)
    64                   {
    65                       var newTabItem = new TabItem();
    66                       
    67   
    68                       if (TabHeaderItemTemplate != null)
    69                           newTabItem.Header = TabHeaderItemTemplate.LoadContent();
    70   
    71                       if (TabItemTemplate != null)
    72                           newTabItem.Content = TabItemTemplate.LoadContent();
    73   
    74                       newTabItem.DataContext = MyItemsSourceIdea;
    75                       Items.Add(newTabItem);
    76                   }
    77               }
    78           }
    79       }
    80   }
    81   
    
     
    EntityDefine.cs:
    -------------------------------
     
    1    using System;
    2    using System.Net;
    3    using System.Windows;
    4    using System.Windows.Controls;
    5    using System.Windows.Documents;
    6    using System.Windows.Ink;
    7    using System.Windows.Input;
    8    using System.Windows.Media;
    9    using System.Windows.Media.Animation;
    10   using System.Windows.Shapes;
    11   using System.Collections.Generic;
    12   using System.Xml;
    13   
    14   namespace SampleDynamicTabControl
    15   {
    16       public class Tabs
    17       {
    18           public string Title { set; get; }
    19           public string TabFrameUri { set; get; }
    20       }
    21   
    22       public class TabCollectionList : List<Tabs>
    23       {
    24           public TabCollectionList()
    25               : base()
    26           {
    27   
    28               string strHeader;
    29               string strFrameUri;
    30               XmlReader reader = XmlReader.Create("TabData.xml");
    31               reader.MoveToContent();
    32   
    33               while (reader.Read())
    34               {
    35                   if (reader.NodeType == XmlNodeType.Element && reader.Name == "TabItem")
    36                   {
    37                       strHeader = reader.GetAttribute("header");
    38                       strFrameUri = reader.GetAttribute("TargetFormUri");
    39                       Add(new Tabs
    40                       {
    41                           Title = strHeader,
    42                           TabFrameUri = strFrameUri,
    43                       });
    44                   }
    45   
    46                   if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "TabCollection")
    47                   {
    48                       break;
    49                   }
    50               }
    51               reader.Close();
    52           }
    53       }
    54   }
    55   
    
     here i am got the path like "/Views/ContentPage1.xaml" from XML file but all 3 Tabs I am getting same XAML file (loaded in navigation:Frame Source=""). But some times i am getting to XAMLs shell know the reson behind that.
    Please reply me ASAP or send to my personal mail ID
    Thursday, February 11, 2010 6:59 AM
  • If you are doing MVVM please replace this: ItemsSource="{Binding Converter={StaticResource tc}}" With this: ItemsSource="{Binding source, Converter={StaticResource tc}}" and remove tab.DataContext = source;
    Monday, February 22, 2010 11:02 AM
  •  Thanks for your previous reply. I have a problem with my tab control. Can you please solve this -

    1. I am having 30 tab items.

    2. I need a tab left right buttons which are to scroll left or right to the tab items.

    3. and a menu which is having all list of tab items and if I click on that I just go to particular tab click.

     Please solve my problem.

    Wednesday, February 24, 2010 6:27 AM
  •  Thanks for your previous reply. I have a problem with my tab control. Can you please solve this -

    1. I am having 30 tab items.

    2. I need a tab left right buttons which are to scroll left or right to the tab items.

    3. and a menu which is having all list of tab items and if I click on that I just go to particular tab click.

     Please solve my problem. My personal mail ID: ashok.just4u@gmail.com

    Well Ashok you can keep all the tab object in a List or Dictionary and try to show only few tabs, let's say 5 tabs and keep replacing them from the List when you click left or right.
    Thursday, May 06, 2010 11:18 AM