Xamarin Forms - Как написать typeof () в XAML? - PullRequest
0 голосов
/ 08 марта 2019

Класс форм xamarin DataTemplate получает параметр с Type в ctor.

public class DataTemplate : ElementTemplate, IDataTemplateController
{
    public DataTemplate(Type type);      
    ...
}

Как передать тип определенного класса в ctor в XAML? В c # я бы написал new DataTemplate(typeof(DeviceViewModel)). Но я должен написать это на XAML.

Псевдокод:

<DataTemplate>
    <x:Arguments>
            <typeof(viewModels:DeviceViewModel)/>
    </x:Arguments>
    <myControls:MyCustomControl/>
</DataTemplate>

EDIT

Чтобы прояснить свои цели, я создал производный пример. Допустим, есть следующая структура:

  • Изображение
  • Музыка
  • Документ
    • .txt
    • PDF
    • XML

Мой список содержит эти элементы. Каждый из этого типа должен обрабатываться в собственном DataTemplate. В моем случае элементы довольно сложны, поэтому я создаю ViewModel для каждого элемента. В коде это может выглядеть так:

public abstract class BaseViewModel
public class PictureViewModel : BaseViewModel
public class MusicViewModel : BaseViewModel
public class DocumentViewModel : BaseViewModel

/* My List full of different ViewModels*/
List<BaseViewModel> itemList;

Теперь я создаю TemplateSelector, который содержит другие TemplateSelector. Он вызывает правильную модель в зависимости от типа модели представления:

using System;
using Xamarin.Forms;
using MyApp.ViewModels;

namespace MyApp.TemplateSelectors
{
    public class MyItemTemplateSelector : DataTemplateSelector
    {
        public DataTemplateSelector PictureTemplateSelector { get; set; }
        public DataTemplateSelector MusicTemplateSelector { get; set; }
        public DataTemplateSelector DocumentTemplateSelector { get; set; }

        public DataTemplateSelector DefaultTemplate { get; set; }

        protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
        {
            switch (item)
            {
                case PictureViewModel vm:
                    return PictureTemplateSelector.SelectTemplate(item, container);
                case MusicViewModel vm:
                    return MusicTemplateSelector.SelectTemplate(item, container);
                case DocumentViewModel vm:
                    return DocumentTemplateSelector.SelectTemplate(item, container);
                default:
                    return DefaultTemplate.SelectTemplate(item, container);
            }
        }
    }
}

В XAML все назначения выполняются:

<DataTemplate x:Key="PictureDefaultTemplate">
    <ViewCell>
       <Image Source="{Binding FilePath}"/>
    </ViewCell>
</DataTemplate>
<DataTemplate x:Key="MusicDefaultTemplate">
    <ViewCell>
       <Button Text="Play this!"/>
    </ViewCell>
</DataTemplate>
<DataTemplate x:Key="DocumentDefaultTemplate">
    <ViewCell>
       <Label Text="show filename at least"/>
    </ViewCell>
</DataTemplate>

<!-- The DocumentTemplateSelector handles file extensions differently -->
<DataTemplate x:Key="MyTxtTemplate">
    <ViewCell>
        <Label Text="This is a text file"/>
    </ViewCell>
</DataTemplate>
<DataTemplate x:Key="MyPdfTemplate">
    <ViewCell>
        <Label Text="This is a pdf file"/>
    </ViewCell>
</DataTemplate>
<DataTemplate x:Key="MyXmlTemplate">
    <ViewCell>
        <Label Text="This is a xml File"/>
    </ViewCell>
</DataTemplate>

<!--provide specific template selectors with initiated datatemplates-->
<templateSelectors:PictureTemplateSelector x:Key="pictureTemplateSelector" DefaultTemplate="{StaticResource PictureDefaultTemplate}"/>
<templateSelectors:MusicTemplateSelector x:Key="musicTemplateSelector" DefaultTemplate="{StaticResource MusicDefaultTemplate}"/>
<templateSelectors:DocumentTemplateSelector x:Key="documentTemplateSelector" DefaultTemplate="{StaticResource DocumentDefaultTemplate}"
                                                                     TxtTemplate="{StaticResource MyTxtTemplate}"
                                                                     PdfTemplate="{StaticResource MyPdfTemplate}"
                                                                     XmlTemplate="{StaticResource MyXmlTemplate}"/>

<!--provide superior template selector with other template selectors -->
<templateSelectors:MyItemTemplateSelector x:Key="myItemTemplateSelector"
                                          PictureTemplateSelector="{StaticResource pictureTemplateSelector}"
                                          MusicTemplateSelector="{StaticResource musicTemplateSelector}"
                                          DocumentTemplateSelector="{StaticResource documentTemplateSelector}"/>

Последнее связывание MyItems (объекты типа BaseViewModel) и установка улучшенного селектора шаблона. Здесь я установил CachingStrategy на RecycleElementAndDataTemplate:

<ListView ItemsSource="{Binding MyItems}"
          ItemTemplate="{StaticResource myItemTemplateSelector}"
          IsPullToRefreshEnabled="True"
          CachingStrategy="RecycleElementAndDataTemplate"/>

Исключение выдается в DocumentTemplateSelector.

using System;
using Xamarin.Forms;
using MyApp.ViewModels;

namespace MyApp.TemplateSelectors
{
    public class DocumentTemplateSelector : DataTemplateSelector
    {
        public DataTemplate TxtTemplate { get; set; }
        public DataTemplate PdfTemplate { get; set; }
        public DataTemplate XmlTemplate { get; set; }

        public DataTemplate DefaultTemplate { get; set; }

        protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
        {
            var doc = (DocumentViewModel)item;
            switch (doc.FileExtension)
            {
                case "txt":
                    return TxtTemplate; // Exception
                case "pdf":
                    return PdfTemplate; // Exception 
                case "xml":
                    return XmlTemplate; // Exception
                default:
                    return DefaultTemplate; // Exception
            }
        }
    }
}

Выдается следующее исключение:

System.NotSupportedException: RecycleElementAndDataTemplate требует, чтобы DataTemplate активировался, когда ctor принимает тип.

@ IvanIčin @ G.hakim Меня не беспокоит, что механизм по умолчанию не работает, но после изучения кода форм xamarin из github я не вижу другого решения, чтобы заставить его работать без передачи типа в DataTemplate ctor.

...