Custom View Cell in Xamarin Forms UWP

This article describes about one of the performance issue with ListView in Xamarin forms and how to overcome by providing the native or platform specific implementation with custom renderers.

If you define a ListView with Custom View Cells in Xamarin Forms, it tries to repeatedly calculate the scroll position of ListView items when you scroll back and forth which causes more memory leaks and crash the app. This limitation can be overcome by rendering a platform specific cell in to Xamarin forms Listview.

Since Xamarin Forms intended to develop more data driven based applications where performance is matters.
Every Xamarin Forms cell has a renderer for each platform that creates an instance of a native control. View Cell in Xamarin Forms will be turned as a native DataTemplate with the help of ViewCellRenderer class.

Create a CustomViewCell class with binding properties in PCL project and Commands as below:

public class CustomViewCell : ViewCell
    {
         public static readonly BindableProperty NameProperty =
             BindableProperty.Create("Name", typeof(string), typeof(CustomViewCell), "");

        public string Name
        {
            get { return (string)GetValue(NameProperty); }
            set { SetValue(NameProperty, value); }
         }

        public static readonly BindableProperty CategoryProperty =
             BindableProperty.Create("Category", typeof(string), typeof(CustomViewCell), "");

        public string Category
        {
            get { return (string)GetValue(CategoryProperty); }
            set { SetValue(CategoryProperty, value); }
         }

        public static readonly BindableProperty ImageFilenameProperty =
             BindableProperty.Create("ImageFilename", typeof(string), typeof(CustomViewCell), "");

        public string ImageFilename
        {
            get { return (string)GetValue(ImageFilenameProperty); }
            set { SetValue(ImageFilenameProperty, value); }
         }

        private ICommand imageTappedCommand;

         public ICommand ImageTappedCommand
        {
            get
            {
                 return this.imageTappedCommand ?? (this.imageTappedCommand = new Command(this.OnImageTapped));
             }
        }

        private async void OnImageTapped()
        {
             // if you are using MVVM, here you can find the parent Binding Context VM
            // and use the respective command.Execute
            await App.Current.MainPage.DisplayAlert("Alert","Image Tapped","ok");
        }
    }

Now create a Xaml view in PCL and add this custom view cell to ListView control as below:

<ListView x:Name="listView" CachingStrategy="RecycleElement">
      <ListView.ItemTemplate>
         <DataTemplate>
          <local:CustomViewCell Name="{Binding Name}" Category="{Binding Category}" ImageFilename="{Binding ImageFilename}" />
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>

A Xamarin.Forms ListView control is used to display a list of data, which is populated through the ItemSource property. The RecycleElement caching strategy attempts to minimize the ListView memory footprint and execution speed by recycling list cells.
Each row in list contains three items, Name, Category and image which will be rendered from the DataTemplate layout.
Create a CustomRenderer in UWP to render the platform specific cell with DataTemplate. To know more about Custom Renderers, refer my previous article.

[assembly: ExportRenderer(typeof(CustomViewCell), typeof(CustomViewCellRenderer))]
namespace ListViewCell.UWP
{
    public class CustomViewCellRenderer : ViewCellRenderer
    {
        public override Windows.UI.Xaml.DataTemplate GetTemplate(Cell cell)
         {
             // here you can distinguish the datatemplates without template selector
            // if you want to bind multiple templates for a Listview
            return App.Current.Resources["listTemplate"] as Windows.UI.Xaml.DataTemplate;
        }
    }
}

And the layout of DataTemplate in UWP project with binding properties and Commands as below:

<DataTemplate x:Key="listTemplate">
            <Grid Background="LightYellow">
                 <Grid.Resources>
                      <local:ImageConverter x:Name="ConcatImageExtensionConverter" />
                 </Grid.Resources>
                 <Grid.RowDefinitions>
                     <RowDefinition Height="Auto" />
                     <RowDefinition Height="Auto" />
                 </Grid.RowDefinitions>
                 <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="0.40*" />
                     <ColumnDefinition Width="0.40*"/>
                     <ColumnDefinition Width="0.20*" />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.ColumnSpan="2" Foreground="#7F3300" FontStyle="Italic" FontSize="22" VerticalAlignment="Top" Text="{Binding Name}" />
                <TextBlock Grid.RowSpan="2" Grid.Column="1" Foreground="#267F00" FontWeight="Bold" FontSize="12" VerticalAlignment="Bottom" Text="{Binding Category}" />
                <Image Grid.RowSpan="2" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center" Source="{Binding ImageFilename, Converter={StaticResource ConcatImageExtensionConverter}}" Width="50" Height="50"
                        local:ImageItemTappedCommand.Command="{Binding ImageTappedCommand}"/>
                <Line Grid.Row="1" Grid.ColumnSpan="3" X1="0" X2="1" Margin="30,20,0,0" StrokeThickness="1" Stroke="LightGray" Stretch="Fill" VerticalAlignment="Bottom" />
             </Grid>
        </DataTemplate>

Since the DataTemplate has designed in Platform, if you want to bind the commands from Xamarin Forms PCL project to a specific control in this DataTemplate, just define the command in CustomViewCell class and attach to control behaviors.
In this case, I have created a behavior for Image Tapped event and Binding the Command as highlighted above.
Image Tapped event behavior class as below:

  public class ImageItemTappedCommand : DependencyObject
    {
        public static readonly DependencyProperty CommandProperty =
           DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(ImageItemTappedCommand), new PropertyMetadata(null, CommandPropertyChanged));

         public static void SetCommand(DependencyObject attached, ICommand value)
        {
            attached.SetValue(CommandProperty, value);
        }


        public static ICommand GetCommand(DependencyObject attached)
         {
             return (ICommand)attached.GetValue(CommandProperty);
         }


         private static void CommandPropertyChanged(DependencyObject d,
                                        DependencyPropertyChangedEventArgs e)
        {
             // Attach click handler
            (d as Image).Tapped += ImageItemTappedCommand_Tapped;
         }

        private static void ImageItemTappedCommand_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
        {
             // Get control
            var image = (sender as Image);

             // Get command
            ICommand command = GetCommand(image);

             // Execute command
            command.Execute(null);
        }
     }

Download the working sample.

      

By Siva Jagan Dhulipalla   Popularity  (5002 Views)