Controls and layouts with rounded corners with Xamarin.Forms.Effect

It is not a rare case when a UI designer creates an app design full of elements with nicely rounded corners.

It can be implemented in two ways:

  • wrap each and every control into a container which will crop its content corners, a Frame with nonzero CornerRadius as an example;
  • create and apply a custom Xamarin.Forms.RoutingEffect allowing controls and layouts to crop their corners themselves.

This post provides a full implementation of the second option.

Let’s start by defining what we’d like to achieve. Here is a schematic example of a xaml applying a rounded corners effect to various layouts and controls, passing corners radius as a hardcoded number or as a resource.

Here is how it will look like on iOS and Android.

Creating an effect will require:

  1. In the shared project, declare a child of RoutingEffect, RoundCornersEffect in our case;
  2. Inside RoundCornersEffect, declare an AttachedProperty named CornerRadius;
  3. In OnCornerRadiusChanged handler, add the RoundCornersEffect to a view when the property value is more than zero and remove otherwise;
  4. In platform projects, declare a child of PlatformEffect, add an ExportEffect attribute;
  5. Override OnAttached() and OnDetached() callbacks, applying corners radius value, and OnElementPropertyChanged().

Let’s get to it!

Shared routing effect:

iOS platform effect:

Android platform effect:

GetDensity() implementation assumes Plugin.CurrentActivity is referenced in the Android project, but you can choose to implement it otherwise.

It came to my attention that accessing Container in OnDetached method may throw an ObjectDisposedException, which may cause the whole page UI to disappear. Hence try/catch clause in each method.

Voila!


References:

  • Xamarin documentation on Effects, especially a section explaining how to use Attached Properties to pass parameters to an effect;
  • Android documentation on ViewOutlineProvider;
  • iOS documentation on CALayer.