平台特定内容

平台特定的功能让你可以使用仅在特定平台上提供的功能,而无需实现自定义呈现器或效果。

通过 XAML 或 Fluent 代码 API 使用特定于平台的过程如下所示:

  1. Xamarin.Forms.PlatformConfiguration 命名空间添加 xmlns 声明或 using 指令。
  2. 为包含特定于平台的功能的命名空间添加 xmlns 声明或 using 指令:
    1. 在 iOS 上,这是 Xamarin.Forms.PlatformConfiguration.iOSSpecific 命名空间。
    2. 在 Android 上,这是 Xamarin.Forms.PlatformConfiguration.AndroidSpecific 命名空间。 对于 Android AppCompat,这是 Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat 命名空间。
    3. 在通用 Windows 平台上,这是 Xamarin.Forms.PlatformConfiguration.WindowsSpecific 命名空间。
  3. 使用 On<T> Fluent API 通过 XAML 或代码应用特定于平台的内容。 T 的值可以是 Xamarin.Forms.PlatformConfiguration 命名空间中的 iOSAndroidWindows 类型。

注意

请注意,尝试在特定于平台的内容不可用的平台上使用它将不会造成错误。 相反,代码将执行,而不会应用特定于平台的内容。

通过 On<T> Fluent 代码 API 使用的特定于平台的内容会返回 IPlatformElementConfiguration 对象。 这样,就可以使用方法级联在同一对象上调用多个特定于平台的内容。

有关 Xamarin.Forms 提供的特定于平台的内容的详细信息,请参阅 iOS 平台特定Android 平台特定Windows 平台特定

创建平台特定内容

供应商可以使用效果创建自己的平台特定内容。 效果可提供特定功能,功能通过特定于平台的内容公开。 结果是一种效果,可通过 XAML 和 Fluent 代码 API 更轻松地使用。

创建特定于平台的内容的过程如下所示:

  1. 将特定功能实现为效果。 有关详细信息,请参阅创建效果
  2. 创建一个平台特定的类,该类将公开效果。 有关详细信息,请参阅创建特定于平台的类
  3. 在特定于平台的类中,实现附加属性,以允许通过 XAML 使用特定于平台的内容。 有关详细信息,请参阅添加附加属性
  4. 在特定于平台的类中,实现扩展方法,以允许通过 Fluent 代码 API 使用特定于平台的内容。 有关详细信息,请参阅添加扩展方法
  5. 修改效果实现,以便仅在与效果相同的平台上调用特定于平台的内容时应用效果。 有关详细信息,请参阅创建效果

将效果公开为特定于平台的内容的结果是,可以通过 XAML 和 Fluent 代码 API 更轻松地使用效果。

注意

预计供应商将使用此技术创建自己的平台特定内容,以便用户轻松使用它们。 虽然用户可以选择创建自己的平台特定内容,但应指出,它需要比创建和使用效果更多的代码。

示例应用程序展示了特定于平台的 Shadow,它将向 Label 控件显示的文本添加阴影:

特定于平台的阴影

示例应用程序将在每个平台上实现特定于平台的 Shadow,以便于读者理解。 但是,除了每个特定于平台的效果实现之外,阴影类的实现对于每个平台来说基本上都是相同的。 因此,本指南重点介绍阴影类的实现以及单个平台上的关联效果。

有关效果的详细信息,请参阅使用效果自定义控件

创建特定于平台的类

特定于平台的类创建为 public static 类:

namespace MyCompany.Forms.PlatformConfiguration.iOS
{
  public static Shadow
  {
    ...
  }
}

以下部分讨论 Shadow 平台特定内容和关联的效果的实现。

添加附加属性

必须将附加属性添加到 Shadow 平台特定内容,以允许通过 XAML 使用:

namespace MyCompany.Forms.PlatformConfiguration.iOS
{
    using System.Linq;
    using Xamarin.Forms;
    using Xamarin.Forms.PlatformConfiguration;
    using FormsElement = Xamarin.Forms.Label;

    public static class Shadow
    {
        const string EffectName = "MyCompany.LabelShadowEffect";

        public static readonly BindableProperty IsShadowedProperty =
            BindableProperty.CreateAttached("IsShadowed",
                                            typeof(bool),
                                            typeof(Shadow),
                                            false,
                                            propertyChanged: OnIsShadowedPropertyChanged);

        public static bool GetIsShadowed(BindableObject element)
        {
            return (bool)element.GetValue(IsShadowedProperty);
        }

        public static void SetIsShadowed(BindableObject element, bool value)
        {
            element.SetValue(IsShadowedProperty, value);
        }

        ...

        static void OnIsShadowedPropertyChanged(BindableObject element, object oldValue, object newValue)
        {
            if ((bool)newValue)
            {
                AttachEffect(element as FormsElement);
            }
            else
            {
                DetachEffect(element as FormsElement);
            }
        }

