views:

214

answers:

2

I'm trying to create a application that will be tabbed where each tab will have a button area and a view area.

Now each tab will essentially have the same layout just different things in the layout and I wanted to be able to reuse the same layout so that I won't have to change at many places ( it's just not good programming ). Can I accomplish this using resources or perhaps Styles.

Please supply a light code example if possible.

EDIT: I've decided to add an example of what I'm trying to do because I'm still not getting it.

Under each TabItem I'm trying to recreate this grid (It's a bit more complicated but you get the idea ):

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="200"/>
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Border Margin="10"
                            BorderBrush="{StaticResource MediumColorBrush}"
                            CornerRadius="10"
                            BorderThickness="2"
                            Grid.Row="0">

               <!-- First content goes here -->

        </Border>

        <Border Margin="10"
                            BorderBrush="{StaticResource MediumColorBrush}"
                            CornerRadius="10"
                            BorderThickness="2"
                            Grid.Row="1">

               <!-- Second content goes here -->

        </Border>

    </Grid>

as you can see also the 2 borders are the same. Now I need to put some content placeholder where my comments are. I wan't to declare this Grid layout in a resource dictionary and then where I use it put seperate content into each border.

I may have alot of TabItems so repeating this code isn't a good idea and each Tab page will have different content in the 2 placeholders.

I'm able to use the

<ContentPresenter Content="{Binding}" />

thing but only for 1 content, what happens when there will be more.

+1  A: 

Ingo,

Code is always available on MSDN. Check this: UserControl, Custom controls, DataTemplates.

Here are some examples of each approach. For sake of simplicity, let's assume the layout you want to replicate is one line of text with green foreground (in reality it may be really different, but you get the idea).


1. User Control

Xaml:

<UserControl x:Class="WpfApplication1.GreenTextUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
  <TextBlock x:Name="txt" Foreground="Green"/>
</UserControl>

C#:

using System.Windows.Controls;

namespace WpfApplication1
{
  public partial class GreenTextUserControl : UserControl
  {
    public string Text
    {
      get { return txt.Text;}
      set { txt.Text = value; }
    }

    public GreenTextUserControl()
    {
      InitializeComponent();
    }
  }
}

Tab control:

<TabControl>
  <TabItem Header="Tab 1">
    <loc:GreenTextUserControl Text="This is Tab 1"/>
  </TabItem>
  <TabItem Header="Tab 2">
    <loc:GreenTextUserControl Text="This is Tab 2"/>
  </TabItem>
  <TabItem Header="Tab 3">
    <loc:GreenTextUserControl Text="This is Tab 3"/>
  </TabItem>
</TabControl>

2. Custom control

C#:

  public class GreenTextBlock : TextBlock
  {
    public GreenTextBlock()
    {
      Foreground = Brushes.Green;
    }
  }

TabControl:

<TabControl>
  <TabItem Header="Tab 1">
    <loc:GreenTextBlock Text="This is Tab 1"/>
  </TabItem>
  <TabItem Header="Tab 2">
    <loc:GreenTextBlock Text="This is Tab 2"/>
  </TabItem>
  <TabItem Header="Tab 3">
    <loc:GreenTextBlock Text="This is Tab 3"/>
  </TabItem>
</TabControl>

If your layout is more complex than textblock, custom controls also allows you to define it in XAML but it differs from UserControls.


3. DataTemplate

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:System="clr-namespace:System;assembly=mscorlib" 
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
    <x:Array x:Key="GreenText" Type="{x:Type System:String}">
      <System:String>This is Tab 1</System:String>
      <System:String>This is Tab 2</System:String>
      <System:String>This is Tab 3</System:String>
    </x:Array>

    <!--Tab item content data template-->
    <DataTemplate x:Key="GreenTextTemplate">
      <TextBlock Text="{Binding}" Foreground="Green"/>
    </DataTemplate>
  </Window.Resources>
    <Grid>
    <TabControl ItemsSource="{StaticResource GreenText}">
      <TabControl.ItemContainerStyle>
        <Style TargetType="{x:Type TabItem}">
          <Setter Property="ContentTemplate" Value="{StaticResource GreenTextTemplate}"/>
        </Style>
      </TabControl.ItemContainerStyle>
    </TabControl>
  </Grid>
