Cannot inherit from DataTemplate

ShimmyWeitzhandlerShimmyWeitzhandler ✭✭✭USMember ✭✭✭
edited April 2017 in Xamarin.Forms

Hi,

I'm trying to use a custom DataTemplate (so I can additional properties to be used by my DataTemplateSelector).
But when I run it renders nothing:

public class CustomDataTemplate : DataTemplate { }

XAML:

<ListView ItemsSource="{Binding Numbers}">
  <ListView.ItemTemplate>
    <forms:CustomDataTemplate>
      <TextCell Text="{Binding}"/>
    </forms:CustomDataTemplate>
  </ListView.ItemTemplate>
</ListView>

Should I switch to base DataTemplate and everything works fine.
Why is that? Is there a way to overcome this?

Best Answer

  • ShimmyWeitzhandlerShimmyWeitzhandler ✭✭✭ US ✭✭✭
    edited April 2017 Accepted Answer

    I finally got it to work, looks like Xamarin's XAML parser/renderer or somewhere up the tree cannot handle DataTemplate subclasses.

    This code works, it requires declaring the DataTemplate and wrapping it, I can live with it.

    [ContentProperty(nameof(Templates))]
    public class MultiDataTemplateSelector : DataTemplateSelector
    {
      public IList<TypedDataTemplate> Templates { get; } = new ObservableCollection<TypedDataTemplate>();
    
      public MultiDataTemplateSelector()
      {
        var incc = (INotifyCollectionChanged)Templates;
        incc.CollectionChanged += (sender, e) =>
        {
          if (e?.NewItems.Cast<TypedDataTemplate>()
              .Any(tdt => tdt?.TargetType == null || tdt?.Template == null) == true)
            throw new InvalidOperationException("All items must have all properties set.");
        };
      }
    
      protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
      {
        if (item == null) throw new ArgumentNullException(nameof(item));
        if (!Templates.Any()) throw new InvalidOperationException("No DataTemplates found.");
    
        var result =
          Templates.FirstOrDefault(t => t.TargetType.GetTypeInfo().IsAssignableFrom(item.GetType().GetTypeInfo()));
        if (result == null)
          throw new ArgumentOutOfRangeException($"Could not find a matching template for type '{item.GetType()}'.");
    
        return result.Template;
      }
    }
    
    [ContentProperty(nameof(Template))]
    public class TypedDataTemplate
    {
      public Type TargetType { get; set; }
      public DataTemplate Template { get; set; }
    }
    

    XAML:

    <ListView ItemsSource="{Binding Objects}">
      <ListView.ItemTemplate>
        <forms:MultiDataTemplateSelector>
          <forms:TypedDataTemplate TargetType="{Type x:Int32}">
            <DataTemplate>
              <ViewCell>
                <Label Text="int" />
              </ViewCell>
            </DataTemplate>
          </forms:TypedDataTemplate>
          <forms:TypedDataTemplate TargetType="{Type x:String}">
            <DataTemplate>
              <ViewCell>
                <Label Text="string"/>
              </ViewCell>
            </DataTemplate>
          </forms:TypedDataTemplate>
        </forms:MultiDataTemplateSelector>
      </ListView.ItemTemplate>
    </ListView>
    

