What is ListView and customize a ListView in Xamarin.Forms


Isn't it fun,

to instantly listen to a blog on the go? PLAY !

 
 

What-is-List-View-and-customize-a-ListView-in-Xamarin.Forms

Table of Content

What is ListView?

A ListView control can be used to display scrollable lists of data in any context. Context actions and data binding are supported by the ListView class.

Components

  1. Headers and footers:

The header and footer components appear at the top and bottom of a list, distinct from the data. Headers and footers can be connected to a different data source than the ListView.

  1. Groups:

A ListView's data can be grouped to make it easier to navigate. The majority of the time, groups are data-bound.

  1. Cells:

Cells are the data items in a ListView. A row of data is represented by each column. You can use one of the built-in cells or create your own unique cell. In XAML or code, both built-in and custom cells can be used/defined.

  1. TextCell and ImageCell are built-in cells that match native controls and are particularly performant.
  2. To display complex data, custom cells are used. A custom cell might, for example, be used to display a music list that contains the album and artist.

Customizing ListView:

A Xamarin application. Types of documents ListView is a view that shows a list of data in a vertical format. This article shows how to construct a custom renderer that contains platform-specific list controls and native cell layouts, providing you more control over the performance of native list controls.

Each and every platform, the Forms view has a renderer that builds a native control object. When a Xamarin.Forms application renders a ListView, the ListViewRenderer class is instantiated in iOS, which then instantiates a native UITableView control. The ListViewRenderer class creates a native ListView control for the Android platform. The ListViewRenderer class on the Universal Windows Platform (UWP) creates a native ListView widget. For more information on Xamarin's renderer and native control classes, visit their website. See Renderer Base Classes and Native Controls for a list of controls that form controls map to.

Creating the Custom ListView Control:

public class NativeListView : ListView
{
public static readonly BindableProperty ItemsProperty =
BindableProperty.Create("Items", typeof(IEnumerable), typeof(NativeListView), new List());
public IEnumerable Items
{
get { return (IEnumerable)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public event EventHandler ItemSelected;
public void NotifyItemSelected(object item)
{
if (ItemSelected != null)
{
ItemSelected(this, new SelectedItemChangedEventArgs(item));
}
}
}



Consuming the Custom Control:


<ContentPage
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="{x:Static local:App.Description}" HorizontalTextAlignment="Center" />
<local:NativeListView Grid.Row="1" x:Name="nativeListView" ItemSelected="OnItemSelected" VerticalOptions="FillAndExpand" />
</Grid>
</ContentPage.Content>
</ContentPage>

NativeListView CustomControl Consume by C#:

public class MainPageCS : ContentPage
{
NativeListView nativeListView;
public MainPageCS()
{
nativeListView = new NativeListView
{
Items = DataSource.GetList(),
VerticalOptions = LayoutOptions.FillAndExpand
};
switch (Device.RuntimePlatform)
{
case Device.iOS:
Padding = new Thickness(0, 20, 0, 0);
break;
case Device.Android:
case Device.UWP:
Padding = new Thickness(0);
break;
}
Content = new Grid
{
RowDefinitions = {
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = new GridLength (1, GridUnitType.Star) }
},
Children = {
new Label { Text = App.Description, HorizontalTextAlignment = TextAlignment.Center },
nativeListView
}
};
nativeListView.ItemSelected += OnItemSelected;
}
}

Creating the Custom Renderer on each Platform

  1. Make a custom ListViewRenderer subclass to render the custom control.
  2. When the listview of xamarin.form is generated, override the custom control's OnElementChanged function and write logic to modify it.
  3. To specify that the custom renderer class will be used to render the Xamarin, add an ExportRenderer attribute to it. Creates a custom control. This attribute is used to tell Xamarin about the custom renderer. Forms are a type of document.

Creating the Custom Renderer on iOS

[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeiOSListViewRenderer))]
namespace CustomRenderer.iOS
{
public class NativeiOSListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
}
if (e.NewElement != null)
{
Control.Source = new NativeiOSListViewSource(e.NewElement as NativeListView);
}
}
}
}
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
NativeiOSListViewCell cell = tableView.DequeueReusableCell (cellIdentifier) as NativeiOSListViewCell;
if (cell == null) {
cell = new NativeiOSListViewCell (cellIdentifier);
}

