How To Set Corner Radius For View, Layout, Cell (Grid, Stack, Listview) In Xamarin.forms

Introduction

This article describes, how we can set Corner Radius for Control or View or Layout. Sometimes, we may get the requirement to set corner radius for StackLayout or Grid or ListView, in such cases I used to try to put View or Layout inside the Frame to make the corner radius, but, it was difficult to set all corner properties. So in this article, we can learn to achieve this functionality using CustomRenderer.

Requirements

  • This article's source code is prepared by using Visual Studio. And it is better to install the latest Visual Studio updates from here.
  • This article is prepared on a MAC machine.
  • This sample project is Xamarin.Forms PCL project.
  • This sample app is targeted for Android, iOS. And tested for Android & iOS.

Description

The creation of  Xamarin.Forms project is very simple in Visual Studio for Mac. It will create three projects:

  1. Shared Code
  2. Xamarin.Android
  3. Xamarin.iOS

Because it's a Mac system with  Visual Studio for Mac it doesn't support Windows projects (UWP, Windows, Windows Phone).

The following steps will show you how to create Xamarin.Forms project in Mac systems with Visual Studio.

First, open Visual Studio for Mac. And Click on New Project

Xamarin

After that, we need to select whether you're doing Xamarin.Forms or Xamarin.Android or Xamarin.iOS project. If we want to create Xamarin.Forms project just follow the below screenshot. 

Xamarin

Then, we have to give the App Name i.e RoundedCornerViewDemo.

Xamarin

 Note: In the above screen under Shared Code, select Portable class Library or Use Shared Library.

Then, click on Next Button and the following screenshot will be displayed. In that screen, we have to browse the file path where we want to save that application on our PC.

Xamarin

After clicking on the create button it will create the RoundedCornerViewDemo Xamarin.Forms project like this.

Xamarin

And the project structure will be:

  • RoundedCornerViewDemo: It is for Shared Code
  • RoundedCornerViewDemo.Droid: It is for Android.
  • RoundedCornerViewDemo.iOS: It is for iOS
Xamarin 

We need to follow the below few steps to make corner radius for the view.

Portable Class Library (PCL)

Step 1:

In PCL, create a class name as RoundedCornerView which should inherit any layout and this article inherits Grid Layout and adds BindableProperties like BorderColor, RoundedCornerRadius, BorderWidth, MakeCircle, FillColor.

RoundedCornerView.cs

  1. using System;  
  2. using Xamarin.Forms;  
  3. namespace RoundedCornerViewDemo.ControlsToolkit.Custom {  
  4.     public class RoundedCornerView: Grid {  
  5.         public static readonly BindableProperty FillColorProperty = BindableProperty.Create<RoundedCornerView, Color> (w => w.FillColor, Color.White);  
  6.         public Color FillColor {  
  7.             get {  
  8.                 return (Color) GetValue(FillColorProperty);  
  9.             }  
  10.             set {  
  11.                 SetValue(FillColorProperty, value);  
  12.             }  
  13.         }  
  14.         public static readonly BindableProperty RoundedCornerRadiusProperty = BindableProperty.Create<RoundedCornerView, double> (w => w.RoundedCornerRadius, 3);  
  15.         public double RoundedCornerRadius {  
  16.             get {  
  17.                 return (double) GetValue(RoundedCornerRadiusProperty);  
  18.             }  
  19.             set {  
  20.                 SetValue(RoundedCornerRadiusProperty, value);  
  21.             }  
  22.         }  
  23.         public static readonly BindableProperty MakeCircleProperty = BindableProperty.Create<RoundedCornerView, Boolean> (w => w.MakeCircle, false);  
  24.         public Boolean MakeCircle {  
  25.             get {  
  26.                 return (Boolean) GetValue(MakeCircleProperty);  
  27.             }  
  28.             set {  
  29.                 SetValue(MakeCircleProperty, value);  
  30.             }  
  31.         }  
  32.         public static readonly BindableProperty BorderColorProperty = BindableProperty.Create<RoundedCornerView, Color> (w => w.BorderColor, Color.Transparent);  
  33.         public Color BorderColor {  
  34.             get {  
  35.                 return (Color) GetValue(BorderColorProperty);  
  36.             }  
  37.             set {  
  38.                 SetValue(BorderColorProperty, value);  
  39.             }  
  40.         }  
  41.         public static readonly BindableProperty BorderWidthProperty = BindableProperty.Create<RoundedCornerView, int> (w => w.BorderWidth, 1);  
  42.         public int BorderWidth {  
  43.             get {  
  44.                 return (int) GetValue(BorderWidthProperty);  
  45.             }  
  46.             set {  
  47.                 SetValue(BorderWidthProperty, value);  
  48.             }  
  49.         }  
  50.     }  
  51. }  

