unoplatform / uno

Open-source platform for building cross-platform native Mobile, Web, Desktop and Embedded apps quickly. Create rich, C#/XAML, single-codebase apps from any IDE. Hot Reload included! 90m+ NuGet Downloads!!
https://platform.uno
Apache License 2.0
9.04k stars 734 forks source link

The bug of DataTemplateSelector in ListView #17461

Open lindexi opened 4 months ago

lindexi commented 4 months ago

Current behavior

This problem is difficult to describe in words, although it is a very simple problem to reproduce.

The data on the list is messed up.

Expected behavior

The correspondence between the data in a row is correct.

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

  1. Create an empty Uno project and add the ListView to xaml.
  2. Add the ContentPresenter in DataTemplate of the ListView. And then set the ContentTemplateSelector to ContentPresenter with FooDataTemplateSelector
  3. The code of FooDataTemplateSelector is:
public class FooDataTemplateSelector : DataTemplateSelector
{
    protected override DataTemplate SelectTemplateCore(object item)
    {
        UIElement view = new ReadonlyValueEditorView(item);

#if HAS_UNO
        return new DataTemplate(() => view);
#else
        return null!;
#endif
    }
}

public class ReadonlyValueEditorView : UserControl
{
    public ReadonlyValueEditorView(object initialValue)
    {
        this.DataContext(
            new BindableObjectEditorModel()
                .WithModel(x => x.Model.Value = initialValue),

            (v, vm) => v
                .Content(new TextBlock()
                    .Text(x => x.Binding(() => vm.Value))
                ));
    }
}

public partial record ObjectEditorModel
{
    public object? Value { get; set; }
}

internal static class EditorViewExtensions
{
    public static TBindable WithModel<TBindable>(this TBindable bindableModel, Action<TBindable> setter)
        where TBindable : BindableViewModelBase
    {
        setter(bindableModel);
        return bindableModel;
    }
}

Running the project with net8.0-desktop target framework, and then you can find the data on the list is messed up.

All the MainPage.xaml code:

<Page x:Class="CadeqaciwhiYibiruhache.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:CadeqaciwhiYibiruhache"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Page.Resources>
    <local:FooDataTemplateSelector x:Key="FooDataTemplateSelector"/>
  </Page.Resources>

  <Grid>
    <ListView x:Name="FooListView">
      <ListView.ItemTemplate>
        <DataTemplate x:DataType="local:DataModel">
          <Grid  ColumnDefinitions="*,*">
            <TextBlock Grid.Column="0" Text="{x:Bind Value}"/>
            <ContentPresenter Grid.Column="1" Content="{x:Bind}"
                              ContentTemplateSelector="{StaticResource FooDataTemplateSelector}" />
          </Grid>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </Grid>
</Page>

All the MainPage.xaml.cs code:

using Uno.Extensions.Reactive.Bindings;

namespace CadeqaciwhiYibiruhache;

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();

        var list = new List<DataModel>();
        for (int i = 0; i < 100; i++)
        {
            list.Add(new DataModel(i.ToString()));
        }

        FooListView.ItemsSource = list;
    }
}

public class FooDataTemplateSelector : DataTemplateSelector
{
    protected override DataTemplate SelectTemplateCore(object item)
    {
        UIElement view = new ReadonlyValueEditorView(item);

#if HAS_UNO
        return new DataTemplate(() => view);
#else
        return null!;
#endif
    }
}

public class ReadonlyValueEditorView : UserControl
{
    public ReadonlyValueEditorView(object initialValue)
    {
        this.DataContext(
            new BindableObjectEditorModel()
                .WithModel(x => x.Model.Value = initialValue),

            (v, vm) => v
                .Content(new TextBlock()
                    .Text(x => x.Binding(() => vm.Value))
                ));
    }
}

public partial record ObjectEditorModel
{
    public object? Value { get; set; }
}

internal static class EditorViewExtensions
{
    public static TBindable WithModel<TBindable>(this TBindable bindableModel, Action<TBindable> setter)
        where TBindable : BindableViewModelBase
    {
        setter(bindableModel);
        return bindableModel;
    }
}

public record DataModel(string Value);

You can find my repro demo code in https://github.com/lindexi/lindexi_gd/tree/3271e3dbebfcf85e04f1c329ca3a802cdcbee87b/UnoDemo/CadeqaciwhiYibiruhache

Workaround

No response

Works on UWP/WinUI

None

Environment

No response

NuGet package version(s)

global.json:

{
  // To update the version of Uno please update the version of the Uno.Sdk here. See https://aka.platform.uno/upgrade-uno-packages for more information.
  "msbuild-sdks": 
  {
    "Uno.Sdk": "5.2.175"
  },
  "sdk": 
  {
    "allowPrerelease": false
  }
}

Affected platforms

No response

IDE

No response

IDE version

No response

Relevant plugins

No response

Anything else we need to know?

No response

jeromelaban commented 6 days ago

@lindexi would you know if this still happens in the latest builds?

lindexi commented 5 days ago

@jeromelaban Yes, we can find this issues in 5.5.43 version.