Creación de un efecto

Download SampleDescargar el ejemplo

Los efectos simplifican la personalización de un control. En este artículo se muestra cómo crear un efecto que cambia el color de fondo del control Entry cuando recibe el foco.

El proceso para crear un efecto de cada proyecto específico de la plataforma es el siguiente:

  1. Se crea una subclase de la clase PlatformEffect.
  2. Se invalida el método OnAttached y se escribe lógica para personalizar el control.
  3. Se invalida el método OnDetached y se escribe lógica para limpiar la personalización del control, si es necesario.
  4. Se agrega un atributo ResolutionGroupName a la clase de efecto. Este atributo establece un espacio de nombres para los efectos para toda la empresa, lo que evita conflictos con otros efectos con el mismo nombre. Tenga en cuenta que este atributo solo se puede aplicar una vez por proyecto.
  5. Agregue un atributo ExportEffect a la clase de efecto. Este atributo registra el efecto con un identificador único que Xamarin.Forms usa junto con el nombre del grupo para buscar el efecto antes de aplicarlo a un control. El atributo toma dos parámetros: el nombre de tipo del efecto y una cadena única que se usará para buscar el efecto antes de aplicarlo a un control.

Después, el efecto se puede consumir si se adjunta al control adecuado.

Nota:

Proporcionar un efecto en cada proyecto de la plataforma es un paso opcional. Al intentar usar un efecto cuando no se ha registrado uno, se devolverá un valor distinto de NULL que no hace nada.

En la aplicación de ejemplo se muestra un elemento FocusEffect que cambia el color de fondo de un control cuando recibe el foco. El siguiente diagrama muestra las responsabilidades de cada proyecto de la aplicación de ejemplo, junto con las relaciones entre ellos:

Focus Effect Project Responsibilities

La clase FocusEffect personaliza un control Entry en el elemento HomePage en cada proyecto específico de la plataforma. Cada clase FocusEffect se deriva de la clase PlatformEffect para cada plataforma. Como resultado, se representa el control Entry con un color de fondo específico de la plataforma, que cambia cuando el control recibe el foco, como se muestra en las capturas de pantalla siguientes:

Focus Effect on each Platform, control focusedFocus Effect on each Platform, control unfocused

Creación del efecto en cada plataforma

En las secciones siguientes se describe la implementación específica de la plataforma de la clase FocusEffect.

Proyecto de iOS

En el ejemplo de código siguiente se muestra la implementación FocusEffect para el proyecto de iOS:

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);
            }
        }
    }
}

El método OnAttached establece la propiedad BackgroundColor del control en color morado claro con el método UIColor.FromRGB y también almacena este color en un campo. Esta funcionalidad se encapsula en un bloque try/catch en caso de que el control al que está asociado el efecto no tenga una propiedad BackgroundColor. El método OnDetached no proporciona ninguna implementación porque no se necesita limpieza.

La invalidación de OnElementPropertyChanged responde a los cambios de propiedad enlazable en el control de Xamarin.Forms. Cuando cambia la propiedad IsFocused, la propiedad BackgroundColor del control se cambia a color blanco si el control tiene el foco; en caso contrario, se cambia a color morado claro. Esta funcionalidad se encapsula en un bloque try/catch en caso de que el control al que está asociado el efecto no tenga una propiedad BackgroundColor.

Proyecto de Android

En el ejemplo de código siguiente se muestra la implementación FocusEffect para el proyecto de Android:

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);
            }
        }
    }
}

El método OnAttached llama al método SetBackgroundColor para establecer el color de fondo del control en verde claro y también almacena este color en un campo. Esta funcionalidad se encapsula en un bloque try/catch en caso de que el control al que está asociado el efecto no tenga una propiedad SetBackgroundColor. El método OnDetached no proporciona ninguna implementación porque no se necesita limpieza.

