Creating an Effect
Effects simplify the customization of a control. This article demonstrates how to create an effect that changes the background color of the Entry control when the control gains focus.
The process for creating an effect in each platform-specific project is as follows:
- Create a subclass of the
PlatformEffect
class. - Override the
OnAttached
method and write logic to customize the control. - Override the
OnDetached
method and write logic to clean up the control customization, if required. - Add a
ResolutionGroupName
attribute to the effect class. This attribute sets a company wide namespace for effects, preventing collisions with other effects with the same name. Note that this attribute can only be applied once per project. - Add an
ExportEffect
attribute to the effect class. This attribute registers the effect with a unique ID that's used by Xamarin.Forms, along with the group name, to locate the effect prior to applying it to a control. The attribute takes two parameters – the type name of the effect, and a unique string that will be used to locate the effect prior to applying it to a control.
The effect can then be consumed by attaching it to the appropriate control.
Note
It's optional to provide an effect in each platform project. Attempting to use an effect when one isn't registered will return a non-null value that does nothing.
The sample application demonstrates a FocusEffect
that changes the background color of a control when it gains focus. The following diagram illustrates the responsibilities of each project in the sample application, along with the relationships between them:
An Entry
control on the HomePage
is customized by the FocusEffect
class in each platform-specific project. Each FocusEffect
class derives from the PlatformEffect
class for each platform. This results in the Entry
control being rendered with a platform-specific background color, which changes when the control gains focus, as shown in the following screenshots:
Creating the Effect on Each Platform
The following sections discuss the platform-specific implementation of the FocusEffect
class.
iOS Project
The following code example shows the FocusEffect
implementation for the iOS project:
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(EffectsDemo.iOS.FocusEffect), nameof(EffectsDemo.iOS.FocusEffect))]
namespace EffectsDemo.iOS
{
public class FocusEffect : PlatformEffect
{
UIColor backgroundColor;
protected override void OnAttached ()
{
try {
Control.BackgroundColor = backgroundColor = UIColor.FromRGB (204, 153, 255);
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}
protected override void OnDetached ()
{
}
protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)
{
base.OnElementPropertyChanged (args);
try {
if (args.PropertyName == "IsFocused") {
if (Control.BackgroundColor == backgroundColor) {
Control.BackgroundColor = UIColor.White;
} else {
Control.BackgroundColor = backgroundColor;
}
}
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}
}
}
The OnAttached
method sets the BackgroundColor
property of the control to light purple with the UIColor.FromRGB
method, and also stores this color in a field. This functionality is wrapped in a try
/catch
block in case the control the effect is attached to does not have a BackgroundColor
property. No implementation is provided by the OnDetached
method because no cleanup is necessary.
The OnElementPropertyChanged
override responds to bindable property changes on the Xamarin.Forms control. When the IsFocused
property changes, the BackgroundColor
property of the control is changed to white if the control has focus, otherwise it's changed to light purple. This functionality is wrapped in a try
/catch
block in case the control the effect is attached to does not have a BackgroundColor
property.
Android Project
The following code example shows the FocusEffect
implementation for the Android project:
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(EffectsDemo.Droid.FocusEffect), nameof(EffectsDemo.Droid.FocusEffect))]
namespace EffectsDemo.Droid
{
public class FocusEffect : PlatformEffect
{
Android.Graphics.Color originalBackgroundColor = new Android.Graphics.Color(0, 0, 0, 0);
Android.Graphics.Color backgroundColor;
protected override void OnAttached()
{
try
{
backgroundColor = Android.Graphics.Color.LightGreen;
Control.SetBackgroundColor(backgroundColor);
}
catch (Exception ex)
{
Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
}
}
protected override void OnDetached()
{
}
protected override void OnElementPropertyChanged(System.ComponentModel.PropertyChangedEventArgs args)
{
base.OnElementPropertyChanged(args);
try
{
if (args.PropertyName == "IsFocused")
{
if (((Android.Graphics.Drawables.ColorDrawable)Control.Background).Color == backgroundColor)
{
Control.SetBackgroundColor(originalBackgroundColor);
}
else
{
Control.SetBackgroundColor(backgroundColor);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
}
}
}
}
The OnAttached
method calls the SetBackgroundColor
method to set the background color of the control to light green, and also stores this color in a field. This functionality is wrapped in a try
/catch
block in case the control the effect is attached to does not have a SetBackgroundColor
property. No implementation is provided by the OnDetached
method because no cleanup is necessary.
The OnElementPropertyChanged
override responds to bindable property changes on the Xamarin.Forms control. When the IsFocused
property changes, the background color of the control is changed to white if the control has focus, otherwise it's changed to light green. This functionality is wrapped in a try
/catch
block in case the control the effect is attached to does not have a BackgroundColor
property.
Universal Windows Platform Projects
The following code example shows the FocusEffect
implementation for Universal Windows Platform (UWP) projects:
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;
[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(EffectsDemo.UWP.FocusEffect), nameof(EffectsDemo.UWP.FocusEffect))]
namespace EffectsDemo.UWP
{
public class FocusEffect : PlatformEffect
{
protected override void OnAttached()
{
try
{
(Control as Windows.UI.Xaml.Controls.Control).Background = new SolidColorBrush(Colors.Cyan);
(Control as FormsTextBox).BackgroundFocusBrush = new SolidColorBrush(Colors.White);
}
catch (Exception ex)
{
Debug.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
}
}
protected override void OnDetached()
{
}
}
}
The OnAttached
method sets the Background
property of the control to cyan, and sets the BackgroundFocusBrush
property to white. This functionality is wrapped in a try
/catch
block in case the control the effect is attached to lacks these properties. No implementation is provided by the OnDetached
method because no cleanup is necessary.
Consuming the Effect
The process for consuming an effect from a Xamarin.Forms .NET Standard library or Shared Library project is as follows:
- Declare a control that will be customized by the effect.
- Attach the effect to the control by adding it to the control's
Effects
collection.
Note
An effect instance can only be attached to a single control. Therefore, an effect must be resolved twice to use it on two controls.
Consuming the Effect in XAML
The following XAML code example shows an Entry
control to which the FocusEffect
is attached:
<Entry Text="Effect attached to an Entry" ...>
<Entry.Effects>
<local:FocusEffect />
</Entry.Effects>
...
</Entry>
The FocusEffect
class in the .NET Standard library supports effect consumption in XAML, and is shown in the following code example:
public class FocusEffect : RoutingEffect
{
public FocusEffect () : base ($"MyCompany.{nameof(FocusEffect)}")
{
}
}
The FocusEffect
class subclasses the RoutingEffect
class, which represents a platform-independent effect that wraps an inner effect that is usually platform-specific. The FocusEffect
class calls the base class constructor, passing in a parameter consisting of a concatenation of the resolution group name (specified using the ResolutionGroupName
attribute on the effect class), and the unique ID that was specified using the ExportEffect
attribute on the effect class. Therefore, when the Entry
is initialized at runtime, a new instance of the MyCompany.FocusEffect
is added to the control's Effects
collection.
Effects can also be attached to controls by using a behavior, or by using attached properties. For more information about attaching an effect to a control by using a behavior, see Reusable EffectBehavior. For more information about attaching an effect to a control by using attached properties, see Passing Parameters to an Effect.
Consuming the Effect in C#
The equivalent Entry
in C# is shown in the following code example:
var entry = new Entry {
Text = "Effect attached to an Entry",
...
};
The FocusEffect
is attached to the Entry
instance by adding the effect to the control's Effects
collection, as demonstrated in the following code example:
public HomePageCS ()
{
...
entry.Effects.Add (Effect.Resolve ($"MyCompany.{nameof(FocusEffect)}"));
...
}
The Effect.Resolve
returns an Effect
for the specified name, which is a concatenation of the resolution group name (specified using the ResolutionGroupName
attribute on the effect class), and the unique ID that was specified using the ExportEffect
attribute on the effect class. If a platform doesn't provide the effect, the Effect.Resolve
method will return a non-null
value.
Summary
This article demonstrated how to create an effect that changes the background color of the Entry
control when the control gains focus.