Step 2:

Create your own Xaml page named RoundedCornerViewPage.xaml, and make sure to refer to "RoundedCornerView" class in Xaml by declaring a namespace for its location and using the namespace prefix on the control element. The following code example shows how the "RoundedCornerView" renderer class can be consumed by a Xaml page:

And here, we are trying to set rounded corner radius for ListView, so place Listview inside our custom renderer control, let's see how it makes corner radius.

RoundedCornerViewPage.xaml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
  3. xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
  4. xmlns:custom="clr-namespace:RoundedCornerViewDemo.ControlsToolkit.Custom;assembly=RoundedCornerViewDemo" 
  5. x:Class="RoundedCornerViewDemo.RoundedCornerViewPage">  
  6.     <StackLayout Spacing="20" Padding="20,40,20,20">  
  7.         <Label Text="RoundedCornerView" HorizontalOptions="CenterAndExpand" FontSize="30" TextColor="Blue" />  
  8.         <custom:RoundedCornerView BorderColor="Gray" BorderWidth="2" BackgroundColor="Transparent" VerticalOptions="FillAndExpand" RoundedCornerRadius="8">  
  9.             <ListView x:Name="EmployeeView">  
  10.                 <ListView.ItemTemplate>  
  11.                     <DataTemplate>  
  12.                         <TextCell Text="{Binding DisplayName}" /> </DataTemplate>  
  13.                 </ListView.ItemTemplate>  
  14.             </ListView>  
  15.         </custom:RoundedCornerView>  
  16.     </StackLayout>  
  17. </ContentPage>  

Note:

The "custom" namespace prefix can be named anything. However, the clr-namespace and assembly values must match the details of the custom renderer class. Once the namespace is declared the prefix is used to reference the custom control/layout.

Step 3:

Add some simple data to bind ObservableCollection to the ListView in code behind. Also, here I'm not following MVVM design pattern.

RoundedCornerViewPage.xaml.cs

  1. using System.Collections.ObjectModel;  
  2. using Xamarin.Forms;  
  3. namespace RoundedCornerViewDemo {  
  4.     public partial class RoundedCornerViewPage: ContentPage {  
  5.         ObservableCollection<Employee> employees = new ObservableCollection<Employee>();  
  6.         public RoundedCornerViewPage() {  
  7.             InitializeComponent();  
  8.             employees.Add(new Employee {  
  9.                 DisplayName = "Rob Finnerty"  
  10.             });  
  11.             employees.Add(new Employee {  
  12.                 DisplayName = "Bill Wrestler"  
  13.             });  
  14.             employees.Add(new Employee {  
  15.                 DisplayName = "Dr. Geri-Beth Hooper"  
  16.             });  
  17.             employees.Add(new Employee {  
  18.                 DisplayName = "Dr. Keith Joyce-Purdy"  
  19.             });  
  20.             employees.Add(new Employee {  
  21.                 DisplayName = "Sheri Spruce"  
  22.             });  
  23.             employees.Add(new Employee {  
  24.                 DisplayName = "Burt Indybrick"  
  25.             });  
  26.             EmployeeView.ItemsSource = employees;  
  27.         }  
  28.     }  
  29.     public class Employee {  
  30.         public string DisplayName {  
  31.             get;  
  32.             set;  
  33.         }  
  34.     }  
  35. }  

Examples:

In the above code, we made corner radius for ListView. But we can also set RoundedCornerRadius for any Control/View and Layout.

StackLayout

  1. <custom:RoundedCornerView BorderColor="Gray" BorderWidth="2" BackgroundColor="Transparent" VerticalOptions="FillAndExpand" RoundedCornerRadius="8">  
  2.     <StackLayout orientation="Horizontal">  
  3.         <Label Text="Hi Welcome" /> 
  4.     </StackLayout>  
  5. </custom:RoundedCornerView>  