if (String.IsNullOrWhiteSpace (tableItems [indexPath.Row].ImageFilename)) {
cell.UpdateCell (tableItems [indexPath.Row].Name
, tableItems [indexPath.Row].Category
, null);
} else {
cell.UpdateCell (tableItems [indexPath.Row].Name
, tableItems [indexPath.Row].Category
, UIImage.FromFile ("Images/" + tableItems [indexPath.Row].ImageFilename + ".jpg"));
}
return cell;
}

In the following code sample, the NativeiOSListViewCell class defines the layout for each cell:

public class NativeiOSListViewCell : UITableViewCell
{
UILabel headingLabel, subheadingLabel;
UIImageView imageView;
public NativeiOSListViewCell(NSString cellId) : base(UITableViewCellStyle.Default, cellId)
{
SelectionStyle = UITableViewCellSelectionStyle.Gray;
ContentView.BackgroundColor = UIColor.FromRGB(218, 255, 127);
imageView = new UIImageView();
headingLabel = new UILabel()
{
Font = UIFont.FromName("Cochin-BoldItalic", 22f),
TextColor = UIColor.FromRGB(127, 51, 0),
BackgroundColor = UIColor.Clear
};
subheadingLabel = new UILabel()
{
Font = UIFont.FromName("AmericanTypewriter", 12f),
TextColor = UIColor.FromRGB(38, 127, 0),
TextAlignment = UITextAlignment.Center,
BackgroundColor = UIColor.Clear
};
ContentView.Add(headingLabel);
ContentView.Add(subheadingLabel);
ContentView.Add(imageView);
}
public void UpdateCell(string caption, string subtitle, UIImage image)
{
headingLabel.Text = caption;
subheadingLabel.Text = subtitle;
imageView.Image = image;
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
headingLabel.Frame = new CoreGraphics.CGRect(5, 4,ContentView.Bounds.Width - 63, 25);
subheadingLabel.Frame = new CoreGraphics.CGRect(100, 18, 100, 20);
imageView.Frame = new CoreGraphics.CGRect(ContentView.Bounds.Width - 63, 5, 33, 33);
}
}

Creating the Custom Renderer on Android:

[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeAndroidListViewRenderer))]
namespace CustomRenderer.Droid
{
public class NativeAndroidListViewRenderer : ListViewRenderer
{
Context _context;
public NativeAndroidListViewRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
Control.ItemClick -= OnItemClick;
}
if (e.NewElement != null)
{
Control.Adapter = new NativeAndroidListViewAdapter(_context as Android.App.Activity, e.NewElement as NativeListView);
Control.ItemClick += OnItemClick;
}
}
void OnItemClick(object sender,
Android.Widget.AdapterView.ItemClickEventArgs e)    {   ((NativeListView)Element).NotifyItemSelected(((NativeListView)Element).Items.ToList()[e.Position - 1]);
}
}
}


Planning to Hire Xamarin App Development Company ?

Your Search ends here.


The GetView method, which is shown in the following code example, returns a view for each row that is populated with data:

