共通言語ランタイム プロパティとして効果のパラメーターを渡す

共通言語ランタイム (CLR) プロパティは、実行時のプロパティの変更に応答しないエフェクトのパラメーターの定義に使用できます。 この記事では、CLR プロパティを使用してエフェクトにパラメーターを渡す方法について説明します。

実行時のプロパティの変更に応答しないエフェクトのパラメーターを作成するプロセスは、次のとおりです。

  1. RoutingEffect クラスをサブクラス化する public クラスを作成します。 RoutingEffect クラスは、通常はプラットフォーム固有となる内部のエフェクトをラップするプラットフォームに依存しないエフェクトを表します。
  2. 解決グループ名と、各プラットフォーム固有のエフェクトクラスで指定された一意の ID を連結したものを渡して、基底クラス コンストラクターを呼び出すコンストラクターを作成します。
  3. エフェクトに渡す各パラメーターのクラスにプロパティを追加します。

効果をインスタンス化するときに各プロパティの値を指定することで、パラメーターを効果に渡すことができます。

このサンプル アプリケーションは、Label コントロールによって表示されるテキストに影を追加する ShadowEffect を示しています。 次の図に、サンプル アプリケーション内の各プロジェクトの役割と、それらの関係を示します。

影エフェクト プロジェクトの責任

HomePage 上の Label コントロールが、各プラットフォーム固有のプロジェクト内の LabelShadowEffect によってカスタマイズされます。 パラメーターは ShadowEffect クラス内のプロパティを介して各 LabelShadowEffect に渡されます。 各プラットフォームの PlatformEffect クラスから、各 LabelShadowEffect クラスが派生します。 これにより、次のスクリーンショットに示すように、Label コントロールによって表示されるテキストに影が追加されます。

各プラットフォーム上の影エフェクト

エフェクトのパラメーターを作成する

次のコード例に示すように、エフェクトのパラメーターを表すには、RoutingEffect クラスをサブクラス化する public クラスを作成する必要があります。

public class ShadowEffect : RoutingEffect
{
  public float Radius { get; set; }

  public Color Color { get; set; }

  public float DistanceX { get; set; }

  public float DistanceY { get; set; }

  public ShadowEffect () : base ("MyCompany.LabelShadowEffect")
  {            
  }
}

ShadowEffect には、各プラットフォーム固有の LabelShadowEffect に渡されるパラメーターを表す 4 つのプロパティが含まれています。 クラス コンストラクターから基底クラスのコンストラクターが呼び出され、解像度グループ名と、各プラットフォーム固有のエフェクト クラスで指定された一意の ID を連結したもので構成されるパラメーターが渡されます。 そのため、ShadowEffect がインスタンス化されると、MyCompany.LabelShadowEffect の新しいインスタンスがコントロールの Effects コレクションに追加されます。

エフェクトの使用

ShadowEffect がアタッチされている Label コントロールを次の XAML コード例に示します。

<Label Text="Label Shadow Effect" ...>
  <Label.Effects>
    <local:ShadowEffect Radius="5" DistanceX="5" 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>
    </local:ShadowEffect>
  </Label.Effects>
</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;
}

label.Effects.Add (new ShadowEffect {
  Radius = 5,
  Color = color,
  DistanceX = 5,
  DistanceY = 5
});

どちらのコード例でも、ShadowEffect クラスのインスタンスは、各プロパティに指定された値でインスタンス化されてから、コントロールの Effects コレクションに追加されます。 ShadowEffect.Color プロパティにはプラットフォーム固有の色の値が使用されることに注意してください。 詳しくは、「Device Class」(デバイス クラス) をご覧ください。

各プラットフォームでのエフェクトの作成

次のセクションで、LabelShadowEffect クラスのプラットフォーム固有の実装について説明します。

iOS プロジェクト

iOS プロジェクト用の LabelShadowEffect の実装を次のコード例に示します。

