Удаление сетки UWP выполняется слишком медленно

#performance #layout #uwp #grid

#Производительность #макет #uwp #сетка

Вопрос:

У меня есть сетка внутри ListBox в моем приложении UWP C #. С маленькой сеткой проблем нет. Однако, когда сетка содержит более 50 ячеек с несколькими строками и столбцами, удаление сетки из родительского элемента происходит очень медленно. Это занимает более 1 минуты или около 2 минут. Я пытался скрыть это, изменив его видимость на свернутую или непрозрачность на 0 или создав исполняемый файл выпуска, но все равно слишком медленно, как и раньше. ToList().Clear() в некоторых случаях работает быстро, но недостаточно.

 ListBox rootBox = new ListBox();
rootBox.Items.Add(grid);              // adding a complex Grid with over 50 cells with inner UI elements like TextBlock, TextBox and so on.
rootBox.Items.Remove(grid);     <--- takes about 2 minutes with CPU utilization under 15% in my modern PC
 

В UWP нет API для приостановки и возобновления обновления макета.

Динамическое управление элементом сетки UWP кажется мне непрактично медленным.

Я пытался найти способ оптимизировать производительность для пользовательского интерфейса Grid, но потерпел неудачу.

Профилирование показало мне, что задача компоновки занимает около 50% процессора процесса, но не интенсивно даже в 1 ядре. Это означает, что медлительность не связана с вычислениями, требующими больших затрат процессора.


О, я попытался упростить проблему и нашел дело. Сетка внутри нескольких вложенных списков! Вложение большего количества списков замедляет работу программы. Вы можете воспроизвести случай, нажав нижнюю кнопку «Очистить» программы ниже. Код воспроизведения:

—————— MainPage.xaml ——————

 <Page
    x:Class="GridSlow.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:GridSlow"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Loaded="OnLoaded_Page"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid Name="rootGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ScrollViewer HorizontalScrollBarVisibility="Visible"  Grid.Row="0">
            <ListBox Name="innerList">
            </ListBox>
        </ScrollViewer>
        <Button Content="Clear" Click="Button_Click" Grid.Row="1"/>
    </Grid>
</Page>
 

———————— MainPage.xaml.cs ————————

 using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;


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

        private void OnLoaded_Page(object sender, RoutedEventArgs args)
        {
            Grid grid = new Grid();

            for (int row = 0; row < 50;   row)
            {
                RowDefinition rowDef = new RowDefinition();
                rowDef.Height = new GridLength(0, GridUnitType.Auto);
                rowDef.MinHeight = 10;
                grid.RowDefinitions.Add(rowDef);

                for (int col = 0; col < 2;   col)
                {
                    ColumnDefinition colDef = new ColumnDefinition();
                    colDef.Width = new GridLength(0, GridUnitType.Auto);
                    colDef.MinWidth = 10;
                    grid.ColumnDefinitions.Add(colDef);

                    Border border = new Border() { BorderBrush = new SolidColorBrush(Colors.DarkGray), BorderThickness = new Thickness(1) };
                    TextBox textBox = new TextBox();
                    textBox.Text = "aaa";
                    border.Child = textBox;
                    grid.Children.Add(border);
                    Grid.SetRow(border, row);
                    Grid.SetColumn(border, col);
                }
            }

            ListBox list2 = new ListBox();

            ListBox list3 = new ListBox();

            ListBox list4 = new ListBox();

            ListBox list5 = new ListBox();

            ListBox list6 = new ListBox();

            TextBox box2 = new TextBox();
            box2.Margin = new Thickness(1);
            list2.Items.Add(box2);
            list2.Items.Add(list3);

            list3.Items.Add(new TextBox());
            list3.Items.Add(list4);

            list4.Items.Add(list5);

            list5.Items.Add(list6);

            list6.Items.Add(grid);

            innerList.Items.Add(list2);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            innerList.Items.Clear();
        }
    }
}
 

Комментарии:

1. Я не могу воспроизвести вашу проблему. Не могли бы вы предоставить подробный код?

2. Я добавил код воспроизведения. Спасибо за проявленный интерес.

Ответ №1:

В вашем макете есть список с именем innerList со сложным элементом, Listbox2 вложен в элемент, а текстовое поле и ListBox3 вложены в ListBox2, что является еще одним сложным макетом. Это бессмысленное вложение элементов управления.

Эта многоуровневая вложенность приводит к тому, что разработчик тратит много времени на рендеринг. Поэтому, когда вы очищаете элементы, макет будет загружаться очень медленно.

Пожалуйста, удалите ненужную вложенность элементов управления, как показано ниже.

 private void OnLoaded_Page(object sender, RoutedEventArgs args)
    {
        Grid grid = new Grid();

        for (int row = 0; row < 50;   row)
        {
            RowDefinition rowDef = new RowDefinition();
            rowDef.Height = new GridLength(0, GridUnitType.Auto);
            rowDef.MinHeight = 10;
            grid.RowDefinitions.Add(rowDef);

            for (int col = 0; col < 2;   col)
            {
                ColumnDefinition colDef = new ColumnDefinition();
                colDef.Width = new GridLength(0, GridUnitType.Auto);
                colDef.MinWidth = 10;
                grid.ColumnDefinitions.Add(colDef);

                Border border = new Border() { BorderBrush = new SolidColorBrush(Colors.DarkGray), BorderThickness = new Thickness(1) };
                TextBox textBox = new TextBox();
                textBox.Text = "aaa";
                border.Child = textBox;
                grid.Children.Add(border);
                Grid.SetRow(border, row);
                Grid.SetColumn(border, col);
            }
        }
        innerList.Items.Add(grid);
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        innerList.Items.Clear();
    }
 

Комментарии:

1. Вместо этого я заменил ListBox на StackPanel. Теперь это работает очень быстро!

2. Добавление текстового поля 1100 в макет сетки через grid. Дочерние элементы. Добавление занимает около 10 секунд. Есть ли какой-либо способ оптимизировать производительность в UWP?

3. @HeeSuKim Каждое из ваших текстовых полей имеет границу родительского элемента управления. Я предлагаю удалить границу.