dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.2k stars 1.75k forks source link

Nested FlexLayout/BindableLayout Very Slow #12908

Closed grumpymonk closed 1 year ago

grumpymonk commented 1 year ago

Description

I am writing a desktop application for genealogy. In rendering the family tree, I am using two FlexLayouts (one inside the other) to create the visual representation of matrix data (list of list of an "ancestor"). The basic layout is as follows; I've omitted non-relevant data binding and property logic for brevity:

<ScrollView Orientation="Both">
    <Grid> <!-- grid sets desired height/width (computed in code-behind) so that the scrollview scrolls appropriately -->
        <FlexLayout BindableLayout.ItemsSource="{Binding Ancestors}">
            <BindableLayout.ItemTemplate>
                <DataTemplate>
                    <FlexLayout BindableLayout.ItemsSource="{Binding .}">
                         <!-- .... data template ... -->
                    </FlexLayout>
                </DataTemplate>
            </BindableLayout.ItemTemplate>
        </FlexLayout>
    </Grid>
</ScrollView>

This renders what I need, and the logic works. However, just showing five generations takes more than 5 seconds to draw. I know that fetching and generating the appropriate matrix takes less than 300ms, and the lag is definitely after the binding to "Ancestors" is made.

Since I need to be able to scroll on both the X and Y axes, using something like a CollectionView is not feasible, although it is noticeably faster. I also need to be able to click on any of the bound items in the inner template, so setting an inner CollectionView to "InputTransparent" is not a solution.

Note that the inner DataTemplate is another Grid with three Label components. So it is not an overly complex layout. Even a single Label within the inner template fails terribly in performance.

Steps to Reproduce

  1. Create a 2D array (e.g., List<List>) of data; use two for loops for any sort of data -- even a single string is fine
  2. Create a XAML layout similar to the one noted above
  3. Run the app
  4. Expectation: "Reasonable" rendering time, e.g., less than 2 seconds

    Observed: Extremely slow rendering, e.g., 5+ seconds for 5 generations of ancestry data.

    Link to public reproduction project repository

    n/a

    Version with bug

    7.0 (current)

    Last version that worked well

    Unknown/Other

    Affected platforms

    Windows, macOS

    Affected platform versions

    Windows 10/11, macOS Monterey+

    Did you find any workaround?

    No. I tried CollectionView (faster, but not feasible for this app due to reasons cited above), other BindableLayouts (e.g., VerticalStackLayout), and even manually populating a Grid in code-behind. It fails on the render every time.

    It would be nice if (as an alternative) CollectionView could have a property to disable scrolling, so that other input (e.g., Tap/Click) can be captured, the performance of CollectionView can be leveraged, but the scrolling can be handled "externally," such as with the top-level ScrollView.

    Relevant log output

    No response

    ghost commented 1 year ago

    Hi @grumpymonk. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md

    This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

    angelru commented 1 year ago

    @grumpymonk I also experience delays with nested FlexLayout, StackLayout and with CollectionView.

    as here I say: https://github.com/dotnet/maui/issues/12130

    you can try: https://github.com/Keflon/FunctionZero.Maui.Controls https://github.com/Keflon/FunctionZero.Maui.Controls#treeviewzero

    grumpymonk commented 1 year ago

    In the process of creating the repro project, I have (sheepishly) realized the root cause of the issue.

    If, hypothetically, we were looking at a "generation" as only a predecessor's parents (no siblings, partners, etc.), then the best case scenario means that at 10 generations, a FlexLayout would be bound with up to 1024 entries to render. The documentation even says this is not optimal, and that a CollectionView type of control should be used. Compound that with also trying to simultaneously render the previous 9 generations, and that's why we end up waiting a very very long time.

    When I switched the number of generations to show to up to 5, my widget updates fairly quickly (< 1.5s on average).

    Unfortunately, while that is the answer to "why" I was observing such poor performance, this is still a bit of a problem. Is there a virtualization or lazy loading feature available within a BindableLayout, or/additionally a way to make a CollectionView disable handling scrolling?

    Thanks for your time.

    jfversluis commented 1 year ago

    Glad you figured out the root cause for your case at least. I think we have a couple of issues already on CollectionView related issues, also the one right above here so I will be closing this one to not duplicate efforts.

    Right now there is no functionality for lazy loading a BindableLayout. From the docs:

    While scrolling can be provided by wrapping a bindable layout in a ScrollView, this is not recommended as bindable layouts lack UI virtualization.