I have code that works but I notice it's rather slow to create the page elements.
Here's what I have so far. Note that I'm not adding everything at once as I found that when I did the page creation was even slower.
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;
}
For reference here is the BadgeGridTemplate I coded:
<?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>
Take a list of data from some source, Put them in a list
taking all the data building(all 100 AddSwitchRows ) UI right then. Those hundreds of UI has to be rendered all at once on the screen. (even if the user is not going to look at them all)
I highly suggest using a Listview. Or FlowListview for grids https://github.com/daniel-luberda/DLToolkit.Forms.Controls
Listview will only try to draw UI for the screen user is looking at.
If there are a thousand more items needed. It will be built only when the user scrolls down to that portion.
If you want to make multiple kinds of cells which depends on the data you receive we should use DataTemplate with ListView
More Information on Microsoft's Official Docs
Example I have added 1000 items in the listView's ItemSource when clicked on the button in header. You can add your template in the Listview.datatemplate tag and bind your ViewModel to this view View And if you want to change item's view as per a property value. Use DataTemplate selecter property of ListView
<?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>
ViewModel
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>();
}
}
You can try using BatchBegin() on stacks variable before the loop of child adds in your function AddSwitchRows and BatchCommit() after the loop. And if that works, do the same to your parent stack variable st in CreateSwitchSection.
Ie. stacks.BatchBegin(); foreach var x in data { … } stacks.BatchCommit();
This may resolve it as many languages displaying forms like to recalculate layout every time (expensive) the collection/list/children are added/removed/updated. Batching allows the layout recalculations to be done once instead of every time the list changes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With