        static void AttachEffect(FormsElement element)
        {
            IElementController controller = element;
            if (controller == null || controller.EffectIsAttached(EffectName))
            {
                return;
            }
            element.Effects.Add(Effect.Resolve(EffectName));
        }

        static void DetachEffect(FormsElement element)
        {
            IElementController controller = element;
            if (controller == null || !controller.EffectIsAttached(EffectName))
            {
                return;
            }

            var toRemove = element.Effects.FirstOrDefault(e => e.ResolveId == Effect.Resolve(EffectName).ResolveId);
            if (toRemove != null)
            {
                element.Effects.Remove(toRemove);
            }
        }
    }
}

IsShadowed 附加属性用于向 Shadow 类附加到的控件添加 MyCompany.LabelShadowEffect 效果以及从其移除该效果。 该附加属性注册属性值更改时执行的 OnIsShadowedPropertyChanged 方法。 反过来,此方法会调用 AttachEffectDetachEffect 方法,以基于 IsShadowed 附加属性的值添加或移除效果。 通过修改控件的 Effects 集合,在控件中添加或移除效果。

注意

请注意,效果是通过指定一个值来解析的,该值是在效果实现上指定的解析组名称和唯一标识符的串联。 有关详细信息,请参阅创建效果

有关附加属性的详细信息,请参阅附加属性

添加扩展方法

必须将扩展方法添加到 Shadow 平台特定内容,以允许通过 Fluent 代码 API 使用:

namespace MyCompany.Forms.PlatformConfiguration.iOS
{
    using System.Linq;
    using Xamarin.Forms;
    using Xamarin.Forms.PlatformConfiguration;
    using FormsElement = Xamarin.Forms.Label;

    public static class Shadow
    {
        ...
        public static bool IsShadowed(this IPlatformElementConfiguration<iOS, FormsElement> config)
        {
            return GetIsShadowed(config.Element);
        }

        public static IPlatformElementConfiguration<iOS, FormsElement> SetIsShadowed(this IPlatformElementConfiguration<iOS, FormsElement> config, bool value)
        {
            SetIsShadowed(config.Element, value);
            return config;
        }
        ...
    }
}

IsShadowedSetIsShadowed 扩展方法会分别调用 IsShadowed 附加属性的 get 和 set 访问器。 每个扩展方法都对 IPlatformElementConfiguration<iOS, FormsElement> 类型进行操作,该类型会指定可以在 iOS 中的 Label 实例上调用的特定于平台的内容。

创建效果

Shadow 平台特定内容会将 MyCompany.LabelShadowEffect 添加到 Label,以及将其移除。 以下代码示例展示了 iOS 项目的 LabelShadowEffect 实现:

[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace ShadowPlatformSpecific.iOS
{
    public class LabelShadowEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            UpdateShadow();
        }

        protected override void OnDetached()
        {
        }

        protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
        {
            base.OnElementPropertyChanged(args);

            if (args.PropertyName == Shadow.IsShadowedProperty.PropertyName)
            {
                UpdateShadow();
            }
        }

        void UpdateShadow()
        {
            try
            {
                if (((Label)Element).OnThisPlatform().IsShadowed())
                {
                    Control.Layer.CornerRadius = 5;
                    Control.Layer.ShadowColor = UIColor.Black.CGColor;
                    Control.Layer.ShadowOffset = new CGSize(5, 5);
                    Control.Layer.ShadowOpacity = 1.0f;
                }
                else if (!((Label)Element).OnThisPlatform().IsShadowed())
                {
                    Control.Layer.ShadowOpacity = 0;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
            }
        }
    }
}

UpdateShadow 方法会设置 Control.Layer 属性以创建阴影,前提是 IsShadowed 附加属性设置为 true,且已在实现效果的同一平台上调用了 Shadow 特定于平台的内容。 此检查通过 OnThisPlatform 方法执行。

如果 Shadow.IsShadowed 附加属性值在运行时发生更改,则效果需要通过移除阴影来响应。 因此,OnElementPropertyChanged 方法的替代版本用于通过调用 UpdateShadow 方法来响应可绑定属性更改。

有关创建效果的详细信息,请参阅创建效果以及传递效果参数作为附加属性

使用特定于平台的内容

通过将 Shadow.IsShadowed 附加属性设置为 boolean 值,在 XAML 中使用 Shadow 特定于平台的内容:

<ContentPage xmlns:ios="clr-namespace:MyCompany.Forms.PlatformConfiguration.iOS" ...>
  ...
  <Label Text="Label Shadow Effect" ios:Shadow.IsShadowed="true" ... />
  ...
</ContentPage>

或者,可以使用 Fluent API 从 C# 使用它:

using Xamarin.Forms.PlatformConfiguration;
using MyCompany.Forms.PlatformConfiguration.iOS;

...

shadowLabel.On<iOS>().SetIsShadowed(true);