[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.iOS
{
    public class LabelShadowEffect : PlatformEffect
    {
        protected override void OnAttached ()
        {
            try {
                var effect = (ShadowEffect)Element.Effects.FirstOrDefault (e => e is ShadowEffect);
                if (effect != null) {
                    Control.Layer.ShadowRadius = effect.Radius;
                    Control.Layer.ShadowColor = effect.Color.ToCGColor ();
                    Control.Layer.ShadowOffset = new CGSize (effect.DistanceX, effect.DistanceY);
                    Control.Layer.ShadowOpacity = 1.0f;
                }
            } catch (Exception ex) {
                Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
            }
        }

        protected override void OnDetached ()
        {
        }
    }
}

OnAttached メソッドで ShadowEffect インスタンスを取得し、指定されたプロパティ値に Control.Layer プロパティを設定して影を作成します。 エフェクトがアタッチされているコントロールに Control.Layer プロパティがない場合に備えて、この機能が try/catch ブロック内にラップされます。 クリーンアップする必要がないので、OnDetached メソッドによる実装は提供されません。

Android プロジェクト

Android プロジェクト用の LabelShadowEffect の実装を次のコード例に示します。

[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.Droid
{
    public class LabelShadowEffect : PlatformEffect
    {
        protected override void OnAttached ()
        {
            try {
                var control = Control as Android.Widget.TextView;
                var effect = (ShadowEffect)Element.Effects.FirstOrDefault (e => e is ShadowEffect);
                if (effect != null) {
                    float radius = effect.Radius;
                    float distanceX = effect.DistanceX;
                    float distanceY = effect.DistanceY;
                    Android.Graphics.Color color = effect.Color.ToAndroid ();
                    control.SetShadowLayer (radius, distanceX, distanceY, color);
                }
            } catch (Exception ex) {
                Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
            }
        }

        protected override void OnDetached ()
        {
        }
    }
}

OnAttached メソッドで ShadowEffect インスタンスを取得し、指定されたプロパティ値を使用して TextView.SetShadowLayer メソッドを呼び出して影を作成します。 エフェクトがアタッチされているコントロールに Control.Layer プロパティがない場合に備えて、この機能が try/catch ブロック内にラップされます。 クリーンアップする必要がないので、OnDetached メソッドによる実装は提供されません。

ユニバーサル Windows プラットフォーム プロジェクト

ユニバーサル Windows プラットフォーム (UWP) プロジェクト用の LabelShadowEffect の実装を次のコード例に示します。

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

        protected override void OnAttached ()
        {
            try {
                if (!shadowAdded) {
                    var effect = (ShadowEffect)Element.Effects.FirstOrDefault (e => e is ShadowEffect);
                    if (effect != null) {
                        var textBlock = Control as Windows.UI.Xaml.Controls.TextBlock;
                        var shadowLabel = new Label ();
                        shadowLabel.Text = textBlock.Text;
                        shadowLabel.FontAttributes = FontAttributes.Bold;
                        shadowLabel.HorizontalOptions = LayoutOptions.Center;
                        shadowLabel.VerticalOptions = LayoutOptions.CenterAndExpand;
                        shadowLabel.TextColor = effect.Color;
                        shadowLabel.TranslationX = effect.DistanceX;
                        shadowLabel.TranslationY = effect.DistanceY;

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

ユニバーサル Windows プラットフォームは影エフェクトを提供しないため、両方のプラットフォーム上の LabelShadowEffect 実装は 2 番目のオフセット Label をプライマリ Label の背後に追加することで、その 1 つをシミュレートします。 OnAttached メソッドによって ShadowEffect インスタンスが取得され、新しい Label を作成され、Label にいくつかのレイアウト プロパティが設定されます。 次に、Label の色と場所を制御する TextColorTranslationXTranslationY プロパティを設定することで影が作成されます。 これにより、shadowLabel のプライマリ Label の背後にオフセットが挿入されます。 エフェクトがアタッチされているコントロールに Control.Layer プロパティがない場合に備えて、この機能が try/catch ブロック内にラップされます。 クリーンアップする必要がないので、OnDetached メソッドによる実装は提供されません。

まとめ

この記事では、CLR プロパティを使用してエフェクトにパラメーターを渡す方法について説明しました。 CLR プロパティは、実行時のプロパティの変更に応答しないエフェクトのパラメーターの定義に使用できます。