Grid

  1. <custom:RoundedCornerView BorderColor="Gray" BorderWidth="2" BackgroundColor="Transparent" VerticalOptions="FillAndExpand" RoundedCornerRadius="8">  
  2.     <Grid>  
  3.         <Grid.RowDefinitions>  
  4.             <RowDefinition Height="Auto" /> 
  5.          </Grid.RowDefinitions>  
  6.         <Grid.ColumnDefinitions>  
  7.             <ColumnDefinition Width="Auto" /> 
  8.         </Grid.ColumnDefinitions>  
  9.         <Label Text="Hi Welcome" /> 
  10.      </Grid>  
  11. </custom:RoundedCornerView>  

ViewCell

  1. <ListView x:Name="EmployeeView">  
  2.     <ListView.ItemTemplate>  
  3.         <DataTemplate>  
  4.             <ViewCell>  
  5.                 <custom:RoundedCornerView BorderColor="Gray" BorderWidth="2" BackgroundColor="Transparent" VerticalOptions="FillAndExpand" RoundedCornerRadius="8">  
  6.                     <TextCell Text="{Binding DisplayName}" /> 
  7.                 </custom:RoundedCornerView>  
  8.             </ViewCell>  
  9.         </DataTemplate>  
  10.     </ListView.ItemTemplate>  
  11. </ListView>  

Xamarin.Android

In Android project, create a class name as RoundedCornerViewRenderer and make sure to add renderer registration for our RoundedCornerView class above the namespace,

