Applying template to a contentcontrol based on data type of bound object

831 views Asked by At

I am stuck with the following problem:

I need to make a custom ContentControl which, when not focused would have a default DataTemplate with a TextBlock inside, but when Focused (iether by Mouse or Keyboard) should be able to present another appropriate DataTemplate based on the type of Content. For an example, let's say if the type of Content is String then a DataTemplate with a TextBox should be presented, if it's DateTime - then a DateTimePicker, and so on.

I know there's a way to supply different DataTemplates depending on Focused/NotFocused for ListBoxItems, i have implemented that through applying a Style with Triggers. But in this case I'm stuck. I've tried it with Style, but with no success.

Here's what I came up with so far:

Themes/generix.xaml for the Gadget control which is just inherited from a ContentControl:

<ControlTemplate x:Key="String">
    <Border BorderBrush="Green" BorderThickness="1">
        <TextBlock Text="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}"></TextBlock>
    </Border>
</ControlTemplate>
<ControlTemplate x:Key="Int32">
    <Border BorderBrush="Blue" BorderThickness="1">
        <TextBlock Text="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}"></TextBlock>
    </Border>
</ControlTemplate>
<ControlTemplate x:Key="Base">
    <Border BorderBrush="Yellow" BorderThickness="1">
        <TextBlock Text="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}"></TextBlock>
    </Border>
</ControlTemplate>
<local:DTC x:Key="DataTypeConverter"></local:DTC>
<Style TargetType="{x:Type local:Gadget}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Content, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:Gadget}},  Converter={StaticResource DataTypeConverter}}" Value="String}">
            <Setter Property="Template" Value="{StaticResource String}"></Setter>
        </DataTrigger>
        <DataTrigger Binding="{Binding Content,  RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:Gadget}}, Converter={StaticResource DataTypeConverter}}" Value="Int32}">
            <Setter Property="Template" Value="{StaticResource Int32}"></Setter>
        </DataTrigger>
    </Style.Triggers>
    <Setter Property="Padding"
            Value="2" />
    <Setter Property="Template" Value="{StaticResource Base}"> </Setter>
</Style>

The Gadget control backend has only one static constructor:

static Gadget()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(Gadget),
                   new FrameworkPropertyMetadata(typeof(Gadget)));

    }

This is the TestClass:

public class TestClass
{
    public TestClass(string one, string two)
    {
        One = one;
        Two = two;
        Three = count++;
    }

    static int count = 0;

    public string One { get; set; } = "One1";
    public string Two { get; set; } = "Two2";
    public int Three { get; set; }
}

This is the valueConverter:

public class DTC : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
      CultureInfo culture)
    {
        return value.GetType();
    }

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

This is how I try to use it in a ListBox

<ListBox Name="lstBox" Margin="10,10,172,40">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <local:Gadget Content="{Binding Path=One}"></local:Gadget>
                    <local:Gadget Content="{Binding Path=Two}"></local:Gadget>
                    <local:Gadget Content="{Binding Path=Three}"></local:Gadget>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

I've searched everywhere but either i'm not formulating my question well, or maybe no one has ever had need for that...

I will really appreciate any suggestions on this matter!

Thanks a lot in advance!

EDIT

In order to make the suggestion of mm8 work I have edited the DTC object to return Type instead of String. The code snipped above is fixed accordingly.

1

There are 1 answers

1
mm8 On BEST ANSWER

Bind to the Content property of {RelativeSource Self} and return the actual type from the Convert method of the converter:

<Style TargetType="{x:Type local:Gadget}" xmlns:s="clr-namespace:System;assembly=mscorlib">
    <Setter Property="Padding" Value="2" />
    <Setter Property="Template" Value="{StaticResource Base}"></Setter>
    <Style.Triggers>
        <DataTrigger Binding="{Binding Content, RelativeSource={RelativeSource Self}, Converter={StaticResource DataTypeConverter}}" 
                     Value="{x:Type s:String}">
            <Setter Property="Template" Value="{StaticResource String}"></Setter>
        </DataTrigger>
        <DataTrigger Binding="{Binding Content, RelativeSource={RelativeSource Self}, Converter={StaticResource DataTypeConverter}}" 
                     Value="{x:Type s:Int32}">
            <Setter Property="Template" Value="{StaticResource Int32}"></Setter>
        </DataTrigger>
    </Style.Triggers>
</Style>

public class DTC : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value.GetType();
    }

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