AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.63k stars 2.22k forks source link

Switching tabs slow when tabs contain large DataGrids #2873

Closed brogan-ohara closed 3 years ago

brogan-ohara commented 5 years ago

I have multiple TabItems in a TabControl, each with a DataGrid of 100+items. When I switch tabs, the first switch takes about 5 seconds and every subsequent switch takes about 1 sec. I also notice that the application's memory usage jumps up every tab switch. Is this expected?

brogan-ohara commented 5 years ago

Here's the link to the code I am working on: https://github.com/brogan-ohara/TestApp

ahopper commented 5 years ago

In running this I noticed memory use jumps 100Mb just by moving the mouse over each row.

ahopper commented 5 years ago

I think there is a problem with the size the DataGrid initially thinks it is which causes it to create far more rows than is needed for display. Setting the Height to 400 on the DataGrids makes it much quicker, ControlCatalog also has the same issue.

Gillibald commented 5 years ago

Removing the StackPanel should improve performance

ahopper commented 5 years ago

This is an interesting problem with using virtualizing controls in complex layouts. The current behaviour of DataGrid and ListBox is not good but I'm not sure how it should ideally work. If you have only a few items there maybe times when you want to shrink the space used. In ControlCatalog the DataGrid gets an initial Measure height of Infinity, if you capture this and set height to zero it all works quickly but I'm not sure that is a real solution.

Gillibald commented 5 years ago

The infinite space that comes from StackPanel is the issue here. When that is removed and DataGrid is still measured with infinite space there is something else going wrong.

ahopper commented 5 years ago

Yep in this case I think that is the only issue, but the current control catalog also has the same issue and it is not obvious to me where the infinity is coming from there. I just wonder if virtualizing controls should have special behaviour during measurement, or at least a warning if Infinity is passed.

brogan-ohara commented 5 years ago

I think there is a problem with the size the DataGrid initially thinks it is which causes it to create far more rows than is needed for display. Setting the Height to 400 on the DataGrids makes it much quicker, ControlCatalog also has the same issue.

Are you saying that manually setting the height is a possible solution or and indicator of the problem. Because when I manually set the height I can't scroll and see the rest of the data.

darkcharmander commented 3 years ago

I would like to say that I have the same problem, only then the whole DataGrid causes a massive performance hit. Changing the height to a fixed value, 'fixes' this. Telling the DataGrid to properly fill the parent (Via ActualHeight with RelativeSource) , doesn't work.

Whenever I attach an array with over 1200 elements to the DataGrid, the RAM usage skyrockets: about 140MB of extra RAM usage, and the FPS drops to almost zero. The DataGrid only lists the strings in this array. (File names) As I've said before: setting the height to a fixed value, will prevent this problem. That isn't a solution however! As it makes the layout very problematic.

It's not often that a directory has over 1200 files in it, but it would be great if the DataGrid would utilize virtualization properly.

Removing the StackPanel/DockPanel/WrapPanel wrapper doesn't do anything.

donandren commented 3 years ago

Virtualization of DataGrid in control catalog tabs is not working for some reason (it's measured with infinity??), but there is a way to overcome that with some custom hacky code until this is fixed in datagrid example:

<HackyPanel>
   <DataGrid Items=....
</HackyPanel>
    using System.Linq;
    using Avalonia;
    using Avalonia.Controls;
    using Avalonia.VisualTree;
    public class HackyPanel : Panel// or Decorator
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            if (double.IsInfinity(availableSize.Height))
            {
                var c = this.GetVisualAncestors().OfType<IControl>().FirstOrDefault(v => v.IsArrangeValid && !v.Bounds.IsEmpty);
                if (c != null)
                {
                    availableSize = availableSize.WithHeight(c.Bounds.Height);
                }
            }
            return base.MeasureOverride(availableSize);
        }
    }
darkcharmander commented 3 years ago

@donandren I can confirm that your code sample fixes the problem. It is a much better temporary fix than adding a fixed height to the DataGrid. Thanks! Let's hope that the virtualization issue will be addressed soon.