将效果参数作为公共语言运行时属性传递

公共语言运行时 (CLR) 属性可用于定义不响应运行时属性更改的效果参数。 本文演示如何使用 CLR 属性将参数传递给效果。

创建不响应运行时属性更改的效果参数的过程如下:

  1. 创建一个 public 类,它将 RoutingEffect 类作为子类。 RoutingEffect 类表示独立于平台的效果,该效果包装通常特定于平台的内部效果。
  2. 创建一个调用基类构造函数的构造函数,传递解析组名称以及每个特定于平台的效果类上指定的唯一 ID 的串联。
  3. 为要传递给效果的每个参数向类添加属性。

在实例化效果时,为每个属性指定值可将参数传递给效果。

示例应用程序展示了向 Label 控件显示的文本添加阴影的 ShadowEffect。 下图说明了示例应用程序中每个项目的职责,以及它们之间的关系:

阴影效果项目职责

HomePage 上的 Label 控件由特定于平台的各项目中的 LabelShadowEffect 自定义。 参数通过 ShadowEffect 类中的属性传递给每个 LabelShadowEffect。 每个 LabelShadowEffect 类均派生自各平台的 PlatformEffect 类。 这就使阴影被添加到 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 的参数。 类构造函数调用基类构造函数,传入由解析组名称以及每个特定于平台的效果类上指定的唯一 ID 的串联组成的参数。 因此,在实例化 ShadowEffect 时,将向控件的 Effects 集合添加 MyCompany.LabelShadowEffect 的新实例。

使用效果

下面的 XAML 代码示例演示附加了 ShadowEffectLabel 控件:

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

在各平台上创建效果

以下各部分讨论特定于平台的 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 实现通过在主 Label 后面添加第二个偏移 Label 来进行模拟。 OnAttached 方法检索 ShadowEffect 实例,创建新的 Label,并在 Label 上设置一些布局属性。 然后通过设置 TextColorTranslationXTranslationY 属性来控制 Label 的颜色和位置,从而创建阴影。 shadowLabel 随后插入主 Label 后面并偏移。 效果所附加到的控件没有 Control.Layer 属性时,此功能包装在 try/catch 块中。 OnDetached 方法不提供任何实现,因为不需要进行清理。

总结

本文演示如何使用 CLR 属性将参数传递给效果。 CLR 属性可用于定义不响应运行时属性更改的效果参数。