Dear Xamarin Community,
I have recently converted all my Listviews to Collectionviews to increase the performance. This works great, but I'm looking to increase the performance even more when it comes to the layout used as the ItemTemplate.
Specifications:
- Multiple columns where the width for each column is configurable. I need to be able to change the width on all columns and also hide columns by setting the width to 0. In the same way it should be possible show columns with default width 0 by setting the width.
- The widht of the columns should be relative to each other to not create a massive breaking change in configurations. The width of each column is today binded to a GridLength property that uses GridLength.Star for width > 0 and GridlLength.Absolute if the width is 0.
- The height must be dynamic. Each column consist of a single label. All the information in each label must be shown, so the height needs to adapt to the length of the labels.
- It is desirable to be able to add custom columns in runtime. This is not possible in the current solution, but there is a high demand for this feature. This is not a part of increasing the performance, and not an absolute must at this time, so don't be shy to shout out some performance tips if you don't have a solution for this part.
Current Implementation:
<CollectionView x:Name="OrderCollectionView" Grid.Row="2" Grid.Column="0" ItemsSource="{Binding Orders}" SelectedItem="{Binding SelectedOrder, Mode=TwoWay}" SelectionMode="Single" SelectionChangedCommand="{Binding SelectionChangedCommand}">
<CollectionView.ItemTemplate>
<DataTemplate>
<CustomGrid.ColumnSpacing="5" Padding="5,10,5,0">
<CustomGrid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference OrderCollectionView}, Path=BindingContext.ItemTappedCommand}" CommandParameter="{Binding .}"/>
</CustomGrid.GestureRecognizers>
<CustomGrid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="6"/>
</CustomGrid.RowDefinitions>
<CustomGrid.ColumnDefinitions>
<ColumnDefinition Width="{Binding OrdernoColumnWidth, Mode=TwoWay}" BindingContext="{Binding Source={x:Reference OrderCollectionView}, Path=BindingContext}"/>
<ColumnDefinition Width="{Binding CustomernoColumnWidth, Mode=TwoWay}" BindingContext="{Binding Source={x:Reference OrderCollectionView}, Path=BindingContext}"/>
<ColumnDefinition Width="{Binding CustomernameColumnWidth, Mode=TwoWay}" BindingContext="{Binding Source={x:Reference OrderCollectionView}, Path=BindingContext}"/>
<ColumnDefinition Width="{Binding OrderlinesColumnWidth, Mode=TwoWay}" BindingContext="{Binding Source={x:Reference OrderCollectionView}, Path=BindingContext}"/>
<ColumnDefinition Width="{Binding PackagesColumnWidth, Mode=TwoWay}" BindingContext="{Binding Source={x:Reference OrderCollectionView}, Path=BindingContext}"/>
<ColumnDefinition Width="{Binding WeightColumnWidth, Mode=TwoWay}" BindingContext="{Binding Source={x:Reference OrderCollectionView}, Path=BindingContext}"/>
</CustomGrid.ColumnDefinitions>
<Label Text="{Binding Path=[orderno]}" Grid.Column="0" FontSize="17" VerticalTextAlignment="Center"/>
<Label Text="{Binding Path=[customerno]}" Grid.Column="1" FontSize="17" VerticalTextAlignment="Center"/>
<Label Text="{Binding Path=[customername]}" Grid.Column="2" FontSize="17" VerticalTextAlignment="Center"/>
<Label Text="{Binding Path=[orderlines]}" Grid.Column="3" FontSize="17" VerticalTextAlignment="Center"/>
<Label Text="{Binding Path=[packages]}" Grid.Column="4" FontSize="17" VerticalTextAlignment="Center"/>
<Label Text="{Binding Path=[weight]}" Grid.Column="5" FontSize="17" VerticalTextAlignment="Center"/>
<BoxView HeightRequest="1" BackgroundColor="#BDBDBD" Grid.Row="1" Grid.ColumnSpan="6" Margin="-5,5,-5,0" VerticalOptions="End"/>
</CustomGrid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
To hide the columns with width = 0 I use a custom controller that inherits from Grid. The only thing this does is to override the OnSizeAllocated to set the HeightRequest to 0 if the width is 0 to completely hide the column, and set it to -1 to get the auto height back. Not sure if this is still needed, but it was in older versions of Xamarin forms. It also seems like setting the height back to -1 does not work as intended anymore. Implementation of the custom Grid:
public class CustomGrid : Grid
{
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
foreach (View child in Children)
{
if (child.Width == 0 || child.Width < -1)
child.HeightRequest = 0;
else
child.HeightRequest = -1;
}
}
}
Problem:
With one or two columns it works just fine. With 5-6 columns the performance is not like it should be, even if half of the columns are hidden with width = 0. I guess they are included in the measurement of the layout anyway, so it doesn't really matter if they are visible or not(?). When assigning some items to the ItemSource, populating the Collectionview "lags". You can see each item beeing added one by one, while with fewer columns they all pop up at the same time without the lagging experience. I'm aware that using Height = Auto in a Grid is a performance hit, and if I change this part the performance is a lot better. But I still need to display all the information in the labels, which means that the height will vary, and I haven't found another good solution for this.
I see a lot of cool stuff with great performance arround the community, so I hope there is someone out there with some knowledge they would like to share about increasing the performance of this kind of layout. It will be greatly appreciated!