views:

644

answers:

2

ok currently i have this piece of code:

<TabItem Style="{DynamicResource MyStyle" x:Name="TabCustomers" Padding="0,1,4,1"
 Header={Binding Path=customersHeader}/>

Now i want to add an icon there so I do (by removing the header above):

<TabItem.Header>
<StackPanel Orientation="Horizontal">
     <Image Stretch="UniformToFill" Source="{StaticResource customers}"/>
     <TextBlock x:Key="textblock" Margin="4,0,0,0" 
     Text="{Binding Path=customersHeader}"/>
</StackPanel>
</TabItem.Header>

So far it's ok. I would like to generalize this using a datatemplate. I assume i have to do this in my resource dictionary:

<DataTemplate x:Key="TabItemCustomersTemplate" DataType="{x:Type TabItem}">
<StackPanel Orientation="Horizontal">
     <Image Stretch="UniformToFill" Source="{StaticResource customers}"/>
     <TextBlock x:Key="textblock" Margin="4,0,0,0" 
     Text="{Binding Path=customersHeader}"/>
    </StackPanel>
</DataTemplate>

and change this in my tabitem declaration:

<TabItem ... HeaderTemplate="{StaticResource TabItemCustomersTemplate}".../>

So i run into the following issues and questions: 1) binding doesnt work, why? 2) how can i access textblock from c#? 3) how can i generalize this so i dont have to copy this over and over again for different tab items (or other controls for the matter) so that i can pass my own text and image source each time? For example you might use this to create an image button and if you have 20 buttons the code becomes messy.

Any ideas?

Thank you.

A: 
  1. if you template the header in a tabitem, you do not need to set the data type of the template. the header is a property of the tab item, it is actually a property of type object, you can put anything in there.

    try removing the DataType="{x:Type TabItem}" and see if it works.

  2. you should not need to access the textblock from c#, you should make do with the binding system. place a custom object in your header. then bind this object to your textblock then adjust the object and it will manipulate the textblock. getting at an element is always hard if it is contained in a data template. you should not need to. if you find yourself walking the visual tree to find a visual element you are doing things the hard way
  3. you can generalise this by following suggestion 2, using a custom object, removing the x:Key of your data template and setting its DataType to be the type of your custom object. then wherever your custom object appears you will get it data templated properly
Aran Mulholland
A: 

Try this, This is working for me

<Window.Resources>
    <!-- <BitmapImage x:Key="customers" UriSource="einstein.jpg"/>--> 
    <DataTemplate x:Key="TabItemCustomersTemplate">
        <StackPanel Orientation="Horizontal">
            <Image Stretch="UniformToFill" Source="{Binding Path=Customers}"/>
            <TextBlock Margin="4,0,0,0" x:Name="txt" Text="{Binding Path=CustomersHeader}"/>
</StackPanel>
    </DataTemplate>
</Window.Resources>
    <Grid>
    <TabControl Name="mytabcontrol">
        <TabItem x:Name="TabCustomers" Padding="0,1,4,1" Header="{Binding}" HeaderTemplate="{StaticResource TabItemCustomersTemplate}">
            <Label Content="myContent" Background="Red"/>
        </TabItem>
    </TabControl>
</Grid>

in code behind

    public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();

        var lst = new List<People>();
        lst.Add(new People() { CustomersHeader = "My Customer" });
        this.DataContext = lst;
    }
}

public class People
{
    public string CustomersHeader { get; set; }
    public BitmapImage Customers { get; set; }
}

Further you can find your textblock in code behind using this

TabPanel tabPanel = GetVisualChild<TabPanel>(mytabcontrol);
if (tabPanel != null)
{
    foreach (UIElement element in tabPanel.Children)
    {
        TabItem tabItem = element as TabItem;
        var image = FindNameFromHeaderTemplate<TextBlock>(tabItem, "txt");
    }
}

    public static T FindNameFromHeaderTemplate<T>(TabItem tabItem, String name) where T : UIElement
    {
        if (tabItem == null)
        {
            throw new ArgumentNullException("container");
        }

        if (tabItem.HeaderTemplate == null)
        {
            return null;
        }

        ContentPresenter contentPresenter = GetVisualChild<ContentPresenter>(tabItem);
        if (contentPresenter == null)
        {
            return null;
        }

        T element = tabItem.HeaderTemplate.FindName(name, contentPresenter) as T;
        return element;
    }

    public static T GetVisualChild<T>(Visual referenceVisual) where T : Visual
    {
        Visual child = null;
        for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceVisual); i++)
        {
            child = VisualTreeHelper.GetChild(referenceVisual, i) as Visual;
            if (child != null && child.GetType() == typeof(T))
            {
                break;
            }
            else if (child != null)
            {
                child = GetVisualChild<T>(child);
                if (child != null && child.GetType() == typeof(T))
                {
                    break;
                }
            }
        }
        return child as T;
    }
viky
thanks! how can i bind a different image source without declaring a different datatemplate?
immuner
you can have either a property in code behind and Bind Source property of image in data template with it or you can use Trigger in data template itself to change image for some specific item.
viky
can you show me how to do the first? bind property in code of a datatemplate?
immuner
I have updated my code to show the image using Binding in data template with property in code behind.
viky