public override  GetView(int position, View convertView, ViewGroup parent)
{
var item = tableItems[position];
var view = convertView;
if (view == null)
{
view = context.LayoutInflater.Inflate(Resource.Layout.NativeAndroidListViewCell, null);
}
view.FindViewById(Resource.Id.Text1).Text = item.Name;
view.FindViewById(Resource.Id.Text2).Text = item.Category;
if (view.FindViewById(Resource.Id.Image).Drawable != null)
{
using (var image = view.FindViewById(Resource.Id.Image).Drawable as BitmapDrawable)
{
if (image != null)
{
if (image.Bitmap != null)
{
image.Bitmap.Dispose();
}}}}
if (!String.IsNullOrWhiteSpace(item.ImageFilename))
{
context.Resources.GetBitmapAsync(item.ImageFilename).ContinueWith((t) => {
var bitmap = t.Result;
if (bitmap != null)
{
view.FindViewById(Resource.Id.Image).SetImageBitmap(bitmap);
bitmap.Dispose();
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else
{
view.FindViewById(Resource.Id.Image).SetImageBitmap(null);
}
return view;
}

Xaml file:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="@drawable/CustomSelector">
<LinearLayout
android:id="@+id/Text"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dip">
<TextView
android:id="@+id/Text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF7F3300"
android:textSize="20dip"
android:textStyle="italic" />
<TextView
android:id="@+id/Text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="#FF267F00"
android:paddingLeft="100dip" />
</LinearLayout>
<ImageView
android:id="@+id/Image"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="5dp"
android:src="@drawable/icon"
android:layout_alignParentRight="true" />
</RelativeLayout>


protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);

if (e.PropertyName == NativeListView.ItemsProperty.PropertyName)
{
Control.Adapter = new NativeAndroidListViewAdapter(_context as Android.App.Activity, Element as NativeListView);
}
}

Creating the Custom Renderer on UWP:

[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeUWPListViewRenderer))]
namespace CustomRenderer.UWP
{
public class NativeUWPListViewRenderer : ListViewRenderer
{
ListView listView;
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
listView = Control as ListView;
if (e.OldElement != null)
{
listView.SelectionChanged -= OnSelectedItemChanged;
}
if (e.NewElement != null)
{
listView.SelectionMode = ListViewSelectionMode.Single;
listView.IsItemClickEnabled = false;
listView.ItemsSource = ((NativeListView)e.NewElement).Items;
listView.ItemTemplate = App.Current.Resources["ListViewItemTemplate"] as Windows.UI.Xaml.DataTemplate;
listView.SelectionChanged += OnSelectedItemChanged;
}}
void OnSelectedItemChanged(object sender, SelectionChangedEventArgs e)
{
((NativeListView)Element).NotifyItemSelected(listView.SelectedItem);
}}}

A DataTemplate named ListViewItemTemplate controls the appearance and content of each native ListView cell. This DataTemplate is presented in the following code example, which is saved in the application-level resource dictionary:






















protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == NativeListView.ItemsProperty.PropertyName)
{
listView.ItemsSource = ((NativeListView)Element).Items;
}
}

Conclusion

In this article, we have learned about ListView control and how to configure it on various platforms using an example.

What is ListView and customize a ListView in Xamarin.Forms

What-is-List-View-and-customize-a-ListView-in-Xamarin.Forms

Table of Content

What is ListView?

A ListView control can be used to display scrollable lists of data in any context. Context actions and data binding are supported by the ListView class.

Components

  1. Headers and footers:

The header and footer components appear at the top and bottom of a list, distinct from the data. Headers and footers can be connected to a different data source than the ListView.

  1. Groups:

A ListView's data can be grouped to make it easier to navigate. The majority of the time, groups are data-bound.

  1. Cells:

Cells are the data items in a ListView. A row of data is represented by each column. You can use one of the built-in cells or create your own unique cell. In XAML or code, both built-in and custom cells can be used/defined.

  1. TextCell and ImageCell are built-in cells that match native controls and are particularly performant.
  2. To display complex data, custom cells are used. A custom cell might, for example, be used to display a music list that contains the album and artist.

Customizing ListView:

A Xamarin application. Types of documents ListView is a view that shows a list of data in a vertical format. This article shows how to construct a custom renderer that contains platform-specific list controls and native cell layouts, providing you more control over the performance of native list controls.

Each and every platform, the Forms view has a renderer that builds a native control object. When a Xamarin.Forms application renders a ListView, the ListViewRenderer class is instantiated in iOS, which then instantiates a native UITableView control. The ListViewRenderer class creates a native ListView control for the Android platform. The ListViewRenderer class on the Universal Windows Platform (UWP) creates a native ListView widget. For more information on Xamarin's renderer and native control classes, visit their website. See Renderer Base Classes and Native Controls for a list of controls that form controls map to.

Creating the Custom ListView Control:

