Partager via


Passer des paramètres d’effet en tant que propriétés jointes

Les propriétés jointes permettent de définir des paramètres d’effet qui répondent aux changements apportés aux propriétés au moment de l’exécution. Cet article décrit comment utiliser des propriétés jointes pour passer des paramètres à un effet et comment changer un paramètre au moment de l’exécution.

Pour créer des paramètres d’effet qui répondent aux changements apportés aux propriétés au moment de l’exécution, suivez ces étapes :

  1. Créez une classe static qui contient une propriété jointe pour chaque paramètre à passer à l’effet.
  2. Ajoutez une propriété jointe supplémentaire à la classe qui contrôlera l’ajout ou la suppression de l’effet sur le contrôle auquel la classe sera jointe. Assurez-vous que cette propriété jointe inscrit un délégué propertyChanged qui sera exécuté au changement de la valeur de la propriété.
  3. Créez des méthodes getter et setter static pour chaque propriété jointe.
  4. Dans le délégué propertyChanged, implémentez la logique qui ajoute ou supprime l’effet.
  5. Implémentez une classe imbriquée dans la classe static, nommée d’après l’effet, pour sous-classer la classe RoutingEffect. Pour le constructeur, appelez le constructeur de classe de base, en passant une concaténation du nom du groupe de résolution, et l’ID unique qui a été spécifié sur la classe d’effet propre à chaque plateforme.

Les paramètres peuvent ensuite être passés à l’effet en ajoutant les propriétés jointes, et les valeurs de propriété, au contrôle approprié. En outre, les paramètres peuvent être changés au moment de l’exécution en spécifiant une nouvelle valeur de propriété jointe.

Remarque

Une propriété jointe est un type spécial de propriété pouvant être liée, définie dans une seule classe mais jointe à d’autres objets, et représentée en XAML sous forme d’attributs contenant une classe et un nom de propriété séparés par un point. Pour plus d’informations, consultez Propriétés jointes.

L’exemple d’application montre un ShadowEffect qui ajoute une ombre au texte affiché par un contrôle Label. La couleur de l’ombre peut également être changée au moment de l’exécution. Le diagramme suivant illustre les responsabilités de chaque projet dans l’exemple d’application ainsi que les relations qu’ils entretiennent les uns avec les autres :

Responsabilités de projet ShadowEffect

Un contrôle Label sur HomePage est personnalisé par LabelShadowEffect dans chaque projet propre à la plateforme. Les paramètres sont passés à chaque LabelShadowEffect par le biais des propriétés jointes dans la classe ShadowEffect. Chaque classe LabelShadowEffect dérive de la classe PlatformEffect pour chaque plateforme. Il en résulte l’ajout d’une ombre au texte affiché par le contrôle Label, comme illustré dans les captures d’écran suivantes :

Effet d’ombre sur chaque plateforme

Création de paramètres d’effet

Une classe static doit être créée pour représenter les paramètres d’effet, comme illustré dans l’exemple de code suivant :

public static class ShadowEffect
{
  public static readonly BindableProperty HasShadowProperty =
    BindableProperty.CreateAttached ("HasShadow", typeof(bool), typeof(ShadowEffect), false, propertyChanged: OnHasShadowChanged);
  public static readonly BindableProperty ColorProperty =
    BindableProperty.CreateAttached ("Color", typeof(Color), typeof(ShadowEffect), Color.Default);
  public static readonly BindableProperty RadiusProperty =
    BindableProperty.CreateAttached ("Radius", typeof(double), typeof(ShadowEffect), 1.0);
  public static readonly BindableProperty DistanceXProperty =
    BindableProperty.CreateAttached ("DistanceX", typeof(double), typeof(ShadowEffect), 0.0);
  public static readonly BindableProperty DistanceYProperty =
    BindableProperty.CreateAttached ("DistanceY", typeof(double), typeof(ShadowEffect), 0.0);

