0

I am working on a WPF application where I want to use TabControl with ItemTemplate and ContentTemplate. I have a textblock in the ItemTemplate (for Header) and another textblock in the ContentTemplate(for Content). I am binding a list to TabControl in constructor from code behind. In Window loaded event, I want to loop over all tab items, access textblock in content template (Name="txt1") for each tab and set different text to textblock in each tab. Below is my XAML:

<TabControl x:Name="mainTabs">
        <TabControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Header}"/>
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate>
                <TextBlock Name="txt1"></TextBlock>
            </DataTemplate>
        </TabControl.ContentTemplate>
</TabControl>

Code behind:

public MainWindow()
        {
            InitializeComponent();
// Read tabs data from xml file
            List<MyTab> lst = ReadTabsFromXml();
            mainTabs.ItemsSource = lst;
}

public class MyTab
    {
        public string Header { get;set; }
        public string Content { get; set; }
    }

Please help me with this.

SamTh3D3v
  • 9,854
  • 3
  • 31
  • 47
swarraj
  • 25
  • 2
  • 9

1 Answers1

1

You don't need to do that. Just store your MyTab classes in an ObservableCollection<MyTab> instead of a List<MyTab>, and set the ItemsSource to the collection. The TabControl will update automatically.

EDIT: I've updated the answer, with a solution regarding using Gecko browser. You can wrap the Gecko browser in a UserControl that you use in each TabItem. Also note that a TabControl recreates the the TabItem every time you switch tabs, which also causes the webpage to reload. You can get around this by using e.g. TabControEx instead of TabControl (https://stackoverflow.com/a/9802346/6839733). But this is not related to the Gecko browser or to binding URL's from a collection - it's just how the TabControl works.


MainWindow.xaml:

<TabControl x:Name="MyTabControl">
    <TabControl.Resources>
        <DataTemplate x:Key="MyTabContentTemplate" x:Shared="False">
            <local:GeckoBrowser Uri="{Binding Path=Uri}" />
        </DataTemplate>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="ContentTemplate" Value="{StaticResource MyTabContentTemplate}" />
        </Style>
    </TabControl.Resources>
    <TabControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=Header}"/>
        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>

MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    public ObservableCollection<WebPageInfo> WebPageCollection { get; set; }
    public MainWindow()
    {
        InitializeComponent();

        // Read tabs data from xml file
        //lst = ReadTabsFromXml();
        WebPageCollection = new ObservableCollection<WebPageInfo>();
        WebPageCollection.Add(new WebPageInfo() { Header = "Tab1", Uri = "https://www.amazon.com/" });
        WebPageCollection.Add(new WebPageInfo() { Header = "Tab2", Uri = "https://www.cnn.com/" });
        WebPageCollection.Add(new WebPageInfo() { Header = "Tab3", Uri = "https://www.microsoft.com/" });
        WebPageCollection.Add(new WebPageInfo() { Header = "Tab4", Uri = "https://www.facebook.com/" });

        MyTabControl.ItemsSource = WebPageCollection;
        MyTabControl.SelectedIndex = 0;
    }

    public class WebPageInfo
    {
        public string Header { get; set; }
        public string Uri { get; set; }
    }

}

GeckoBrowser.xaml:

<UserControl x:Class="WpfApplication1.GeckoBrowser"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfApplication1"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Border x:Name="MyBorder" Background="Black" Margin="0" />
</UserControl>

GeckoBrowser.xaml.cs:

public partial class GeckoBrowser : UserControl
{
    public static readonly DependencyProperty UriProperty =
        DependencyProperty.Register("Uri", typeof(string), typeof(GeckoBrowser), new FrameworkPropertyMetadata(null));

    public string Uri
    {
        get { return (string)GetValue(UriProperty); }
        set { SetValue(UriProperty, value); }
    }

    public GeckoBrowser()
    {
        InitializeComponent();
        Xpcom.Initialize("Firefox");
        Loaded += GeckoBrowser_Loaded;
    }

    private void GeckoBrowser_Loaded(object sender, RoutedEventArgs e)
    {
        WindowsFormsHost host = new WindowsFormsHost();
        GeckoWebBrowser browser = new GeckoWebBrowser();
        browser.Navigate(Uri);
        host.Child = browser;
        MyBorder.Child = host;
    }
}
Oystein
  • 1,232
  • 11
  • 26
  • Thanks for the reply. I understand below XAML will do content binding to all textblocks for all tabs: But how and where (Window.Loaded or constructor) can I do the same thing from code behind ? How can I loop over all tabs, access textblock in ContentTemplate for each tab and assign text (MyTab.Content) to it ? – swarraj Feb 18 '18 at 05:59
  • You can do it at application startup using e.g. the constructor or WindowLoaded, given that it's values that you _know_ at startup. But what then if the values change? Then you need a method for running back through the `TabControl` and updating any changed values. This seems very cumbersome to me. Can you explain why you want to do this in code-behind? What are you trying to achieve? – Oystein Feb 18 '18 at 21:13
  • I had given textblock as an example in contenttemplate. Actually in content template, I have Gecko WebBrowser. For all tabs, I want to load home pages of all sites (for example, Amazon, Flipkart, etc.) at application start and not at tab selection. To achieve this, I was trying to access Gecko WebBrowser from code behind by looping over all tabs and will invoke its Navigate() method. So, finally I want to have Amazon page for Amazon tab, Flipkart page for Flipkart tab, etc. Please help me with this. – swarraj Feb 21 '18 at 16:13
  • @swarraj See updated answer. I've added an example including the Gecko browser. Also note that a normal `TabControl` will reload controls every time you switch tabs. You can get around this by using e.g. `TabControlEx`: https://stackoverflow.com/a/9802346/6839733 – Oystein Feb 22 '18 at 09:03
  • Thanks a lot for the answer, It worked for me. I struggled a lot for it. Referring to TabControlEx approach, can you tell me how to use XAML part (which is found in link) in WPF application ? how to use TabControlEx control like ? – swarraj Feb 27 '18 at 18:53
  • Yes, referring to the marked answer written by Dennis in TabControlEx link: You need to create a class called `TabControlEx.cs` in the same folder as your `MainWindow.xaml` file, and copy the code from the answer. Then add the XAML style for TabControlEx under , and change `TargetType` from `controls:TabControlEx` to `local:TabControlEx` (or add `controls` as namespace). Now you can use `local:TabControlEx` instead of `TabControl`. – Oystein Feb 28 '18 at 08:38
  • Thank you for the reply. However, I see the code(which you had provided before TabControlEx implementation) calls constructor of GeckoBrowser while switching between tabs and loads corresponding web page. Is there any way to load all tabs (with corresponding webpage) at application start only? – swarraj Mar 05 '18 at 18:28
  • how can I load all tabs with corresponding web pages at application start only ? Please help me with this. – swarraj Mar 24 '18 at 08:14
  • Try setting the SelectedIndex value to every value in the TabControlEx Loaded event handler. – Oystein Mar 24 '18 at 09:14