Answers

  • DeviPrasadChinniDeviPrasadChinni ✭✭ USMember ✭✭
    edited April 2017

    @ShimmyWeitzhandler

    Add ViewCell in your DataTemplate if not please share your CustomDataTemplate class code

  • ShimmyWeitzhandlerShimmyWeitzhandler ✭✭✭ USMember ✭✭✭

    Same behavior with ViewCell.
    Here's what I'm trying to achieve:

    [ContentProperty(nameof(Templates))]
    public class MultiDataTemplateSelector : DataTemplateSelector
    {
      public IList<TypedDataTemplate> Templates { get; } = new List<TypedDataTemplate>();
    
      protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
      {
        if (item == null) throw new ArgumentNullException(nameof(item));
        if (!Templates.Any()) throw new InvalidOperationException("No DataTemplates were found.");
    
        var result = Templates.FirstOrDefault(t => t.TargetType.GetTypeInfo().IsSubclassOf(item.GetType()));
        if (result == null)
          throw new ArgumentOutOfRangeException($"Could not find a matching template for type '{item.GetType()}'.");
    
        return result;
      }
    }
    
    public class TypedDataTemplate : DataTemplate
    {
      public Type TargetType { get; set; }
    }
    

    Repro XAML (changing to regular DataTemplate works):

    <ListView ItemsSource="{Binding Numbers}">
      <ListView.ItemTemplate>
        <forms:TypedDataTemplate>
          <ViewCell>
            <Label Text="{Binding}"/>
          </ViewCell>
        </forms:TypedDataTemplate>
      </ListView.ItemTemplate>
    </ListView>
    

    Here's the XAML I want to achieve:

    <ListView ItemsSource="{Binding Numbers}">
      <ListView.ItemTemplate>
        <forms:TypedDataTemplate TargetType="{Type models:Manager}">
          <ViewCell>
            <Label Text="{Binding Medals}"/>
          </ViewCell>
        </forms:TypedDataTemplate>
        <forms:TypedDataTemplate TargetType="{Type models:Customer}">
          <ViewCell>
            <Label Text="{Binding Rating}"/>
          </ViewCell>
        </forms:TypedDataTemplate>
        <forms:TypedDataTemplate TargetType="{Type models:Person}">
          <ViewCell>
            <Label Text="{Binding Name}"/>
          </ViewCell>
        </forms:TypedDataTemplate>
      </ListView.ItemTemplate>
    </ListView>
    
  • ShimmyWeitzhandlerShimmyWeitzhandler ✭✭✭ USMember ✭✭✭
    edited April 2017

    DELETED

  • ShimmyWeitzhandlerShimmyWeitzhandler ✭✭✭ USMember ✭✭✭
    edited April 2017 Accepted Answer

    I finally got it to work, looks like Xamarin's XAML parser/renderer or somewhere up the tree cannot handle DataTemplate subclasses.

    This code works, it requires declaring the DataTemplate and wrapping it, I can live with it.

    [ContentProperty(nameof(Templates))]
    public class MultiDataTemplateSelector : DataTemplateSelector
    {
      public IList<TypedDataTemplate> Templates { get; } = new ObservableCollection<TypedDataTemplate>();
    
      public MultiDataTemplateSelector()
      {
        var incc = (INotifyCollectionChanged)Templates;
        incc.CollectionChanged += (sender, e) =>
        {
          if (e?.NewItems.Cast<TypedDataTemplate>()
              .Any(tdt => tdt?.TargetType == null || tdt?.Template == null) == true)
            throw new InvalidOperationException("All items must have all properties set.");
        };
      }
    
      protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
      {
        if (item == null) throw new ArgumentNullException(nameof(item));
        if (!Templates.Any()) throw new InvalidOperationException("No DataTemplates found.");
    
        var result =
          Templates.FirstOrDefault(t => t.TargetType.GetTypeInfo().IsAssignableFrom(item.GetType().GetTypeInfo()));
        if (result == null)
          throw new ArgumentOutOfRangeException($"Could not find a matching template for type '{item.GetType()}'.");
    
        return result.Template;
      }
    }
    
    [ContentProperty(nameof(Template))]
    public class TypedDataTemplate
    {
      public Type TargetType { get; set; }
      public DataTemplate Template { get; set; }
    }
    

    XAML:

    <ListView ItemsSource="{Binding Objects}">
      <ListView.ItemTemplate>
        <forms:MultiDataTemplateSelector>
          <forms:TypedDataTemplate TargetType="{Type x:Int32}">
            <DataTemplate>
              <ViewCell>
                <Label Text="int" />
              </ViewCell>
            </DataTemplate>
          </forms:TypedDataTemplate>
          <forms:TypedDataTemplate TargetType="{Type x:String}">
            <DataTemplate>
              <ViewCell>
                <Label Text="string"/>
              </ViewCell>
            </DataTemplate>
          </forms:TypedDataTemplate>
        </forms:MultiDataTemplateSelector>
      </ListView.ItemTemplate>
    </ListView>
    
  • flchauxflchaux ✭✭ FRMember ✭✭

    Thank you for sharing you solution. After one day searching I didn't find any other solution to inherit from DataTemplate.

Sign In or Register to comment.