  public static bool GetHasShadow (BindableObject view)
  {
    return (bool)view.GetValue (HasShadowProperty);
  }

  public static void SetHasShadow (BindableObject view, bool value)
  {
    view.SetValue (HasShadowProperty, value);
  }
  ...

  static void OnHasShadowChanged (BindableObject bindable, object oldValue, object newValue)
  {
    var view = bindable as View;
    if (view == null) {
      return;
    }

    bool hasShadow = (bool)newValue;
    if (hasShadow) {
      view.Effects.Add (new LabelShadowEffect ());
    } else {
      var toRemove = view.Effects.FirstOrDefault (e => e is LabelShadowEffect);
      if (toRemove != null) {
        view.Effects.Remove (toRemove);
      }
    }
  }

  class LabelShadowEffect : RoutingEffect
  {
    public LabelShadowEffect () : base ("MyCompany.LabelShadowEffect")
    {
    }
  }
}

La classe ShadowEffect contient cinq propriétés jointes, elles-mêmes ayant chacune des méthodes setter et getter static. Quatre de ces propriétés représentent les paramètres à passer à chaque LabelShadowEffect propre à la plateforme. La classe ShadowEffect définit également une propriété jointe HasShadow qui contrôle l’ajout ou la suppression de l’effet sur le contrôle auquel la classe ShadowEffect est jointe. Cette propriété jointe inscrit la méthode OnHasShadowChanged qui est exécutée au changement de la valeur de la propriété. Cette méthode ajoute ou supprime l’effet en fonction de la valeur de la propriété jointe HasShadow.

La classe imbriquée LabelShadowEffect, qui sous-classe la classe RoutingEffect, prend en charge l’ajout et la suppression d’effet. La classe RoutingEffect représente un effet indépendant de la plateforme qui wrappe un effet interne généralement propre à une plateforme. Cela simplifie le processus de suppression de l’effet, car il n’y a pas d’accès au moment de la compilation aux informations de type pour un effet propre à la plateforme. Le constructeur LabelShadowEffect appelle le constructeur de classe de base, en passant un paramètre constitué d’une concaténation du nom du groupe de résolution, et l’ID unique qui a été spécifié sur la classe d’effet propre à chaque plateforme. Cela permet l’ajout et la suppression de l’effet dans la méthode OnHasShadowChanged de la façon suivante :

  • Ajout de l’effet : une nouvelle instance de LabelShadowEffect est ajoutée à la collection Effects du contrôle. Cela évite d’utiliser la méthode Effect.Resolve pour ajouter l’effet.
  • Suppression d’effet : la première instance de la LabelShadowEffect collection du Effects contrôle est récupérée et supprimée.

Consommation de l’effet

Chaque LabelShadowEffect propre à la plateforme peut être consommé en ajoutant les propriétés jointes à un contrôle Label, comme illustré dans l’exemple de code XAML suivant :

<Label Text="Label Shadow Effect" ...
       local:ShadowEffect.HasShadow="true" local:ShadowEffect.Radius="5"
       local:ShadowEffect.DistanceX="5" local:ShadowEffect.DistanceY="5">
  <local:ShadowEffect.Color>
    <OnPlatform x:TypeArguments="Color">
        <On Platform="iOS" Value="Black" />
        <On Platform="Android" Value="White" />
        <On Platform="UWP" Value="Red" />
    </OnPlatform>
  </local:ShadowEffect.Color>
</Label>

Le contrôle Label équivalent en C# est présenté dans l’exemple de code suivant :

var label = new Label {
  Text = "Label Shadow Effect",
  ...
};

Color color = Color.Default;
switch (Device.RuntimePlatform)
{
    case Device.iOS:
        color = Color.Black;
        break;
    case Device.Android:
        color = Color.White;
        break;
    case Device.UWP:
        color = Color.Red;
        break;
}

