繁体   English   中英

有什么办法可以在使用C#后端时加快向页面添加新元素的速度吗?

[英]Is there any way that I can speed up the adding of new elements to a page when using the C# back end?

我有可行的代码,但我注意到创建页面元素的速度相当慢。

这是我到目前为止所拥有的。 请注意,我没有立即添加所有内容,因为我发现创建页面时速度更慢。

    public void CreateSwitchSection(bool? selected)
    {
        Application.Current.Resources.TryGetValue("FrameBorder", out object frameBorder);
        var st = new StackLayout { Orientation = StackOrientation.Vertical, Spacing = 0 };
        st.Children.Add(AddSwitchRows(selected, App.cardSetWithWordCount.Take(20)));
        st.Children.Add(AddSwitchRows(selected, App.cardSetWithWordCount.Skip(20).Take(20)));
        st.Children.Add(AddSwitchRows(selected, App.cardSetWithWordCount.Skip(40).Take(20)));
        st.Children.Add(AddSwitchRows(selected, App.cardSetWithWordCount.Skip(60).Take(20)));
        st.Children.Add(AddSwitchRows(selected, App.cardSetWithWordCount.Skip(80).Take(20)));
        var fr = new Frame { Style = (Style)frameBorder };
        var fs = new FrameStack { };
        var ht = new HeaderTemplate()
        {
            Text = "CHOOSE CARD SETS FOR THE DECK"
        };
        fs.Children.Add(ht);
        fs.Children.Add(st);
        fs.Children.Add(new LineTemplate());
        fr.Content = fs;
        details.Children.Clear();
        details.Children.Add(fr);
    }

    private StackLayout AddSwitchRows(bool? selected, IEnumerable<CardSetWithWordCount> data)
    {
        var stack = new StackLayout
        {
            Orientation = StackOrientation.Vertical,
            Spacing = 0
        };

        foreach (var x in data)
        {
            var cell = new BadgeGridTemplate
            {
                BindingContext = x,
                Text = x.Name,
                State = selected == true ? "E" : "D",
                Message = x.TotalWordCount.ToString(),
                TapCommand = (Command)vm.SelectCardSetCmd,
                RowId = x.Id,
                Separator = true
            };
            stack.Children.Add(cell);
        }
        return stack;
    }

这里参考的是我编写的BadgeGridTemplate:

<?xml version="1.0" encoding="UTF-8"?>
<t:BaseGridTemplate xmlns="http://xamarin.com/schemas/2014/forms" 
                    xmlns:t="clr-namespace:Japanese.Templates" 
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
                    xmlns:local="clr-namespace:Japanese;assembly=Japanese" 
                    xmlns:b="clr-namespace:Behaviors;assembly=Behaviors" 
                    xmlns:converters="clr-namespace:Japanese.Converters;assembly=Japanese" 
                    x:Class="Japanese.Templates.BadgeGridTemplate" 
                    x:Name="this" 
                    HeightRequest="{DynamicResource GridHeight}" Margin="0"
    Orientation="Vertical" Spacing="0">
    <BoxView HeightRequest="1" HorizontalOptions="FillAndExpand" IsVisible="{Binding Separator, Source={x:Reference this}}" BackgroundColor="{DynamicResource LineColor}" Margin="0" />
    <Grid Padding="20,0" VerticalOptions="CenterAndExpand">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Label Grid.Column="0" Text="{Binding Text,  Source={x:Reference this}}" TextColor="{DynamicResource LabelColor}" Style="{StaticResource LabelText}" VerticalTextAlignment="Center" WidthRequest="30" />
        <t:Button Grid.Column="1" Meta="GsT" RowId="{Binding RowId, Source={x:Reference this}}" State="{Binding State, Source={x:Reference this}}" TapCommand="{Binding TapCommand, Source={x:Reference this}}" Text="{Binding Message, Source={x:Reference this}}" Theme="{Binding Theme}" WidthRequest="30" />
    </Grid>
</t:BaseGridTemplate>