RoundedCornerViewRenderer.cs

  1. using System;  
  2. using Android.Graphics;  
  3. using RoundedCornerViewDemo;  
  4. using RoundedCornerViewDemo.ControlsToolkit.Custom;  
  5. using RoundedCornerViewDemo.Droid.Renderers;  
  6. using Xamarin.Forms;  
  7. using Xamarin.Forms.Platform.Android;  
  8. [assembly: ExportRenderer(typeof(RoundedCornerView), typeof(RoundedCornerViewRenderer))]  
  9. namespace RoundedCornerViewDemo.Droid.Renderers {  
  10.     public class RoundedCornerViewRenderer: ViewRenderer {  
  11.         protected override void OnElementChanged(ElementChangedEventArgs<View> e) {  
  12.             base.OnElementChanged(e);  
  13.         }  
  14.         protected override bool DrawChild(Canvas canvas, global::Android.Views.View child, long drawingTime) {  
  15.             if (Element == nullreturn false;  
  16.             RoundedCornerView rcv = (RoundedCornerView) Element;  
  17.             this.SetClipChildren(true);  
  18.             rcv.Padding = new Thickness(0, 0, 0, 0);  
  19.             //rcv.HasShadow = false;      
  20.             int radius = (int)(rcv.RoundedCornerRadius);  
  21.             // Check if make circle is set to true. If so, then we just use the width and      
  22.             // height of the control to calculate the radius. RoundedCornerRadius will be ignored      
  23.             // in this case.      
  24.             if (rcv.MakeCircle) {  
  25.                 radius = Math.Min(Width, Height) / 2;  
  26.             }  
  27.             // When we create a round rect, we will have to double the radius since it is not      
  28.             // the same as creating a circle.      
  29.             radius *= 2;  
  30.             try {  
  31.                 //Create path to clip the child       
  32.                 var path = new Path();  
  33.                 path.AddRoundRect(new RectF(0, 0, Width, Height), new float[] {  
  34.                     radius,  
  35.                     radius,  
  36.                     radius,  
  37.                     radius,  
  38.                     radius,  
  39.                     radius,  
  40.                     radius,  
  41.                     radius  
  42.                 }, Path.Direction.Ccw);  
  43.                 canvas.Save();  
  44.                 canvas.ClipPath(path);  
  45.                 // Draw the child first so that the border shows up above it.      
  46.                 var result = base.DrawChild(canvas, child, drawingTime);  
  47.                 canvas.Restore();  
  48.                 /*   
  49.                  * If a border is specified, we use the same path created above to stroke   
  50.                  * with the border color.   
  51.                  * */  
  52.                 if (rcv.BorderWidth > 0) {  
  53.                     // Draw a filled circle.      
  54.                     var paint = new Paint();  
  55.                     paint.AntiAlias = true;  
  56.                     paint.StrokeWidth = rcv.BorderWidth;  
  57.                     paint.SetStyle(Paint.Style.Stroke);  
  58.                     paint.Color = rcv.BorderColor.ToAndroid();  
  59.                     canvas.DrawPath(path, paint);  
  60.                     paint.Dispose();  
  61.                 }  
  62.                 //Properly dispose      
  63.                 path.Dispose();  
  64.                 return result;  
  65.             } catch (Exception ex) {  
  66.                 System.Console.Write(ex.Message);  
  67.             }  
  68.             return base.DrawChild(canvas, child, drawingTime);  
  69.         }  
  70.     }  
  71. }  

Here, OnElementChanged method instantiates an Android UI Layout. And also make sure to override DrawChild which is responsible for getting the canvas in the right state that includes BorderColor, BorderWidth, BorderRadius etc.

Xamarin.iOS

In iOS project, create a class name is RoundedCornerViewRenderer and make sure to add renderer registration for our RoundedCornerView class in above of the namespace:

RoundedCornerViewRenderer.cs

  1. using System;  
  2. using System.Diagnostics;  
  3. using RoundedCornerViewDemo;  
  4. using RoundedCornerViewDemo.ControlsToolkit.Custom;  
  5. using RoundedCornerViewDemo.iOS;  
  6. using Xamarin.Forms;  
  7. using Xamarin.Forms.Platform.iOS;  
  8. [assembly: ExportRenderer(typeof(RoundedCornerView), typeof(RoundedCornerViewRenderer))]  
  9. namespace RoundedCornerViewDemo.iOS {  
  10.     public class RoundedCornerViewRenderer: ViewRenderer {  
  11.         protected override void OnElementChanged(ElementChangedEventArgs<View> e) {  
  12.             base.OnElementChanged(e);  
  13.             if (this.Element == nullreturn;  
  14.             this.Element.PropertyChanged += (sender, e1) => {  
  15.                 try {  
  16.                     if (NativeView != null) {  
  17.                         NativeView.SetNeedsDisplay();  
  18.                         NativeView.SetNeedsLayout();  
  19.                     }  
  20.                 } catch (Exception exp) {  
  21.                     Debug.WriteLine("Handled Exception in RoundedCornerViewDemoRenderer. Just warngin : " + exp.Message);  
  22.                 }  
  23.             };  
  24.         }  
  25.         public override void Draw(CoreGraphics.CGRect rect) {  
  26.             base.Draw(rect);  
  27.             this.LayoutIfNeeded();  
  28.             RoundedCornerView rcv = (RoundedCornerView) Element;  
  29.             //rcv.HasShadow = false;      
  30.             rcv.Padding = new Thickness(0, 0, 0, 0);  
  31.             //this.BackgroundColor = rcv.FillColor.ToUIColor();      
  32.             this.ClipsToBounds = true;  
  33.             this.Layer.BackgroundColor = rcv.FillColor.ToCGColor();  
  34.             this.Layer.MasksToBounds = true;  
  35.             this.Layer.CornerRadius = (nfloat) rcv.RoundedCornerRadius;  
  36.             if (rcv.MakeCircle) {  
  37.                 this.Layer.CornerRadius = (int)(Math.Min(Element.Width, Element.Height) / 2);  
  38.             }  
  39.             this.Layer.BorderWidth = 0;  
  40.             if (rcv.BorderWidth > 0 && rcv.BorderColor.A > 0.0) {  
  41.                 this.Layer.BorderWidth = rcv.BorderWidth;  
  42.                 this.Layer.BorderColor = newUIColor(  
  43.                     (nfloat)rcv.BorderColor.R,
  44.                     (nfloat) rcv.BorderColor.G,
  45.                     (nfloat) rcv.BorderColor.B,
  46.                     (nfloat) rcv.BorderColor.A).CGColor;  
  47.                  }  
  48.         }  
  49.     }  
  50. }  

Here, OnElementChanged method instantiates an iOS UI, with a reference to the layout being assigned to the renderer's Element property. In DrawChild method reference to the Layout is assigned to the renderer's Element property.

Output 

 
Please download the source code from here. 


Similar Articles