ShadowEffect.SetHasShadow (label, true);
ShadowEffect.SetRadius (label, 5);
ShadowEffect.SetDistanceX (label, 5);
ShadowEffect.SetDistanceY (label, 5);
ShadowEffect.SetColor (label, color));

La propriété jointe ShadowEffect.HasShadow doit être définie sur true pour exécuter la méthode ShadowEffect.OnHasShadowChanged qui ajoute ou supprime LabelShadowEffect sur le contrôle Label. Dans les deux exemples de code, la propriété jointe ShadowEffect.Color définit des valeurs de couleur différentes pour chaque plateforme. Pour plus d’informations, consultez l’article sur la classe Device.

Avec un contrôle Button, la couleur de l’ombre peut également être changée au moment de l’exécution. En cas de clic sur Button, le code suivant change la couleur de l’ombre en définissant la propriété jointe ShadowEffect.Color :

ShadowEffect.SetColor (label, Color.Teal);

Consommation de l’effet avec un style

Les effets qui peuvent être consommés en ajoutant des propriétés jointes à un contrôle peuvent également être consommés par un style. L’exemple de code XAML suivant montre un style explicite pour l’effet d’ombre, applicable aux contrôles Label :

<Style x:Key="ShadowEffectStyle" TargetType="Label">
  <Style.Setters>
    <Setter Property="local:ShadowEffect.HasShadow" Value="True" />
    <Setter Property="local:ShadowEffect.Radius" Value="5" />
    <Setter Property="local:ShadowEffect.DistanceX" Value="5" />
    <Setter Property="local:ShadowEffect.DistanceY" Value="5" />
  </Style.Setters>
</Style>

Le Style peut être appliqué à un contrôle Label en définissant sa propriété Style sur l’instance Style à l’aide de l’extension de balisage StaticResource, comme illustré dans l’exemple de code suivant :

<Label Text="Label Shadow Effect" ... Style="{StaticResource ShadowEffectStyle}" />

Pour plus d’informations sur les styles, consultez Styles.

Création de l’effet sur chaque plateforme

Les sections suivantes décrivent l’implémentation de la classe LabelShadowEffect pour chaque plateforme.

Projet iOS

L’exemple de code suivant illustre l’implémentation de LabelShadowEffect pour le projet iOS :

