Características específicas de las plataformas

Las características específicas de plataforma permiten usar funciones que solo están disponibles en una plataforma concreta sin necesidad de crear representadores ni efectos personalizados.

El proceso para consumir una característica específica de plataforma a través de XAML o a través de la API de código fluida es el siguiente:

  1. Agregue una declaración xmlns o una directiva using para el espacio de nombres Xamarin.Forms.PlatformConfiguration.
  2. Agregue una declaración xmlns o una directiva using para el espacio de nombres que contiene la funcionalidad específica de plataforma:
    1. En iOS, este es el espacio de nombres Xamarin.Forms.PlatformConfiguration.iOSSpecific.
    2. En Android, este es el espacio de nombres Xamarin.Forms.PlatformConfiguration.AndroidSpecific. Para Android AppCompat, este es el espacio de nombres Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat.
    3. En la Plataforma universal de Windows, este es el espacio de nombres Xamarin.Forms.PlatformConfiguration.WindowsSpecific.
  3. Aplique la característica específica de plataforma desde XAML o desde el código con la API fluida On<T>. El valor de T pueden ser los tipos iOS, Android o Windows del espacio de nombres Xamarin.Forms.PlatformConfiguration.

Nota:

Tenga en cuenta que si intenta consumir una característica específica de plataforma en una plataforma en la que no está disponible, no se producirá un error. En su lugar, el código se ejecutará sin que se aplique la característica específica de plataforma.

Las características específicas de plataforma consumidas a través de la API de código fluido On<T> devuelven objetos IPlatformElementConfiguration. Esto permite invocar varias características específicas de plataforma en el mismo objeto con el método en cascada.

Para obtener más información sobre las características específicas de plataforma proporcionados por Xamarin.Forms, consulte Características específicas de plataforma en iOS, Características específicas de plataforma en Android y Características específicas de plataforma en Windows.

Creación de características específicas de plataforma

Los proveedores pueden crear sus propias características específicas de plataforma con efectos. Un efecto proporciona la funcionalidad específica, que luego se expone a través de una característica específica de plataforma. El resultado es un efecto que se puede consumir más fácilmente a través de XAML y a través de una API de código fluida.

El proceso para crear una característica específica de plataforma es el siguiente:

  1. Implemente la funcionalidad específica como un efecto. Para más información, consulte Creación de un Efecto.
  2. Cree una clase específica de plataforma que exponga el efecto. Para obtener más información, consulte Creación de una clase específica de plataforma.
  3. En la clase específica de plataforma, implemente una propiedad adjunta para permitir que la característica específica de plataforma se consuma a través de XAML. Para obtener más información, consulte Agregar una propiedad adjunta.
  4. En la clase específica de plataforma, implemente métodos de extensión para permitir que la característica específica de plataforma se consuma a través de una API de código fluida. Para obtener más información, consulte Agregar métodos de extensión.
  5. Modifique la implementación del efecto para que solo se aplique si se ha invocado la característica específica de plataforma en la misma plataforma que el efecto. Para más información, consulte Creación de un Efecto.

El resultado de exponer un efecto como una característica específica de plataforma es que se puede consumir más fácilmente a través de XAML y a través de una API de código fluida.

Nota:

Se prevé que los proveedores usen esta técnica para crear sus propias características específicas de plataforma, para facilitar el consumo por parte de los usuarios. Aunque los usuarios pueden optar por crear sus propias características específicas de plataforma, debe tenerse en cuenta que requiere más código que crear y consumir un efecto.

La aplicación de ejemplo muestra una característica específica de plataforma Shadow que agrega una sombra al texto mostrado por un control Label:

Propiedad reemplazada específica de la plataforma

La aplicación de ejemplo implementa la característica específica de plataforma Shadow de cada plataforma para facilitar la comprensión. Sin embargo, aparte de cada implementación de efecto de una característica específica de plataforma, la implementación de la clase de propiedad reemplazada es prácticamente idéntica para cada plataforma. Por lo tanto, esta guía se centra en la implementación de la clase Shadow y el efecto asociado en una sola plataforma.

Para obtener más información sobre los efectos, consulte Personalización de controles con efectos.

Creación de una clase de característica específica de plataforma

Se crea una característica específica de plataforma como una clase public static:

namespace MyCompany.Forms.PlatformConfiguration.iOS
{
  public static Shadow
  {
    ...
  }
}

En las siguientes secciones se describe la implementación de la característica específica de plataforma Shadow y el efecto asociado.

Adición de una propiedad adjunta

Se debe agregar una propiedad adjunta a la característica específica de plataforma Shadow para permitir el consumo a través de XAML:

namespace MyCompany.Forms.PlatformConfiguration.iOS
{
    using System.Linq;
    using Xamarin.Forms;
    using Xamarin.Forms.PlatformConfiguration;
    using FormsElement = Xamarin.Forms.Label;

    public static class Shadow
    {
        const string EffectName = "MyCompany.LabelShadowEffect";

        public static readonly BindableProperty IsShadowedProperty =
            BindableProperty.CreateAttached("IsShadowed",
                                            typeof(bool),
                                            typeof(Shadow),
                                            false,
                                            propertyChanged: OnIsShadowedPropertyChanged);

        public static bool GetIsShadowed(BindableObject element)
        {
            return (bool)element.GetValue(IsShadowedProperty);
        }

        public static void SetIsShadowed(BindableObject element, bool value)
        {
            element.SetValue(IsShadowedProperty, value);
        }

