Platform effects are used to simplify native control customization, reducing the need to create CustomRenderers
for small styling changes. This means we don't have to create a custom renderer every single time we want native customization. To implement a PlatformEffect
, we first create a class that subclasses the PlatformEffect
framework. Then we have to write platform-specific implementations for each.
Following is a small overview of how the rendering process will look among the different projects:
Let's add a new folder called Effects
inside the Camera
project, add in a new file called LabelShadowEffect.cs
, and implement the following:
public class LabelShadowEffect : RoutingEffect { #region Public Properties public float Radius { get; set; } public Color Color { get; set; } public float DistanceX { get; set; } public float DistanceY { get; set; } #endregion #region Constructors public LabelShadowEffect() : base("Camera.LabelShadowEffect") { } #endregion }
Our LabelShadowEffect
must inherit the PlatformEffect
framework. The Radius
property is responsible for the corner radius of the shadow. Then we have the Color
property that will set the color of the shadow. Finally, we have the DistanceX
and DistanceY
properties for assigning the position of the shadow.
Now we must create the platform implementations. Let's start with iOS, add in a new folder called Effects
, add in a new file called LabelShadowEffectiOS.cs
, and implement the following:
public class LabelShadowEffectiOS : PlatformEffect { #region Protected Methods protected override void OnAttached() { try { var effect = (LabelShadowEffect)Element.Effects.FirstOrDefault(e => e is LabelShadowEffect); if (effect != null) { Control.Layer.CornerRadius = effect.Radius; Control.Layer.ShadowColor = effect.Color.ToCGColor(); Control.Layer.ShadowOffset = new CGSize(effect.DistanceX, effect.DistanceY); Control.Layer.ShadowOpacity = 1.0f; } } catch (Exception ex) { Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message); } } protected override void OnDetached() { } #endregion }
All PlatformEffects
must override the OnAttached
and OnDetached
methods. The OnAttached
method is where we set up all native shadow effects. We start with retrieving the first PlatformEffect
from the Effects
list of the Element
object. Like our CustomRenderers
, we have access to the original Xamarin.Forms
element that we are customizing. In the OnDetached
method, we would normally dispose any objects that are no longer required.
We must also add assembly lines above the namespace block like the following:
[assembly: Xamarin.Forms.ResolutionGroupName("Camera")] [assembly: Xamarin.Forms.ExportEffect(typeof(Camera.Droid.Effects.LabelShadowEffectiOS), "LabelShadowEffect")]
We must add a ResolutionGroupName
to specify the namespace for the effects; this prevents collisions with other effects of the same name. We also add the ExportEffect
attribute to register the effect with a unique ID that is used by Xamarin.Forms
, along with the group name, to locate the effect prior to applying it to a control.
Now let's add the equivalent for Android. Add a new folder in the Camera.Droid
project, add a new file called LabelShadowEffectDroid.cs
, and implement the following:
public class LabelShadowEffectDroid : PlatformEffect { #region Protected Methods protected override void OnAttached() { try { var control = Control as Android.Widget.TextView; var effect = (LabelShadowEffect)Element.Effects.FirstOrDefault(e => e is LabelShadowEffect); if (effect != null) { control.SetShadowLayer(effect.Radius, effect.DistanceX, effect.DistanceY, effect.Color.ToAndroid()); } } catch (Exception ex) { Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message); } } protected override void OnDetached() { } #endregion }
In our Android implementation, we start with retrieving the control as a native TextView
. We then retrieve the first LabelShadowEffect
object from the list of effects from the Element
. We then use the method SetShadowLayer
to create native shadowing on the TextView
.
Great! Now we have our native implementations, let's add the DescriptionLabel
object to the MainPage
:
<Label x:Name="DesciptionLabel" Text="{Binding DescriptionMessage}" TextColor="Black" HorizontalOptions="Center" Font="Arial, 20" Grid.Row="1" Grid.Column="0"> <Label.Effects> <e:LabelShadowEffect Radius="5" DistanceX="5" DistanceY="5"> <e:LabelShadowEffect.Color> <OnPlatform x:TypeArguments="Color" iOS="Black" Android="Blue" WinPhone="Red" /> </e:LabelShadowEffect.Color> </e:LabelShadowEffect> </Label.Effects> </Label>
Here we are able to attach the effect inside our XAML. We must also add the namespace to the Effects
folder:
xmlns:e="clr-namespace:Camera.Effects;assembly=Camera"
This is how the MainPage
will look once complete:
3.145.107.100