[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.iOS
{
    public class LabelShadowEffect : PlatformEffect
    {
        protected override void OnAttached ()
        {
            try {
                UpdateRadius ();
                UpdateColor ();
                UpdateOffset ();
                Control.Layer.ShadowOpacity = 1.0f;
            } catch (Exception ex) {
                Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
            }
        }

        protected override void OnDetached ()
        {
        }
        ...

        void UpdateRadius ()
        {
            Control.Layer.ShadowRadius = (nfloat)ShadowEffect.GetRadius (Element);
        }

        void UpdateColor ()
        {
            Control.Layer.ShadowColor = ShadowEffect.GetColor (Element).ToCGColor ();
        }

        void UpdateOffset ()
        {
            Control.Layer.ShadowOffset = new CGSize (
                (double)ShadowEffect.GetDistanceX (Element),
                (double)ShadowEffect.GetDistanceY (Element));
        }
    }

La méthode OnAttached appelle les méthodes qui récupèrent les valeurs de propriété jointe à l’aide des méthodes getter ShadowEffect, et qui définissent les propriétés Control.Layer sur les valeurs de propriété pour créer l’ombre. Cette fonctionnalité est wrappée dans un bloc try/catch au cas où le contrôle auquel l’effet est joint n’a pas les propriétés Control.Layer. Aucune implémentation n’est fournie par la méthode OnDetached, car aucun nettoyage n’est nécessaire.

Répondre aux changements apportés aux propriétés

Si l’une des valeurs de la propriété jointe ShadowEffect change au moment de l’exécution, l’effet doit répondre en affichant les changements. Une version substituée de la méthode OnElementPropertyChanged, dans la classe d’effet propre à chaque plateforme, est utilisée pour répondre aux changements de la propriété pouvant être liée, comme illustré dans l’exemple de code suivant :

public class LabelShadowEffect : PlatformEffect
{
  ...
  protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)
  {
    if (args.PropertyName == ShadowEffect.RadiusProperty.PropertyName) {
      UpdateRadius ();
    } else if (args.PropertyName == ShadowEffect.ColorProperty.PropertyName) {
      UpdateColor ();
    } else if (args.PropertyName == ShadowEffect.DistanceXProperty.PropertyName ||
               args.PropertyName == ShadowEffect.DistanceYProperty.PropertyName) {
      UpdateOffset ();
    }
  }
  ...
}

La méthode OnElementPropertyChanged met à jour le rayon, la couleur ou le décalage de l’ombre si la valeur de la propriété jointe ShadowEffect appropriée a changé. Une vérification de la propriété changée doit toujours être effectuée, car cette substitution peut être appelée plusieurs fois.

Projet Android

L’exemple de code suivant illustre l’implémentation de LabelShadowEffect pour le projet Android :

[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.Droid
{
    public class LabelShadowEffect : PlatformEffect
    {
        Android.Widget.TextView control;
        Android.Graphics.Color color;
        float radius, distanceX, distanceY;

        protected override void OnAttached ()
        {
            try {
                control = Control as Android.Widget.TextView;
                UpdateRadius ();
                UpdateColor ();
                UpdateOffset ();
                UpdateControl ();
            } catch (Exception ex) {
                Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
            }
        }

        protected override void OnDetached ()
        {
        }
        ...

        void UpdateControl ()
        {
            if (control != null) {
                control.SetShadowLayer (radius, distanceX, distanceY, color);
            }
        }

        void UpdateRadius ()
        {
            radius = (float)ShadowEffect.GetRadius (Element);
        }

        void UpdateColor ()
        {
            color = ShadowEffect.GetColor (Element).ToAndroid ();
        }

        void UpdateOffset ()
        {
            distanceX = (float)ShadowEffect.GetDistanceX (Element);
            distanceY = (float)ShadowEffect.GetDistanceY (Element);
        }
    }

La méthode OnAttached appelle les méthodes qui récupèrent les valeurs de propriété jointe à l’aide des méthodes getter ShadowEffect, et appelle la méthode TextView.SetShadowLayer pour créer une ombre avec les valeurs de propriété définies. Cette fonctionnalité est wrappée dans un bloc try/catch au cas où le contrôle auquel l’effet est joint n’a pas les propriétés Control.Layer. Aucune implémentation n’est fournie par la méthode OnDetached, car aucun nettoyage n’est nécessaire.

Répondre aux changements apportés aux propriétés

Si l’une des valeurs de la propriété jointe ShadowEffect change au moment de l’exécution, l’effet doit répondre en affichant les changements. Une version substituée de la méthode OnElementPropertyChanged, dans la classe d’effet propre à chaque plateforme, est utilisée pour répondre aux changements de la propriété pouvant être liée, comme illustré dans l’exemple de code suivant :

public class LabelShadowEffect : PlatformEffect
{
  ...
  protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)
  {
    if (args.PropertyName == ShadowEffect.RadiusProperty.PropertyName) {
      UpdateRadius ();
      UpdateControl ();
    } else if (args.PropertyName == ShadowEffect.ColorProperty.PropertyName) {
      UpdateColor ();
      UpdateControl ();
    } else if (args.PropertyName == ShadowEffect.DistanceXProperty.PropertyName ||
               args.PropertyName == ShadowEffect.DistanceYProperty.PropertyName) {
      UpdateOffset ();
      UpdateControl ();
    }
  }
  ...
}

La méthode OnElementPropertyChanged met à jour le rayon, la couleur ou le décalage de l’ombre si la valeur de la propriété jointe ShadowEffect appropriée a changé. Une vérification de la propriété changée doit toujours être effectuée, car cette substitution peut être appelée plusieurs fois.

Projet de plateforme Windows universelle

L’exemple de code suivant montre l’implémentation de LabelShadowEffect pour le projet de plateforme Windows universelle (UWP) :

[assembly: ResolutionGroupName ("MyCompany")]
[assembly: ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.UWP
{
    public class LabelShadowEffect : PlatformEffect
    {
        Label shadowLabel;
        bool shadowAdded = false;

        protected override void OnAttached ()
        {
            try {
                if (!shadowAdded) {
                    var textBlock = Control as Windows.UI.Xaml.Controls.TextBlock;

                    shadowLabel = new Label ();
                    shadowLabel.Text = textBlock.Text;
                    shadowLabel.FontAttributes = FontAttributes.Bold;
                    shadowLabel.HorizontalOptions = LayoutOptions.Center;
                    shadowLabel.VerticalOptions = LayoutOptions.CenterAndExpand;

                    UpdateColor ();
                    UpdateOffset ();

                    ((Grid)Element.Parent).Children.Insert (0, shadowLabel);
                    shadowAdded = true;
                }
            } catch (Exception ex) {
                Debug.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
            }
        }

        protected override void OnDetached ()
        {
        }
        ...

        void UpdateColor ()
        {
            shadowLabel.TextColor = ShadowEffect.GetColor (Element);
        }

        void UpdateOffset ()
        {
            shadowLabel.TranslationX = ShadowEffect.GetDistanceX (Element);
            shadowLabel.TranslationY = ShadowEffect.GetDistanceY (Element);
        }
    }
}

La plateforme Windows universelle ne fournissant pas d’effet d’ombre, l’implémentation de LabelShadowEffect sur les deux plateformes en simule un en ajoutant un deuxième décalage Label derrière le contrôle Label principal. La méthode OnAttached crée le nouveau contrôle Label et définit certaines propriétés de disposition sur Label. Elle appelle ensuite les méthodes qui récupèrent les valeurs de propriété jointe à l’aide des méthodes getter ShadowEffect, puis elle crée l’ombre en définissant les propriétés TextColor, TranslationXet TranslationY qui contrôlent la couleur et l’emplacement de Label. Le contrôle shadowLabel est alors inséré en décalage derrière le contrôle Label principal. Cette fonctionnalité est wrappée dans un bloc try/catch au cas où le contrôle auquel l’effet est joint n’a pas les propriétés Control.Layer. Aucune implémentation n’est fournie par la méthode OnDetached, car aucun nettoyage n’est nécessaire.

Répondre aux changements apportés aux propriétés

Si l’une des valeurs de la propriété jointe ShadowEffect change au moment de l’exécution, l’effet doit répondre en affichant les changements. Une version substituée de la méthode OnElementPropertyChanged, dans la classe d’effet propre à chaque plateforme, est utilisée pour répondre aux changements de la propriété pouvant être liée, comme illustré dans l’exemple de code suivant :

public class LabelShadowEffect : PlatformEffect
{
  ...
  protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)
  {
    if (args.PropertyName == ShadowEffect.ColorProperty.PropertyName) {
      UpdateColor ();
    } else if (args.PropertyName == ShadowEffect.DistanceXProperty.PropertyName ||
                      args.PropertyName == ShadowEffect.DistanceYProperty.PropertyName) {
      UpdateOffset ();
    }
  }
  ...
}

La méthode OnElementPropertyChanged met à jour la couleur ou le décalage de l’ombre si la valeur de la propriété jointe ShadowEffect appropriée a changé. Une vérification de la propriété changée doit toujours être effectuée, car cette substitution peut être appelée plusieurs fois.

Résumé

Cet article a décrit comment utiliser des propriétés jointes pour passer des paramètres à un effet et comment changer un paramètre au moment de l’exécution. Les propriétés jointes permettent de définir des paramètres d’effet qui répondent aux changements apportés aux propriétés au moment de l’exécution.