Search code examples
c#datatemplateavaloniauidatatemplateselectoravalonia

Selecting a DataTemplate based on ViewModel type in Avalonia ItemsControl


There is UserControl with ItemsControl inside and it is required to show items with different DataTemplate based on Items collection element type.

Following TemplateSelector and XAMLs created based on this answer (Selecting a DataTemplate based on DataContext property in Avalonia)

    <ItemsControl Items="{Binding Items}">
        <ItemsControl.DataTemplates>
            <views:ItemsTemplateSelector>
                <DataTemplate x:Key="{x:Type itemViewModels:Item1ViewModel}">
                    <itemsViews:Item1View/>
                </DataTemplate>
                <DataTemplate x:Key="{x:Type itemViewModels:Item2ViewModel}">
                    <itemsViews:Item2View/>
                </DataTemplate>
            </views:ItemsTemplateSelector>
        </ItemsControl.DataTemplates>
    </ItemsControl>
        public ItemsViewModel()
        {
            this.Items = new ObservableCollection<IItemViewModel>();
            this.Items.Add(new Item1ViewModel("Item1"));
            this.Items.Add(new Item2ViewModel("Item2"));
        }

        public ObservableCollection<IitemViewModel> Items { get; }
    public class ItemsTemplateSelector : IDataTemplate
    {
        public bool SupportsRecycling => false;

        [Content]
        public Dictionary<Type, IDataTemplate> Templates { get; } = new Dictionary<Type, IDataTemplate>();

        public IControl Build(object data)
        {
            var type = data.GetType();
            var template = this.Templates[type];
            var control = template.Build(data);

            return control;
        }

        public bool Match(object data)
        {
            return data is IItemViewModel;
        }
    }
    public interface IItemViewModel
    {
        string Name { get; }
    }
    public class Item1ViewModel : IItemViewModel
    {
        public Item1ViewModel (string name)
        {
            this.Name = name;
        }

        public string Name { get; }
    }
<UserControl
  xmlns="https://github.com/avaloniaui"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  x:Class="Desktop.Views.Items.Item1View">

  <TextBlock Text="{Binding Name}"/>

</UserControl>

In Runtime exception appears:

Avalonia.Markup.Xaml.XamlLoadException: 'No precompiled XAML found for Desktop.Views.Items.Item1View, make sure to specify x:Class and include your XAML file as AvaloniaResource'

If in ItemsControl instead of <itemsViews:Item1View/> specify <TextBlock Text="{Binding Name}"/>, then everything works well. But I would like to split the markup for each item into a separate XAML file with separate ViewModel. Items can be completely different.

Is it possible to solve this problem so that ItemsControl will select a DataTemplate based on ViewModel type?


Solution

  • To solve this I manually edited project file *.csproj added AvaloniaResource element

    <AvaloniaResource Include="..\Desktop\Views\Items*.xaml">
        <SubType>Designer</SubType>
    </AvaloniaResource>