我们想做什么:

从一些来源获取数据列表,将它们放入列表中

你的代码在做什么:

然后立即获取所有数据构建(所有100个AddSwitchRows)UI。 必须在屏幕上一次渲染数百个UI。 (即使用户不打算全部查看)

建议:我们该怎么做:

我强烈建议使用Listview。 或者用于网格的FlowListview https://github.com/daniel-luberda/DLToolkit.Forms.Controls

为什么?

Listview只会尝试为用户正在查看的屏幕绘制UI。

如果还有一千件物品需要。 仅当用户向下滚动到该部分时才会构建它。

如果你想根据你收到的数据制作多种单元格,我们应该使用DataTemplate和ListView

有关Microsoft官方文档的更多信息

示例在单击标题中的按钮时,我在listView的ItemSource中添加了1000个项目。 您可以在Listview.datatemplate标记中添加模板,并将ViewModel绑定到此视图View如果要根据属性值更改项目视图。 使用ListView的DataTemplate选择器属性

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:stackAnswerApp"
             x:Class="stackAnswerApp.MainPage"
             x:Name="Root">
    <ContentPage.BindingContext>
        <local:MainPageViewModel />
    </ContentPage.BindingContext>
    <ListView ItemsSource="{Binding ListItems}" RowHeight="100">
        <ListView.Header>
            <Button Text="Start Adding" Command="{Binding StartAddingItems}" />
        </ListView.Header>
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <!-- whatever is your template -->
                    <StackLayout BackgroundColor="Aqua">
                        <Label Text="{Binding Text1}" />
                        <Button BackgroundColor="Blue"
                                Text="Go" BindingContext="{Binding Source={x:Reference Root},Path=BindingContext}"
                                Command="{Binding  ButtonTapped}" />
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

视图模型

class AModel
    {
        public string Text1 { get; set; }
        public string Text2 { get; set; }
    }

    class MainPageViewModel
    {
        public ObservableCollection<AModel> ListItems { get; set; }
        private const int TotalRows = 1000;

        public ICommand StartAddingItems
        {
            get
            {
                return new Command(async () =>
                {
                    //add too many items in the list
                    await Task.Run(async () => { await AddItemsToList(); });
                });
            }
        }

        private async Task AddItemsToList()
        {
            for (var i = 0; i < TotalRows; i++)
            {
                ListItems.Add(new AModel() {Text1 = $"index {i}", Text2 = $"tap {i}"});
            }
        }

        public ICommand ButtonTapped
        {
            get
            {
                return new Command((() =>
                {
                    //button in the list was tapped
                }));
            }
        }

        public MainPageViewModel()
        {
            ListItems = new ObservableCollection<AModel>();
        }
    }

如果订单很重要,我还没有看到另一种方法。 如果顺序无关紧要,您可以将StackLayout拆分为多个StackLayouts,并使用Task.WhenAll将各个元素添加到异步线程中。

Task.WhenAll就好像有多个人同时为你工作,而不只是一个人。

您可以尝试在stacks变量上使用BatchBegin(),然后在循环之后在您的函数AddSwitchRows和BatchCommit()中添加子循环。 如果可行,请在CreateSwitchSection中对父堆栈变量st执行相同操作。

IE浏览器。 stacks.BatchBegin(); foreach var x in data {...} stacks.BatchCommit();

这可以解决它,因为许多语言显示形式,例如每次(昂贵)添加/删除/更新集合/列表/子项时重新计算布局。 批处理允许布局重新计算一次,而不是每次列表更改。

我认为问题在App.cardSetWithWordCount中它可能是一个linq查询我认为每次都会转到db。只运行一次并保存在变量或属性上

  var globalApp_cardSetWithWordCount = App.cardSetWithWordCount.ToList()

Linq在列表或toarray被调用时被执行。

 List<int> ints = new List<int>();

        foreach(var y in query)
            ints.Add(y.Count());

        return ints.ToArray();

然后使用相同的take和skip格式

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM