共用方式為


將效果參數傳遞為附加屬性

附加屬性可用來定義會回應執行階段屬性變更的效果參數。 本文示範如何使用附加屬性將參數傳遞給效果,並在執行階段變更參數。

建立會回應執行階段屬性變更之效果參數的程序如下:

  1. 建立 static 類別,其中包含要傳遞至效果之每個參數的附加屬性。
  2. 將其他附加屬性新增至類別,該類別將用於控制新增或移除對該類別將會附加至之控制項的效果。 請確保此附加屬性會註冊 propertyChanged 委派;當屬性值變更時,就會執行此委派。
  3. 建立每個附加屬性的 static getter 和 setter。
  4. 實作 propertyChanged 委派中的邏輯,以新增和移除效果。
  5. static 類別內實作巢狀類別,該類別是 RoutingEffect 類別的子類別,以效果來命名。 針對建構函式,呼叫基底類別建構函式,並傳入解析群組名稱串連,以及每個平台特定效果類別所指定的唯一識別碼。

參數可藉由將附加屬性和屬性值新增至適當的控制項中來傳遞效果。 此外,參數可以藉由指定新的附加屬性值,在執行階段變更。

注意

附加屬性是可繫結屬性的特殊類型,定義於一個類別但附加至其他物件,而且在 XAML 中會識別為包含類別和屬性名稱並以句號分隔的屬性。 如需詳細資訊,請參閱附加屬性

範例應用程式所示範的 ShadowEffect 會將陰影新增至 Label 控制項所顯示的文字。 此外,陰影的色彩可以在執行階段變更。 下圖說明應用程式範例中每個專案的責任,以及這些專案之間的關聯性:

陰影效果專案責任

HomePage 上的 Label 控制項是由每個平台特定專案中的 LabelShadowEffect 所自訂。 透過 ShadowEffect 類別中的附加屬性,將參數傳遞至每個 LabelShadowEffect。 每個 LabelShadowEffect 類別都衍生自每個平台的 PlatformEffect 類別。 這會導致將陰影新增至 Label 控制項所顯示的文字,如下列螢幕擷取畫面所示:

每個平台上的陰影效果

建立效果參數

應建立代表效果參數的 static 類別,如下列程式碼範例所示:

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

ShadowEffect 包含五個附加屬性,每個附加屬性都具有 static getter 和 setter。 其中四個屬性代表要傳遞至每個平台特定 LabelShadowEffect 的參數。 ShadowEffect 類別也會定義 HasShadow 附加屬性,該附加屬性會用於控制附加至控制項之 ShadowEffect 類別效果的新增或移除。 此附加屬性會註冊 OnHasShadowChanged 方法;當屬性的值變更時,就會執行此方法。 此方法會根據 HasShadow 附加屬性的值來新增或移除效果。

巢狀 LabelShadowEffect 類別 (會將 RoutingEffect 類別分類為子類別) 支援新增和移除效果。 RoutingEffect 類別代表包裝通常是平台特定之內部效果的平台獨立效果。 這可簡化效果移除程序,因為對於平台特定效果,並不存在對類型資訊的編譯時間資訊存取。 LabelShadowEffect 建構函式會呼叫基底類別建構函式,並傳入由解析群組名稱串連組成的參數,以及每個平台特定效果類別所指定的唯一識別碼。 這會在 OnHasShadowChanged 方法中提供效果的新增和移除,如下所示:

  • 效果新增LabelShadowEffect 的新執行個體會新增至控制項的 Effects 集合。 這會取代使用 Effect.Resolve 方法來新增效果。
  • 效果移除 – 會擷取並移除控件Effects集合中的 第一個實例LabelShadowEffect

使用效果

您可以將附加屬性新增至 Label 控制項來使用每個平台特定的 LabelShadowEffect,如下列 XAML 程式碼範例所示:

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

下列程式碼範例顯示 C# 中的對等 Label

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

ShadowEffect.HasShadow 附加屬性設為 true 以執行 ShadowEffect.OnHasShadowChanged 方法,該方法會將 LabelShadowEffect 新增至或移除自 Label 控制項。 在這兩個程式碼範例中,ShadowEffect.Color 附加屬性會提供平台特定的色彩值。 如需詳細資訊,請參閱裝置類別

此外,Button 可讓陰影色彩在執行階段變更。 按一下 Button 時,下列程式碼會藉由設定 ShadowEffect.Color 附加屬性來變更陰影色彩:

ShadowEffect.SetColor (label, Color.Teal);

透過樣式使用效果

效果可以透過將附加屬性新增至控制項來使用,也可以由樣式使用。 下列 XAML 程式碼範例顯示陰影效果的「明確」樣式,該陰影效果可以套用至 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>

使用 StaticResource 標記延伸將其 Style 屬性設為 Style 執行個體,Style 即可套用至 Label,如下列程式碼範例所示:

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

如需樣式的詳細資訊,請參閱樣式

在每個平台上建立效果

下列各節會討論 LabelShadowEffect 類別的平台特定實作。

iOS 專案

下列程式碼範例會示範 iOS 專案的 LabelShadowEffect 實作:

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

OnAttached 方法會呼叫使用 ShadowEffect getter 來擷取附加屬性值的方法,將 Control.Layer 屬性設為屬性值以建立陰影。 這項功能會包裝在 try/catch 區塊中,以免效果附加至的控制項沒有 Control.Layer 屬性。 因為沒有必要的清除,所以 OnDetached 方法不提供實作。

回應屬性變更

如果任何 ShadowEffect 附加屬性值在執行階段變更,則效果需要透過顯示變更來回應。 在平台特定的效果類別中,OnElementPropertyChanged 方法的覆寫版本是回應可繫結屬性變更的位置,如下列程式碼範例所示:

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

OnElementPropertyChanged 方法會更新陰影的半徑、色彩或位移,前提是已變更適當的 ShadowEffect 附加屬性值。 因為此覆寫會呼叫多次,所以請一律檢查變更的屬性。

Android 專案

下列程式碼範例會示範 Android 專案的 LabelShadowEffect 實作:

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

OnAttached 方法會呼叫使用 ShadowEffect getter 來擷取附加屬性值的方法,並呼叫會呼叫 TextView.SetShadowLayer 方法的方法,以使用屬性值來建立陰影。 這項功能會包裝在 try/catch 區塊中,以免效果附加至的控制項沒有 Control.Layer 屬性。 因為沒有必要的清除,所以 OnDetached 方法不提供實作。

回應屬性變更

如果任何 ShadowEffect 附加屬性值在執行階段變更,則效果需要透過顯示變更來回應。 在平台特定的效果類別中,OnElementPropertyChanged 方法的覆寫版本是回應可繫結屬性變更的位置,如下列程式碼範例所示:

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

OnElementPropertyChanged 方法會更新陰影的半徑、色彩或位移,前提是已變更適當的 ShadowEffect 附加屬性值。 因為此覆寫會呼叫多次,所以請一律檢查變更的屬性。

通用 Windows 平台專案

下列程式碼範例示範通用 Windows 平台 (UWP) 專案的 LabelShadowEffect 實作:

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

通用 Windows 平台未提供陰影效果,因此這兩個平台上的 LabelShadowEffect 實作會透過在主要 Label 後方新增第二個偏移的 Label 來模擬此效果。 OnAttached 方法會建立新的 Label,並在 Label 上設定一些配置屬性。 然後,該方法會呼叫使用 ShadowEffect getter 來擷取附加屬性值的方法,並藉由設定 TextColorTranslationXTranslationY 屬性以控制色彩和 Label 的位置,來建立陰影。 shadowLabel 會接著偏移插入在主要 Label 的後方。 這項功能會包裝在 try/catch 區塊中,以免效果附加至的控制項沒有 Control.Layer 屬性。 因為沒有必要的清除,所以 OnDetached 方法不提供實作。

回應屬性變更

如果任何 ShadowEffect 附加屬性值在執行階段變更,則效果需要透過顯示變更來回應。 在平台特定的效果類別中,OnElementPropertyChanged 方法的覆寫版本是回應可繫結屬性變更的位置,如下列程式碼範例所示:

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

OnElementPropertyChanged 方法會更新陰影的色彩或位移,前提是已變更適當的 ShadowEffect 附加屬性值。 因為此覆寫會呼叫多次,所以請一律檢查變更的屬性。

摘要

本文示範如何使用附加屬性將參數傳遞給效果,並在執行階段變更參數。 附加屬性可用來定義會回應執行階段屬性變更的效果參數。