C
C#11mo ago
Krogenth

❔ Avalonia nested ItemsRepeater datatemplates

I am attempting to handle rendering UI for a list of lists of objects that implement a base interface in avalonia. The issue isn't the list objects themselves, but that some implementations also hold lists of other objects of the same base type that should also be handled. So for the base list of objects, I know I can make a selector to pick which type of implementing object to render for in the datatemplate list, i.e.:
public class InheritingObjectType : BaseInterface
{
...
public List<BaseInterface> NestedObjects {get;}
};

// other implementing classes

public class SomeSelector : IDataTemplate
{
[Content]
public Dictionary<string, IDataTemplate> Templates { get; private set; } = new();

public IControl Build(object data) =>
Templates[data.GetType().Name].Build(data);

public bool Match(object data)
=> data is BaseInterface && Templates.ContainsKey(data.GetType().Name);
}
public class InheritingObjectType : BaseInterface
{
...
public List<BaseInterface> NestedObjects {get;}
};

// other implementing classes

public class SomeSelector : IDataTemplate
{
[Content]
public Dictionary<string, IDataTemplate> Templates { get; private set; } = new();

public IControl Build(object data) =>
Templates[data.GetType().Name].Build(data);

public bool Match(object data)
=> data is BaseInterface && Templates.ContainsKey(data.GetType().Name);
}
<ItemsRepeater.DataTemplates>
<selectors:SomeSelector>
<DataTemplate x:Key="InheritingObjectType"
DataType="{x:Type types:InheritingObjectType}">
<!-- whatever needs rendered -->
<!-- how to render nested objects? -->
</DataTemplate>
<!-- other datatemplates -->
</selectors:MapOpcodeSelector>
</ItemsRepeater.DataTemplates>
<ItemsRepeater.DataTemplates>
<selectors:SomeSelector>
<DataTemplate x:Key="InheritingObjectType"
DataType="{x:Type types:InheritingObjectType}">
<!-- whatever needs rendered -->
<!-- how to render nested objects? -->
</DataTemplate>
<!-- other datatemplates -->
</selectors:MapOpcodeSelector>
</ItemsRepeater.DataTemplates>
I'm unsure if just defining some UserControl/TemplatedControl that is self-referencing is appropriate for this, or if there might be some other concept I'm just unaware of.
3 Replies
Accord
Accord11mo ago
Looks like nothing has happened here. I will mark this as stale and this post will be archived until there is new activity.
Krogenth
Krogenth11mo ago
Well it took a bit of thinking, but the proper approach is a TreeView, with a selector for the TreeDataTemplates. Something like the following should work, where some inheriting objects have children, while others do not:
namespace Types;

public interface BaseInterface
{
...
};

// define objects
public class InheritingObjectType : BaseInterface
{
...
public List<BaseInterface> NestedObjects {get;}
};

public class OtherInheritingObjectType : BaseInterface
{
...
};
// other implementing classes
namespace Types;

public interface BaseInterface
{
...
};

// define objects
public class InheritingObjectType : BaseInterface
{
...
public List<BaseInterface> NestedObjects {get;}
};

public class OtherInheritingObjectType : BaseInterface
{
...
};
// other implementing classes
namespace viewmodels;

public class ObjectsViewModel
{
// collection to feed TreeView
public ObservableCollection<BaseInterface> Items {get;}
};
namespace viewmodels;

public class ObjectsViewModel
{
// collection to feed TreeView
public ObservableCollection<BaseInterface> Items {get;}
};
// tree data template selector
public class SomeSelector : ITreeDataTemplate
{
[Content]
public Dictionary<string, IDataTemplate> Templates { get; private set; } = new();

public InstancedBinding ItemsSelector(object item)
{
if (item is BaseInterface obj)
{
return ((TreeDataTemplate)Templates[obj.GetType().Name]).ItemsSelector(item);
}
else
{
return null;
}
}

public IControl Build(object data) =>
Templates[data.GetType().Name].Build(data);

public bool Match(object data)
=> data is BaseInterface && Templates.ContainsKey(data.GetType().Name);
}
// tree data template selector
public class SomeSelector : ITreeDataTemplate
{
[Content]
public Dictionary<string, IDataTemplate> Templates { get; private set; } = new();

public InstancedBinding ItemsSelector(object item)
{
if (item is BaseInterface obj)
{
return ((TreeDataTemplate)Templates[obj.GetType().Name]).ItemsSelector(item);
}
else
{
return null;
}
}

public IControl Build(object data) =>
Templates[data.GetType().Name].Build(data);

public bool Match(object data)
=> data is BaseInterface && Templates.ContainsKey(data.GetType().Name);
}
<TreeView Items="{Binding Items}">
<TreeView.DataTemplates>
<selectors:SomeSelector>
<TreeDataTemplate x:Key="InheritingObjectType"
DataType="{x:Type types:InheritingObjectType}"
ItemsSource="{Binding NestedObjects}">
<!-- whatever needs rendered -->
</TreeDataTemplate>
<TreeDataTemplate x:Key="OtherInheritingObjectType"
DataType="{x:Type types:OtherInheritingObjectType}">
<!-- whatever needs rendered -->
</TreeDataTemplate>
<!-- other datatemplates -->
</selectors:MapOpcodeSelector>
</TreeView.DataTemplates>
</TreeView>
<TreeView Items="{Binding Items}">
<TreeView.DataTemplates>
<selectors:SomeSelector>
<TreeDataTemplate x:Key="InheritingObjectType"
DataType="{x:Type types:InheritingObjectType}"
ItemsSource="{Binding NestedObjects}">
<!-- whatever needs rendered -->
</TreeDataTemplate>
<TreeDataTemplate x:Key="OtherInheritingObjectType"
DataType="{x:Type types:OtherInheritingObjectType}">
<!-- whatever needs rendered -->
</TreeDataTemplate>
<!-- other datatemplates -->
</selectors:MapOpcodeSelector>
</TreeView.DataTemplates>
</TreeView>
Accord
Accord11mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.