</Window>

That's it :). Hope it helps.

Anvaka
Thank you, still wrapping my head around WPF. Now just realized the possibility to create my own class who inherits from TabItem but has the structure ready. Not sure though if this works as I don't know how to then add things into the layouts.
Ingó Vals
Ok found out that you can't inherit Xaml classes so that's of no use to me.All the articles you mentioned are about customizing controls not reusing layouts and if you would mean that I could customize the TabItem or something to make this work I'm not seeing it.
Ingó Vals
Customizing TabItem doesn't seem right to me (though you can via Custom controls). Instead you could create a user control and put it inside each TabItem. More elegant solution is to create DataTemplate and use it as ItemTemplate for the TabControl...
Anvaka
Ok there msut be some misunderstanding or I am just plain stupid. You mention user controls and you originally gave me a link to how one should make a usercontrol but I just don't know how that is useful in this scenario.You also mention DataTemplate, what I can understand is that revolves around binding data wich is not the problem. I wan't to re-use the same main layout over few tabItems without having to repeat the code.If what you are implying does actually work as I need it too I don't understand how. Is there an example somewhere.
Ingó Vals
Hey Ingo, don't be hard on yourself. Sorry for confusing answer. I've extended it. Take a look.
Anvaka
+2  A: 

TabItem is a ContentControl which allows any child content, but also allows templating of the content, which is exactly what you're trying to do. You can use a DataTemplate like this to do your shared layout. ContentPresenter is the placeholder for the different content of each TabItem.

<DataTemplate x:Key="ButtonViewerTemplate">
    <DockPanel>
        <Button DockPanel.Dock="Bottom" Content="OK"/>
        <Button DockPanel.Dock="Bottom" Content="Cancel"/>
        <Border Background="Aqua" BorderBrush="Red" BorderThickness="2" Padding="5">
            <ContentPresenter Content="{Binding}" />
        </Border>
    </DockPanel>
</DataTemplate>

To use the template just set it to each TabItem's ContentTemplate. This works with anything derived from ContentControl.

<TabControl>
    <TabItem ContentTemplate="{StaticResource ButtonViewerTemplate}" Header="Some Buttons">
        <UniformGrid>
            <Button Content="XXXXX"/>
            <Button Content="XXXXX"/>
            <Button Content="XXXXX"/>
            <Button Content="XXXXX"/>
        </UniformGrid>
    </TabItem>
    <TabItem ContentTemplate="{StaticResource ButtonViewerTemplate}" Header="All Blue">
        <Border Background="Blue" MinHeight="50"/>
    </TabItem>
    <TabItem ContentTemplate="{StaticResource ButtonViewerTemplate}" Header="Image">
        <Image Source="http://i.msdn.microsoft.com/Platform/Controls/StoMastheadMSDN/resources/logo_msdn.png"/&gt;
    </TabItem>
</TabControl>
John Bowen
OK let's say I have a grid inside the the TabItem I would define the grid inside the DataTemplate. But what if I then wan't to add content into the grid differently between tabs.Perhaps I should rather define a Grid style and use that on each grid but I'm not sure how to define row and columns sizes in a grid style.
Ingó Vals
Both the DataTemplate and the TabItem Content can contain anything, including Panels like Grid. Anything that needs to be different between the templated items can just be placed in the TabItem Content.
John Bowen
Ok you are meaning that the UniformGrid you create in the second codeWindow will be put into tha Aqua color border in the first window replacing the content/binding guy?
Ingó Vals
Exactly. And that can be anything you want: Control, Panel, UserControl, etc.
John Bowen
Ok but what if you have 2 or more content going into separate GridRows like in my example above?
Ingó Vals
You can use something like a HeaderedContentControl (with Content and Header) or a custom ContentControl with additional Content properties (with associated template properties) for each separate area that you need to provide templates for.
John Bowen