La invalidación de OnElementPropertyChanged responde a los cambios de propiedad enlazable en el control de Xamarin.Forms. Cuando cambia la propiedad IsFocused, el color de fondo del control se cambia a color blanco si el control tiene el foco; en caso contrario, se cambia a color verde claro. Esta funcionalidad se encapsula en un bloque try/catch en caso de que el control al que está asociado el efecto no tenga una propiedad BackgroundColor.

Proyectos de la Plataforma universal de Windows

En el ejemplo de código siguiente se muestra la implementación FocusEffect para los proyectos de la Plataforma universal de Windows (UWP):

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()
        {
        }
    }
}

El método OnAttached establece la propiedad Background del control en cian y la propiedad BackgroundFocusBrush en blanco. Esta funcionalidad se encapsula en un bloque try/catch en caso de que el control al que está asociado el efecto carezca de estas propiedades. El método OnDetached no proporciona ninguna implementación porque no se necesita limpieza.

Consumo del efecto

El proceso para consumir un efecto desde un proyecto de biblioteca de .NET Standard o de biblioteca compartida de Xamarin.Forms es el siguiente:

  1. Se declara un control que el efecto va a personalizar.
  2. Se adjunta el efecto al control agregándolo a la colección Effects del control.

Nota:

Una instancia de efecto solo se puede adjuntar a un único control. Por tanto, un efecto se debe resolver dos veces para usarlo en dos controles.

Consumo del efecto en XAML

En el ejemplo de código XAML siguiente se muestra un control Entry al que se adjunta el elemento FocusEffect:

<Entry Text="Effect attached to an Entry" ...>
    <Entry.Effects>
        <local:FocusEffect />
    </Entry.Effects>
    ...
</Entry>

La clase FocusEffect de la biblioteca de .NET Standard admite el consumo de efectos en XAML, y se muestra en el ejemplo de código siguiente:

public class FocusEffect : RoutingEffect
{
    public FocusEffect () : base ($"MyCompany.{nameof(FocusEffect)}")
    {
    }
}

La clase FocusEffect crea subclases de la clase RoutingEffect, que representa un efecto independiente de la plataforma que encapsula un efecto interno que suele ser específico de la plataforma. La clase FocusEffect llama al constructor de clase base, y se pasa un parámetro que consiste en la concatenación del nombre del grupo de resolución (que se especifica con el atributo ResolutionGroupName en la clase de efecto), y el identificador único que se ha especificado con el atributo ExportEffect en la clase de efecto. Por tanto, cuando se inicializa Entry en tiempo de ejecución, se agrega una nueva instancia de MyCompany.FocusEffect a la colección Effects del control.

Los efectos también se pueden adjuntar a los controles mediante un comportamiento, o bien mediante propiedades adjuntas. Para obtener más información sobre cómo adjuntar un efecto a un control mediante un comportamiento, vea EffectBehavior reutilizable. Para obtener más información sobre cómo adjuntar un efecto a un control mediante propiedades adjuntas, vea Pasar parámetros a un efecto.

Consumo del efecto en C#

El control Entry equivalente en C# se muestra en el ejemplo de código siguiente:

var entry = new Entry {
  Text = "Effect attached to an Entry",
  ...
};

FocusEffect se adjunta a la instancia de Entry mediante la adición del efecto a la colección Effects del control, como se muestra en el ejemplo de código siguiente:

public HomePageCS ()
{
  ...
  entry.Effects.Add (Effect.Resolve ($"MyCompany.{nameof(FocusEffect)}"));
  ...
}

Effect.Resolve devuelve un elemento Effect para el nombre especificado, que es una concatenación del nombre del grupo de resolución (que se especifica con el atributo ResolutionGroupName en la clase de efecto), y el identificador único que se ha especificado con el atributo ExportEffect en la clase de efecto. Si una plataforma no proporciona el efecto, el método Effect.Resolve devolverá un valor que no es null.

Resumen

En este artículo se ha mostrado cómo crear un efecto que cambia el color de fondo del control Entry cuando el control recibe el foco.