public class NativeListView : ListView
{
public static readonly BindableProperty ItemsProperty =
BindableProperty.Create("Items", typeof(IEnumerable), typeof(NativeListView), new List());
public IEnumerable Items
{
get { return (IEnumerable)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public event EventHandler ItemSelected;
public void NotifyItemSelected(object item)
{
if (ItemSelected != null)
{
ItemSelected(this, new SelectedItemChangedEventArgs(item));
}
}
}



Consuming the Custom Control:


<ContentPage
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="{x:Static local:App.Description}" HorizontalTextAlignment="Center" />
<local:NativeListView Grid.Row="1" x:Name="nativeListView" ItemSelected="OnItemSelected" VerticalOptions="FillAndExpand" />
</Grid>
</ContentPage.Content>
</ContentPage>

NativeListView CustomControl Consume by C#:

public class MainPageCS : ContentPage
{
NativeListView nativeListView;
public MainPageCS()
{
nativeListView = new NativeListView
{
Items = DataSource.GetList(),
VerticalOptions = LayoutOptions.FillAndExpand
};
switch (Device.RuntimePlatform)
{
case Device.iOS:
Padding = new Thickness(0, 20, 0, 0);
break;
case Device.Android:
case Device.UWP:
Padding = new Thickness(0);
break;
}
Content = new Grid
{
RowDefinitions = {
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = new GridLength (1, GridUnitType.Star) }
},
Children = {
new Label { Text = App.Description, HorizontalTextAlignment = TextAlignment.Center },
nativeListView
}
};
nativeListView.ItemSelected += OnItemSelected;
}
}

Creating the Custom Renderer on each Platform

  1. Make a custom ListViewRenderer subclass to render the custom control.
  2. When the listview of xamarin.form is generated, override the custom control's OnElementChanged function and write logic to modify it.
  3. To specify that the custom renderer class will be used to render the Xamarin, add an ExportRenderer attribute to it. Creates a custom control. This attribute is used to tell Xamarin about the custom renderer. Forms are a type of document.

Creating the Custom Renderer on iOS

[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeiOSListViewRenderer))]
namespace CustomRenderer.iOS
{
public class NativeiOSListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
}
if (e.NewElement != null)
{
Control.Source = new NativeiOSListViewSource(e.NewElement as NativeListView);
}
}
}
}
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
NativeiOSListViewCell cell = tableView.DequeueReusableCell (cellIdentifier) as NativeiOSListViewCell;
if (cell == null) {
cell = new NativeiOSListViewCell (cellIdentifier);
}

if (String.IsNullOrWhiteSpace (tableItems [indexPath.Row].ImageFilename)) {
cell.UpdateCell (tableItems [indexPath.Row].Name
, tableItems [indexPath.Row].Category
, null);
} else {
cell.UpdateCell (tableItems [indexPath.Row].Name
, tableItems [indexPath.Row].Category
, UIImage.FromFile ("Images/" + tableItems [indexPath.Row].ImageFilename + ".jpg"));
}
return cell;
}

In the following code sample, the NativeiOSListViewCell class defines the layout for each cell:

public class NativeiOSListViewCell : UITableViewCell
{
UILabel headingLabel, subheadingLabel;
UIImageView imageView;
public NativeiOSListViewCell(NSString cellId) : base(UITableViewCellStyle.Default, cellId)
{
SelectionStyle = UITableViewCellSelectionStyle.Gray;
ContentView.BackgroundColor = UIColor.FromRGB(218, 255, 127);
imageView = new UIImageView();
headingLabel = new UILabel()
{
Font = UIFont.FromName("Cochin-BoldItalic", 22f),
TextColor = UIColor.FromRGB(127, 51, 0),
BackgroundColor = UIColor.Clear
};
subheadingLabel = new UILabel()
{
Font = UIFont.FromName("AmericanTypewriter", 12f),
TextColor = UIColor.FromRGB(38, 127, 0),
TextAlignment = UITextAlignment.Center,
BackgroundColor = UIColor.Clear
};
ContentView.Add(headingLabel);
ContentView.Add(subheadingLabel);
ContentView.Add(imageView);
}
public void UpdateCell(string caption, string subtitle, UIImage image)
{
headingLabel.Text = caption;
subheadingLabel.Text = subtitle;
imageView.Image = image;
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
headingLabel.Frame = new CoreGraphics.CGRect(5, 4,ContentView.Bounds.Width - 63, 25);
subheadingLabel.Frame = new CoreGraphics.CGRect(100, 18, 100, 20);
imageView.Frame = new CoreGraphics.CGRect(ContentView.Bounds.Width - 63, 5, 33, 33);
}
}

