0
votes

I've got a TabControl that is bound to its viewmodel Items property:

<TabControl ItemsSource="{Binding Items}"/>

This is the viewmodel class:

public class ViewModel
{
    public ObservableCollection<object> Items { get; } = new ObservableCollection<object>();
    public ViewModel()
    {
        Items.Add(new Person() { FirstName = "Alan", LastName = "Turing" });
        Items.Add(new Car() { ModelName = "Fiesta", Manifacturer = "Ford" });
    }

}

Items's type is ObservableCollection<object>; this is because each tab represents a different kind of object, in this example a Person and a Car.

Question

I want to bind the tab's header to a text that is not available as a property of the bound classes, let say a manually provided header.
For example, I would like the first tab to have the text "Person" as header and the second to have the text "Car" as header:

Person And Car As Tab's Headers

I thought of:

  • adding the Header property to each class. It's easy and straightforward from the binding point of view, but this is sort of a generalized viewer and not all the interfaces of the input data can be modified.
  • an "attached property" would still require to act at class-level; yet feasible but probably a bit overkill
  • A Dictionary<object, string> defined at the viewmodel level, let's call it Headers.
    Each time i add an object to the collection, i would also add an Header to the Headers dictionary. Its key would be the object itself.

    Person p = new Person() { FirstName = "Alan", LastName = "Turing" };
    Headers.Add(p, "Person");
    Items.Add(p);
    

But how to write the binding expression then?

<TabControl ItemsSource="{Binding Items}">
    <TabControl.ItemTemplate>
        <DataTemplate>
            <Label Content="{Binding ????????}"/>
        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>

Even if possible, this solution seems to be not very 'binding-friendly'.

There seems to be two problems here: dynamically attach an information to an instance, and access it via binding.

Any ideas?

Postscript - the code

The code, for completeness. My classes:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Car
{
    public string ModelName { get; set; }
    public string Manifacturer { get; set; }
}

My DataTemplate, defined in the <Winodow.Resources> tag.

    <DataTemplate DataType="{x:Type local:Person}">
        <StackPanel>
            <Label Content="{Binding FirstName}"/>
            <Label Content="{Binding LastName}"/>
        </StackPanel>
    </DataTemplate>

    <DataTemplate DataType="{x:Type local:Car}">
        <StackPanel>
            <Label Content="{Binding Manifacturer}"/>
            <Label Content="{Binding ModelName}"/>
        </StackPanel>
    </DataTemplate>
1
I would for sure just use a IValueConverter to convert the object to the proper header. - Nawed Nabi Zada
Alternative would also be a HeaderTemplateSelector. You could then decide based on type. - XAMlMAX
how about a Dictionary<string, object> called Items? Then in header you bind to Key, in tab you can use content presenter bound to Value. - Krzysztof Skowronek

1 Answers

1
votes

You can also override the ToString() method for your model classed and return your friendly name from there.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public override string ToString()
    {
        return "Person";
    }
}

Template

<TabControl ItemsSource="{Binding Items}">
    <TabControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}"/>
        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>