        ...

        static void OnIsShadowedPropertyChanged(BindableObject element, object oldValue, object newValue)
        {
            if ((bool)newValue)
            {
                AttachEffect(element as FormsElement);
            }
            else
            {
                DetachEffect(element as FormsElement);
            }
        }

        static void AttachEffect(FormsElement element)
        {
            IElementController controller = element;
            if (controller == null || controller.EffectIsAttached(EffectName))
            {
                return;
            }
            element.Effects.Add(Effect.Resolve(EffectName));
        }

        static void DetachEffect(FormsElement element)
        {
            IElementController controller = element;
            if (controller == null || !controller.EffectIsAttached(EffectName))
            {
                return;
            }

            var toRemove = element.Effects.FirstOrDefault(e => e.ResolveId == Effect.Resolve(EffectName).ResolveId);
            if (toRemove != null)
            {
                element.Effects.Remove(toRemove);
            }
        }
    }
}

La propiedad adjunta IsShadowed se usa para agregar y quitar el efecto MyCompany.LabelShadowEffect del control al que está asociada la clase Shadow. Esta propiedad adjunta registra el método OnIsShadowedPropertyChanged que se ejecutará cuando cambie el valor de la propiedad. A su vez, este método llama al método AttachEffect o DetachEffect para agregar o quitar el efecto en función del valor de la propiedad adjunta IsShadowed. El efecto se agrega o quita del control modificando la colección del control Effects.

Nota:

Tenga en cuenta que el efecto se resuelve especificando un valor que es una concatenación del nombre del grupo de resolución y el identificador único que se especifica en la implementación del efecto. Para más información, consulte Creación de un Efecto.

Para más información sobre las propiedades adjuntas, consulte Propiedades asociadas.

Adición de métodos de extensión

Los métodos de extensión se deben agregar a la característica específica de plataforma Shadow para permitir el consumo a través de una API de código fluida:

namespace MyCompany.Forms.PlatformConfiguration.iOS
{
    using System.Linq;
    using Xamarin.Forms;
    using Xamarin.Forms.PlatformConfiguration;
    using FormsElement = Xamarin.Forms.Label;

    public static class Shadow
    {
        ...
        public static bool IsShadowed(this IPlatformElementConfiguration<iOS, FormsElement> config)
        {
            return GetIsShadowed(config.Element);
        }

        public static IPlatformElementConfiguration<iOS, FormsElement> SetIsShadowed(this IPlatformElementConfiguration<iOS, FormsElement> config, bool value)
        {
            SetIsShadowed(config.Element, value);
            return config;
        }
        ...
    }
}

Los métodos de extensión IsShadowed y SetIsShadowed invocan los descriptores de acceso get y set para la propiedad adjunta IsShadowed, respectivamente. Cada método de extensión funciona en el tipo IPlatformElementConfiguration<iOS, FormsElement>, que especifica que la característica específica de plataforma se puede invocar en instancias Label de iOS.

Creación del efecto

La característica específica de plataforma Shadow agrega el MyCompany.LabelShadowEffect a Label y lo quita. En el ejemplo de código siguiente se muestra la implementación LabelShadowEffect para el proyecto de iOS:

[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace ShadowPlatformSpecific.iOS
{
    public class LabelShadowEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            UpdateShadow();
        }

        protected override void OnDetached()
        {
        }

        protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
        {
            base.OnElementPropertyChanged(args);

            if (args.PropertyName == Shadow.IsShadowedProperty.PropertyName)
            {
                UpdateShadow();
            }
        }

        void UpdateShadow()
        {
            try
            {
                if (((Label)Element).OnThisPlatform().IsShadowed())
                {
                    Control.Layer.CornerRadius = 5;
                    Control.Layer.ShadowColor = UIColor.Black.CGColor;
                    Control.Layer.ShadowOffset = new CGSize(5, 5);
                    Control.Layer.ShadowOpacity = 1.0f;
                }
                else if (!((Label)Element).OnThisPlatform().IsShadowed())
                {
                    Control.Layer.ShadowOpacity = 0;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
            }
        }
    }
}

El método UpdateShadow establece las propiedades Control.Layer para crear la propiedad reemplazada, siempre que la propiedad adjunta IsShadowed esté establecida en true y siempre que se haya invocado la característica específica de plataforma Shadow en la misma plataforma para la que se implementa el efecto. Esta comprobación se realiza con el método OnThisPlatform.

Si el valor de propiedad adjunta Shadow.IsShadowed cambia en runtime, el efecto debe responder quitando la propiedad reemplazada. Por lo tanto, se usa una versión invalidada del método OnElementPropertyChanged para responder al cambio de propiedad enlazable llamando al método UpdateShadow.

Para obtener más información sobre cómo crear un efecto, consulte Crear un efecto y Pasar parámetros de efecto como propiedades adjuntas.

Consumo de la característica específica de plataforma

La característica específica de plataforma Shadow se consume en XAML estableciendo la propiedad adjunta Shadow.IsShadowed en un valor de boolean:

<ContentPage xmlns:ios="clr-namespace:MyCompany.Forms.PlatformConfiguration.iOS" ...>
  ...
  <Label Text="Label Shadow Effect" ios:Shadow.IsShadowed="true" ... />
  ...
</ContentPage>

Como alternativa, se puede consumir desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using MyCompany.Forms.PlatformConfiguration.iOS;

...

shadowLabel.On<iOS>().SetIsShadowed(true);