Creating the Custom Renderer on Android:

[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeAndroidListViewRenderer))]
namespace CustomRenderer.Droid
{
public class NativeAndroidListViewRenderer : ListViewRenderer
{
Context _context;
public NativeAndroidListViewRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
Control.ItemClick -= OnItemClick;
}
if (e.NewElement != null)
{
Control.Adapter = new NativeAndroidListViewAdapter(_context as Android.App.Activity, e.NewElement as NativeListView);
Control.ItemClick += OnItemClick;
}
}
void OnItemClick(object sender,
Android.Widget.AdapterView.ItemClickEventArgs e)    {   ((NativeListView)Element).NotifyItemSelected(((NativeListView)Element).Items.ToList()[e.Position - 1]);
}
}
}


Planning to Hire Xamarin App Development Company ?

Your Search ends here.


The GetView method, which is shown in the following code example, returns a view for each row that is populated with data:

public override  GetView(int position, View convertView, ViewGroup parent)
{
var item = tableItems[position];
var view = convertView;
if (view == null)
{
view = context.LayoutInflater.Inflate(Resource.Layout.NativeAndroidListViewCell, null);
}
view.FindViewById(Resource.Id.Text1).Text = item.Name;
view.FindViewById(Resource.Id.Text2).Text = item.Category;
if (view.FindViewById(Resource.Id.Image).Drawable != null)
{
using (var image = view.FindViewById(Resource.Id.Image).Drawable as BitmapDrawable)
{
if (image != null)
{
if (image.Bitmap != null)
{
image.Bitmap.Dispose();
}}}}
if (!String.IsNullOrWhiteSpace(item.ImageFilename))
{
context.Resources.GetBitmapAsync(item.ImageFilename).ContinueWith((t) => {
var bitmap = t.Result;
if (bitmap != null)
{
view.FindViewById(Resource.Id.Image).SetImageBitmap(bitmap);
bitmap.Dispose();
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else
{
view.FindViewById(Resource.Id.Image).SetImageBitmap(null);
}
return view;
}

Xaml file:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="@drawable/CustomSelector">
<LinearLayout
android:id="@+id/Text"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dip">
<TextView
android:id="@+id/Text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF7F3300"
android:textSize="20dip"
android:textStyle="italic" />
<TextView
android:id="@+id/Text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="#FF267F00"
android:paddingLeft="100dip" />
</LinearLayout>
<ImageView
android:id="@+id/Image"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="5dp"
android:src="@drawable/icon"
android:layout_alignParentRight="true" />
</RelativeLayout>


protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);

if (e.PropertyName == NativeListView.ItemsProperty.PropertyName)
{
Control.Adapter = new NativeAndroidListViewAdapter(_context as Android.App.Activity, Element as NativeListView);
}
}

Creating the Custom Renderer on UWP:

[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeUWPListViewRenderer))]
namespace CustomRenderer.UWP
{
public class NativeUWPListViewRenderer : ListViewRenderer
{
ListView listView;
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
listView = Control as ListView;
if (e.OldElement != null)
{
listView.SelectionChanged -= OnSelectedItemChanged;
}
if (e.NewElement != null)
{
listView.SelectionMode = ListViewSelectionMode.Single;
listView.IsItemClickEnabled = false;
listView.ItemsSource = ((NativeListView)e.NewElement).Items;
listView.ItemTemplate = App.Current.Resources["ListViewItemTemplate"] as Windows.UI.Xaml.DataTemplate;
listView.SelectionChanged += OnSelectedItemChanged;
}}
void OnSelectedItemChanged(object sender, SelectionChangedEventArgs e)
{
((NativeListView)Element).NotifyItemSelected(listView.SelectedItem);
}}}

A DataTemplate named ListViewItemTemplate controls the appearance and content of each native ListView cell. This DataTemplate is presented in the following code example, which is saved in the application-level resource dictionary:






















protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == NativeListView.ItemsProperty.PropertyName)
{
listView.ItemsSource = ((NativeListView)Element).Items;
}
}

Conclusion

In this article, we have learned about ListView control and how to configure it on various platforms using an example.