unoplatform / uno

Build Mobile, Desktop and WebAssembly apps with C# and XAML. Today. Open source and professionally supported.
https://platform.uno
Apache License 2.0
8.62k stars 696 forks source link

`TreeView` - nested elements not applying data template #11942

Open nickrandolph opened 1 year ago

nickrandolph commented 1 year ago

Current behavior

When using a datatemplateselector to define different templates for the type of data in each node, nested elements aren't being rendered using the data template.

This works on WinUI eg image

This doesn't work on other uno targets (wasm, mobile, skia) image

Expected behavior

The correct data template should be applied for each node

How to reproduce it (as minimally and precisely as possible)

UnoApp7.zip

Workaround

No response

Works on UWP/WinUI

None

Environment

No response

NuGet package version(s)

No response

Affected platforms

No response

IDE

No response

IDE version

No response

Relevant plugins

No response

Anything else we need to know?

No response

Youssef1313 commented 1 year ago

Similar issue https://github.com/unoplatform/uno/issues/11926

jeromelaban commented 1 year ago

This is an interesting issue, where in general, the top level element of a DataTemplate is supposed to be a TreeViewItem, but in the template from the sample it is not.

The workaround is easy, where the leaf template can be changed to be:

<DataTemplate x:Key="animalTemplate">
    <TreeViewItem>
        <Border Background="Red"
                MinHeight="20"
                MinWidth="20">
            <TextBlock Text="{Binding Name}" />
        </Border>
    </TreeViewItem>
</DataTemplate>

Though this means that ItemsControl, TreeView or ContentControl should be handling this specific structure properly and at this point, it's only rendering a ToString of the databound item.

ramezgerges commented 1 month ago

I investigated this using the changed from #14692 and it's still broken. The problem is mainly with the interaction of Content<Control|Presenter>s with ContentTemplateSelector (which is set from the ItemTemplateSelector of the wrapping ItemsControl like in the sample). On WinUI (and Uno), something like this works:

Snippet 1

<ContentControl Content="1" ContentTemplate="{StaticResource someTemplate}">
</ContentControl>

but this doesn't work:

Snippet 2

<ContentControl Content="1" ContentTemplateSelector="{StaticResource someTemplateSelector}">
</ContentControl>

This is a problem in this repro because after materializing the nested element the state of the child TreeViewItem is similar to the example above. In fact, the same problem would happen without nesting. Here's a simpler repro:

<UserControl.Resources>
    <DataTemplate x:Key="animalTemplate">
      <Border Background="Red" Width="50" Height="50">
    <TextBlock Foreground="Green" Text="{Binding}" />
      </Border>
    </DataTemplate>

    <local:TreeItemTemplateSelector x:Key="treeItemTemplateSelector" Template="{StaticResource animalTemplate}" />

</UserControl.Resources>

<StackPanel>
      <TreeView x:Name="tvtop" ItemsSource="12" ItemTemplateSelector="{StaticResource treeItemTemplateSelector}" />
</StackPanel>
public class TreeItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate Template { get; set; }
    protected override DataTemplate SelectTemplateCore(object item)
    {
    return Template;
    }
}

Everything seems to be set correctly on our side, but we're just not using the ContentTemplateSelector of the problematic TreeViewitem. The logic for selecting the right template and using it in WinUI's source is very non-trivial and the source of truth seems to be the GetSelectedTemplate method in VirtualizingInformation, so it's very different to what we're doing. Actually, a lot of things that you'd expect to be working on WinUI actually don't.

We can do many workarounds but they wouldn't be very accurate. The most resonable of them would be to add

SetUpdateTemplate();

in ContentPresenter.OnContentTemplateSelectorChanged. This would make some sense, but it will still be different from WinUI. For example, Snippet 2 which does NOT work (i.e. ignores the selector and looks weird) on WinUI, will "work" on our side. I think we can settle for this since we will never really match WinUI's behaviour without porting ContentPresenter and/or ContentControl.