Übergeben von Effect-Parametern als angefügte Eigenschaften

Beispiel herunterladen Das Beispiel herunterladen

Angefügte Eigenschaften können verwendet werden, um Effect-Parameter zu definieren, die auf Änderungen der Runtimeeigenschaften reagieren. In diesem Artikel wird veranschaulicht, wie Sie angefügte Eigenschaften verwenden können, um Parameter an einen Effekt zu übergeben, und wie Sie einen Parameter zur Laufzeit anpassen können.

Effect-Parameter, die auf die Änderungen von Runtimeeigenschaften reagieren, werden folgendermaßen erstellt:

  1. Erstellen Sie eine static-Klasse, die eine angefügte Eigenschaft für jeden Parameter enthält, die an den Effekt übergeben werden soll.
  2. Fügen Sie der Klasse eine zusätzliche angefügte Eigenschaft hinzu, die steuert, ob der Effekt dem Steuerelement hinzugefügt oder von diesem entfernt wird, an das die Klasse angefügt werden soll. Stellen Sie sicher, dass die angefügte Eigenschaft einen propertyChanged-Delegaten registriert, der ausgeführt wird, wenn der Wert der Eigenschaft geändert wird.
  3. Erstellen Sie static-Getter und -Setter für jede angefügte Eigenschaft.
  4. Implementieren Sie Logik zum Hinzufügen und Entfernen des Effekts im propertyChanged-Delegaten.
  5. Implementieren Sie eine geschachtelte Klasse innerhalb der static-Klasse, die nach dem Effekt benannt ist, der als Unterklasse der RoutingEffect-Klasse fungiert. Rufen Sie den Basisklassenkonstruktor für den Konstruktor auf, der eine Verkettung des Namens der Auflösungsgruppe übergibt, und die eindeutige ID, die in jeder plattformspezifischen Effect-Klasse angegeben wurde.

Parameter können dann an den Effekt übergeben werden, indem die angefügten Eigenschaften und die Eigenschaftswerte an das entsprechende Steuerelement übergeben werden. Parameter können außerdem zur Laufzeit geändert werden, indem ein neuer Wert für die angefügte Eigenschaft festgelegt wird.

Hinweis

Eine angefügte Eigenschaft stellt eine besondere Form von bindbaren Eigenschaften dar. Sie wird in einer Klasse definiert, jedoch an andere Objekte angefügt, und ist in XAML als Attribut erkennbar, das eine Klasse und einen Eigenschaftsnamen enthält, die durch einen Punkt voneinander getrennt sind. Weitere Informationen finden Sie unter Angefügte Eigenschaften.

Die Beispielanwendung veranschaulicht eine ShadowEffect-Klasse, die einen Schatten zu dem Text hinzufügt, der vom Label-Steuerelement angezeigt wird. Die Farbe des Schattens kann darüber hinaus zur Laufzeit geändert werden. Das folgende Diagramm veranschaulicht die Zuständigkeiten jedes Projekts in der Beispielanwendung sowie deren Beziehungen zueinander:

Verantwortlichkeiten des Schatteneffektprojekts

Ein Label-Steuerelement im HomePage-Element wird in jedem plattformspezifischen Projekt von LabelShadowEffect angepasst. Parameter werden über angefügte Eigenschaften in der ShadowEffect-Klasse an jede LabelShadowEffect-Klasse übergeben. Jede LabelShadowEffect-Klasse wird von der PlatformEffect-Klasse für jede Plattform abgeleitet. Dadurch wird wie in den folgenden Screenshots dargestellt ein Schatten zu dem Text hinzugefügt, der vom Label-Steuerelement angezeigt wird:

Schatteneffekt auf jeder Plattform

Erstellen von Effect-Parametern

Eine static-Klasse sollte wie im folgenden Codebeispiel gezeigt zur Darstellung von Effect-Parametern erstellt werden:

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

Die ShadowEffect-Klasse enthält fünf angefügte Eigenschaften mit static-Gettern und -Settern für jede angefügte Eigenschaft. Vier dieser Eigenschaften stellen die Parameter dar, die an jede plattformspezifischen LabelShadowEffect-Klasse übergeben werden sollen. Die ShadowEffect-Klasse definiert darüber hinaus eine angefügte Eigenschaft HasShadow, die steuert, ob der Effekt dem Steuerelement hinzugefügt oder von diesem entfernt wird, an das die ShadowEffect-Klasse angefügt ist. Diese angefügte Eigenschaft registriert die OnHasShadowChanged-Methode, die ausgeführt wird, wenn der Wert der Eigenschaft geändert wird. Basierend auf dem Wert der angefügten Eigenschaft HasShadow fügt diese Methode den Effekt an oder entfernt ihn.

