7
votes

Est-il possible d'accélérer l'ajout de nouveaux éléments à une page lors de l'utilisation du backend C #?

J'ai du code qui fonctionne mais je remarque que la création des éléments de la page est assez lente.

Voici ce que j'ai jusqu'à présent. Notez que je n'ajoute pas tout en même temps car j'ai trouvé que lorsque j'ai fait la création de la page était encore plus lente.

<?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>

Pour référence, voici le BadgeGridTemplate que j'ai codé: p>

    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;
    }


5 commentaires

Semble être assez optimisé pour moi!


Avez-vous le code pour App.cardSetWithWordCount? Est-ce juste une variable?


@ G.hakim - le problème est que cela semble prendre 1 à 2 secondes pour s'afficher et que lorsque j'ai chronométré les opérations de la base de données, ce n'était que des millisecondes. Je me demande donc s'il est possible d'accélérer la création de ces éléments.


Au lieu d'essayer de créer plusieurs mises en page imbriquées, essayez d'utiliser une seule mise en page comme une grille ou une mise en page flexible pour obtenir le même aspect et la même sensation. Plus le nombre de mises en page est élevé, plus le rendu prendra du temps.


Le problème est que cet exemple de code en question essaie de rendre tous les 100 éléments à l'écran à la fois, même si l'utilisateur n'en regarde que quelques-uns à la fois. C'est pourquoi vous envisagez un délai de 1 à 3 secondes.


4 Réponses :


0
votes

Si l'ordre est important, je n'ai pas vu d'autre moyen de le faire. Si l'ordre n'a pas d'importance, vous pouvez diviser le StackLayout en plusieurs StackLayouts et ajouter simplement les éléments individuels à l'intérieur de threads asynchrones en utilisant Task.WhenAll .

Tâche.WhenAll, c'est comme si plusieurs personnes travaillaient pour vous en même temps, au lieu d'une seule personne.


1 commentaires

Task.WhenAll est exécuté sur le fil d'arrière-plan, je pense que cela vaut la peine d'être mentionné



4
votes

Ce que nous essayons de faire:

Prenez une liste de données d'une source, mettez-les dans une liste

Ce que fait votre code:

en prenant construction de données (toutes les 100 AddSwitchRows) UI alors. Ces centaines d'interfaces utilisateur doivent être rendues en même temps à l'écran. (même si l'utilisateur ne va pas tous les regarder)

Suggestion: Comment faire cela:

Je suggère fortement d'utiliser une Listview. Ou FlowListview pour les grilles https://github.com/daniel-luberda/DLToolkit.Forms. Contrôles

Pourquoi?

Listview essaiera uniquement de dessiner l'interface utilisateur pour l'écran que l'utilisateur regarde.

S'il y a un millier d'articles supplémentaires nécessaires. Il ne sera construit que lorsque l'utilisateur fera défiler jusqu'à cette partie.

Si vous souhaitez créer plusieurs types de cellules qui dépendent des données que vous recevez, nous devons utiliser DataTemplate avec ListView

Plus d'informations sur les documents officiels de Microsoft

Exemple J'ai ajouté 1 000 éléments dans l ' ItemSource de listView lorsque j'ai cliqué sur le bouton dans l'en-tête. Vous pouvez ajouter votre modèle dans la balise Listview.datatemplate et lier votre ViewModel à cette vue Afficher Et si vous souhaitez modifier la vue de l'élément en fonction d'une valeur de propriété. Utilisez sélecteur DataTemplate a > propriété de ListView

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>();
        }
    }

ViewModel

<?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>

1 commentaires

Veuillez me dire si vous voulez un exemple encore plus détaillé. J'apprend peut-être quelque chose de nouveau.



0
votes

Vous pouvez essayer d'utiliser BatchBegin () sur la variable stacks avant que la boucle de child s'ajoute dans votre fonction AddSwitchRows et BatchCommit () après la boucle. Et si cela fonctionne, faites de même avec votre variable de pile parent st dans CreateSwitchSection.

Ie. stacks.BatchBegin (); foreach var x dans les données { … } stacks.BatchCommit ();

Cela peut le résoudre car de nombreuses langues affichant des formulaires aiment recalculer la mise en page chaque fois (cher) la collection / la liste / les enfants sont ajoutés / supprimés / mis à jour. Le traitement par lots permet de recalculer la mise en page une fois au lieu de chaque fois que la liste change.


0 commentaires

0
votes

Je pense que le problème est dans App.cardSetWithWordCount Cela peut être une requête linq Je pense que cela va à db à chaque fois. Il suffit de l'exécuter une fois et de l'enregistrer sur la variable ou la propriété

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

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

        return ints.ToArray();

Linq est exécuté quand to list ou toarray est appelé.

  var globalApp_cardSetWithWordCount = App.cardSetWithWordCount.ToList()

puis utilisez le même format de prise et de saut


0 commentaires