Search code examples
c#datatemplatedatatemplateselectoravaloniaui

Selecting a DataTemplate based on DataContext property in Avalonia


I'm implementing a UserControl which should display a list of settings:

public class SettingPropertyItem {
    string Name { get; }
    Type ValueType { get; }
    object Value { get; set; }
}

Based on each type in ValueType a different DataTemplate should be used.
To facilitate this the UserControl has the following Control with a SettingPropertyItem as its DataContext:

<UserControl x:Class="AVDump3Gui.Controls.Settings.SettingsView">
    ...
    <ItemsControl Items="{Binding Properties}" Margin="16,0,0,0">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
            ...
                <ContentControl Content="{Binding}"/>
            ...
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    ...
</UserControl>

Then the view where the Usercontrol is used, I've added a DataTemplate into its DataTemplates:

<sv:SettingsView.DataTemplates>
  <DataTemplate DataType="{x:Type vm:SettingPropertyItem}">
    ...
  </DataTemplate>
</sv:SettingsView.DataTemplates>

So far so good, everything works as expected. But now I'm a bit stumped as I don't know how to apply different DataTemplates based on a Property within the DataContext.
With WPF a DataTemplateSelector or Triggers seem to be the way to go (ignoring additional Frameworks), but they don't seem to exist in Avalonia. I've also tried styles but the selector doesn't seem to be able to access DataContext properties.

How can this be done?


Solution

  • In Avalonia DataTemplateSelector isn't needed because you can just implement IDataTemplate yourself and select the template there.

    i. e.

    public class MyTemplateSelector : IDataTemplate
    {
        public bool SupportsRecycling => false;
        [Content]
        public Dictionary<string, IDataTemplate> Templates {get;} = new Dictionary<string, IDataTemplate>();
    
        public IControl Build(object data)
        {
            return Templates[((MyModel) data).Value].Build(data);
        }
    
        public bool Match(object data)
        {
            return data is MyModel;
        }
    }
    
    public class MyModel
    {
        public string Value { get; set; }
    }
    
      <ItemsControl>
        <ItemsControl.Items>
          <scg:List x:TypeArguments="local:MyModel">
            <local:MyModel Value="MyKey"/>
            <local:MyModel Value="MyKey2"/>
          </scg:List>
        </ItemsControl.Items>
        <ItemsControl.DataTemplates>
          <local:MyTemplateSelector>
            <DataTemplate x:Key="MyKey">
              <TextBlock Background="Red" Text="{Binding Value}"/>
            </DataTemplate>
            <DataTemplate x:Key="MyKey2">
              <TextBlock Background="Blue" Text="{Binding Value}"/>
            </DataTemplate>
            
          </local:MyTemplateSelector>
        </ItemsControl.DataTemplates>
      </ItemsControl>