Die geschachtelte LabelShadowEffect-Klasse, die als Unterklasse der RoutingEffect-Klasse implementiert wird, unterstützt das Hinzufügen und Entfernen von Effekten. Die RoutingEffect-Klasse stellt einen plattformunabhängigen Effekt dar, der einen in der Regel plattformspezifischen inneren Effekt umschließt. Dadurch wird das Entfernen eines Effekts vereinfacht, da während der Kompilierzeit nicht auf die Typinformationen für einen plattformspezifischen Effekt zugegriffen werden kann. Der LabelShadowEffect-Konstruktor ruft den Basisklassenkonstruktor auf, der einen Parameter bestehend aus einer Verkettung des Namens der Auflösungsgruppe übergibt, und die eindeutige ID, die in jeder plattformspezifischen Effect-Klasse angegeben wurde. Dadurch können Effekte in der OnHasShadowChanged-Methode folgendermaßen hinzugefügt oder entfernt werden:

  • Hinzufügen von Effekten: Eine neue Instanz der LabelShadowEffect-Klasse wird zur Effects-Collection des Steuerelements hinzugefügt. Dadurch wird mithilfe der Effect.Resolve-Methode eine Ersetzung durchgeführt, um den Effekt hinzuzufügen.
  • Effektentfernung: Die erste instance der LabelShadowEffect in der Auflistung des Steuerelements Effects wird abgerufen und entfernt.

Nutzen des Effekts

Jede plattformspezifische LabelShadowEffect-Klasse kann (wie im folgenden XAML-Codebeispiel veranschaulicht) verwendet werden, indem die angefügten Eigenschaften zu einem Label-Steuerelement hinzugefügt werden:

<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>

Das entsprechende Label-Steuerelement in C# wird im folgenden Codebeispiel veranschaulicht:

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

Wenn Sie die angefügte Eigenschaft ShadowEffect.HasShadow auf true festlegen, wird die ShadowEffect.OnHasShadowChanged-Methode ausgeführt, die LabelShadowEffect zum Label-Steuerelement hinzufügt oder aus diesem entfernt. In beiden Codebeispielen enthält die angefügte Eigenschaft ShadowEffect.Color plattformspezifische Farbwerte. Weitere Informationen finden Sie unter Xamarin.Forms Device Class (Xamarin.Forms-Geräteklasse).

Darüber hinaus wird durch Button ermöglicht, die Farbe des Schattens zur Laufzeit zu ändern. Wenn Sie auf Button klicken, ändert folgender Code die Schattenfarbe, indem die angefügte Eigenschaft ShadowEffect.Color festgelegt wird:

ShadowEffect.SetColor (label, Color.Teal);

Nutzen von Effect-Klassen mit einer Style-Klasse

Effect-Klassen können genutzt werden, indem Sie angefügte Eigenschaften zu einem Steuerelement hinzufügen, das auch von einer Formatvorlage verwendet werden kann. In folgendem XAML-Codebeispiel wird eine explizite Formatvorlage für den Schatteneffekt veranschaulicht, die auf Label-Steuerelemente angewendet werden kann:

<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>

Die Style-Klasse kann auf eine Label-Klasse angewendet werden, indem ihre Style-Eigenschaft wie im folgenden Codebeispiel gezeigt mithilfe der Markuperweiterung StaticResource für die Style-Instanz festgelegt wird:

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

Weitere Informationen zu Formatvorlagen finden Sie unter Styles (Formatvorlagen).

Erstellen von Effect-Klassen auf verschiedenen Plattformen

In den folgenden Abschnitten wird die plattformspezifische Implementierung der LabelShadowEffect-Klasse erläutert.

iOS-Projekt

Im folgenden Codebeispiel wird die Implementierung von LabelShadowEffect für das iOS-Projekt veranschaulicht:

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

Die OnAttached-Methode ruft Methoden auf, die die Werte der angefügten Eigenschaft mithilfe von ShadowEffect-Gettern abrufen und Control.Layer-Eigenschaften auf die Eigenschaftswerte festlegen, um den Schatten zu erstellen. Diese Funktion wird von einem try/catch-Block umschlossen, falls das Steuerelement, an das der Effekt angefügt ist, nicht über die Control.Layer-Eigenschaften verfügt. Von der OnDetached-Methode wird keine Implementierung bereitgestellt, da keine Bereinigung erforderlich ist.

Reaktion auf Änderungen bei den Eigenschaften

Wenn der Wert der angefügten ShadowEffect-Eigenschaft sich zur Laufzeit ändert, muss der Effekt darauf reagieren und die Änderungen darstellen. Auf Änderungen in bindbaren Eigenschaften muss in einer überschriebenen Version der OnElementPropertyChanged-Methode in der plattformspezifischen Effect-Klasse reagiert werden. Dies wird in folgendem Codebeispiel dargestellt:

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

Die OnElementPropertyChanged-Methode aktualisiert den Radius, die Farbe und das Offset des Schattens, wenn der Wert der entsprechenden angefügten ShadowEffect-Eigenschaft sich geändert hat. Eine geänderte Eigenschaft sollte immer überprüft werden, da die Möglichkeit besteht, dass diese Überschreibung häufig aufgerufen wird.

Android-Projekt

Im folgenden Codebeispiel wird die Implementierung von LabelShadowEffect für das Android-Projekt veranschaulicht:

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

Die OnAttached-Methode ruft Methoden auf, die die Werte der angefügten Eigenschaft mithilfe von ShadowEffect-Gettern abrufen. Außerdem wird eine Methode aufgerufen, die die TextView.SetShadowLayer-Methode aufruft, um einen Schatten auf Grundlage der Eigenschaftswerte zu erstellen. Diese Funktion wird von einem try/catch-Block umschlossen, falls das Steuerelement, an das der Effekt angefügt ist, nicht über die Control.Layer-Eigenschaften verfügt. Von der OnDetached-Methode wird keine Implementierung bereitgestellt, da keine Bereinigung erforderlich ist.

Reaktion auf Änderungen bei den Eigenschaften

Wenn der Wert der angefügten ShadowEffect-Eigenschaft sich zur Laufzeit ändert, muss der Effekt darauf reagieren und die Änderungen darstellen. Auf Änderungen in bindbaren Eigenschaften muss in einer überschriebenen Version der OnElementPropertyChanged-Methode in der plattformspezifischen Effect-Klasse reagiert werden. Dies wird in folgendem Codebeispiel dargestellt:

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

Die OnElementPropertyChanged-Methode aktualisiert den Radius, die Farbe und das Offset des Schattens, wenn der Wert der entsprechenden angefügten ShadowEffect-Eigenschaft sich geändert hat. Eine geänderte Eigenschaft sollte immer überprüft werden, da die Möglichkeit besteht, dass diese Überschreibung häufig aufgerufen wird.

Projekt für die Universelle Windows-Plattform

Das folgende Codebeispiel veranschaulicht die Implementierung von LabelShadowEffect für ein Projekt für die Universelle Windows-Plattform (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);
        }
    }
}

Die Universelle Windows-Plattform stellt keinen Schatteneffekt zur Verfügung, sodass die Implementierung von LabelShadowEffect auf beiden Plattformen einen Effekt erzeugt, indem hinter der primären Label-Klasse eine zweite versetzte Label-Klasse hinzugefügt wird. Die OnAttached-Methode erstellt die neue Label-Klasse und legt Layouteigenschaften für Label fest. Anschließend ruft diese Methoden auf, die die Werte der angefügten Eigenschaften mithilfe von ShadowEffect-Gettern abrufen, und erstellt den Schatten, indem die Eigenschaften TextColor, TranslationX und TranslationY darauf festgelegt werden, die Farbe und die Position von Label zu steuern. Das shadowLabel-Element wird dann versetzt hinter der primären Label-Klasse eingefügt. Diese Funktion wird von einem try/catch-Block umschlossen, falls das Steuerelement, an das der Effekt angefügt ist, nicht über die Control.Layer-Eigenschaften verfügt. Von der OnDetached-Methode wird keine Implementierung bereitgestellt, da keine Bereinigung erforderlich ist.

Reaktion auf Änderungen bei den Eigenschaften

Wenn der Wert der angefügten ShadowEffect-Eigenschaft sich zur Laufzeit ändert, muss der Effekt darauf reagieren und die Änderungen darstellen. Auf Änderungen in bindbaren Eigenschaften muss in einer überschriebenen Version der OnElementPropertyChanged-Methode in der plattformspezifischen Effect-Klasse reagiert werden. Dies wird in folgendem Codebeispiel dargestellt:

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

Die OnElementPropertyChanged-Methode aktualisiert die Farbe und das Offset des Schattens, wenn der Wert der entsprechenden angefügten ShadowEffect-Eigenschaft sich geändert hat. Eine geänderte Eigenschaft sollte immer überprüft werden, da die Möglichkeit besteht, dass diese Überschreibung häufig aufgerufen wird.

Zusammenfassung

In diesem Artikel wurde veranschaulicht, wie Sie angefügte Eigenschaften verwenden können, um Parameter an einen Effekt zu übergeben, und wie Sie einen Parameter zur Laufzeit anpassen können. Angefügte Eigenschaften können verwendet werden, um Effect-Parameter zu definieren, die auf Änderungen der Runtimeeigenschaften reagieren.