创建效果

效果简化了控件的自定义。 本文演示如何创建效果,在指向 Entry 控件时更改其背景颜色。

在每个特定于平台的项目中创建效果的过程如下:

  1. 创建 PlatformEffect 类的子类。
  2. 替代 OnAttached 方法并写入自定义控件的逻辑。
  3. 根据需要替代 OnDetached 方法并写入清理控件自定义的逻辑。
  4. 向效果类添加 ResolutionGroupName 属性。 此属性为效果设置一个公司范围的命名空间,以避免与同名的其他效果发生冲突。 请注意,每个项目只能应用一次该属性。
  5. 向效果类添加 ExportEffect 属性。 该属性使用 Xamarin.Forms 所用的唯一 ID 以及组名注册效果,以便在将应用于控件之前定位该效果。 该属性接受两个参数 - 效果的类型名称和一个唯一的字符串,该字符串用于在将效果应用于控件之前定位该效果。

然后,可以通过将效果附加到相应控件来使用该效果。

注意

可选择在每个平台项目中提供效果。 尝试在未注册时使用效果将返回一个不执行任何操作的非 null 值。

示例应用程序演示了 FocusEffect,它在获得焦点时更改控件的背景颜色。 下图说明了示例应用程序中每个项目的职责,以及它们之间的关系:

焦点效果项目职责

HomePage 上的 Entry 控件由特定于平台的各项目中的 FocusEffect 类自定义。 每个 FocusEffect 类均派生自各平台的 PlatformEffect 类。 这导致使用特定于平台的背景颜色呈现 Entry 控件,背景颜色在控件获得焦点时变化,如以下屏幕截图所示:

每个平台的焦点效果,已为控件设置焦点每个平台的焦点效果,未为控件设置焦点

在各平台上创建效果

以下各部分讨论特定于平台的 FocusEffect 类的实现。

iOS 项目

以下代码示例展示了 iOS 项目的 FocusEffect 实现:

using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(EffectsDemo.iOS.FocusEffect), nameof(EffectsDemo.iOS.FocusEffect))]
namespace EffectsDemo.iOS
{
    public class FocusEffect : PlatformEffect
    {
        UIColor backgroundColor;

        protected override void OnAttached ()
        {
            try {
                Control.BackgroundColor = backgroundColor = UIColor.FromRGB (204, 153, 255);
            } catch (Exception ex) {
                Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
            }
        }

        protected override void OnDetached ()
        {
        }

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

            try {
                if (args.PropertyName == "IsFocused") {
                    if (Control.BackgroundColor == backgroundColor) {
                        Control.BackgroundColor = UIColor.White;
                    } else {
                        Control.BackgroundColor = backgroundColor;
                    }
                }
            } catch (Exception ex) {
                Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
            }
        }
    }
}

OnAttached 方法使用 UIColor.FromRGB 方法将控件的 BackgroundColor 属性设置为淡紫色,并将该颜色存储在字段中。 效果所附加到的控件没有 BackgroundColor 属性时,此功能包装在 try/catch 块中。 OnDetached 方法不提供任何实现,因为不需要进行清理。

OnElementPropertyChanged 替代响应 Xamarin.Forms 控件上的可绑定属性更改。 当 IsFocused 属性更改时,如果控件具有焦点,控件的 BackgroundColor 属性更改为白色;否则更改为淡紫色。 效果所附加到的控件没有 BackgroundColor 属性时,此功能包装在 try/catch 块中。

Android 项目

以下代码示例展示了 Android 项目的 FocusEffect 实现:

using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(EffectsDemo.Droid.FocusEffect), nameof(EffectsDemo.Droid.FocusEffect))]
namespace EffectsDemo.Droid
{
    public class FocusEffect : PlatformEffect
    {
        Android.Graphics.Color originalBackgroundColor = new Android.Graphics.Color(0, 0, 0, 0);
        Android.Graphics.Color backgroundColor;

        protected override void OnAttached()
        {
            try
            {
                backgroundColor = Android.Graphics.Color.LightGreen;
                Control.SetBackgroundColor(backgroundColor);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
            }
        }

        protected override void OnDetached()
        {
        }

        protected override void OnElementPropertyChanged(System.ComponentModel.PropertyChangedEventArgs args)
        {
            base.OnElementPropertyChanged(args);
            try
            {
                if (args.PropertyName == "IsFocused")
                {
                    if (((Android.Graphics.Drawables.ColorDrawable)Control.Background).Color == backgroundColor)
                    {
                        Control.SetBackgroundColor(originalBackgroundColor);
                    }
                    else
                    {
                        Control.SetBackgroundColor(backgroundColor);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
            }
        }
    }
}

OnAttached 方法调用 SetBackgroundColor 方法将控件的背景色设置为浅绿色,并将此颜色存储在字段中。 效果所附加到的控件没有 SetBackgroundColor 属性时,此功能包装在 try/catch 块中。 OnDetached 方法不提供任何实现,因为不需要进行清理。

OnElementPropertyChanged 替代响应 Xamarin.Forms 控件上的可绑定属性更改。 当 IsFocused 属性更改时,如果控件具有焦点,控件的背景色更改为白色;否则更改为淡紫色。 效果所附加到的控件没有 BackgroundColor 属性时,此功能包装在 try/catch 块中。

通用 Windows 平台项目

下面的代码示例显示了通用 Windows 平台 (UWP) 项目的 FocusEffect 实现:

using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;

[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(EffectsDemo.UWP.FocusEffect), nameof(EffectsDemo.UWP.FocusEffect))]
namespace EffectsDemo.UWP
{
    public class FocusEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            try
            {
                (Control as Windows.UI.Xaml.Controls.Control).Background = new SolidColorBrush(Colors.Cyan);
                (Control as FormsTextBox).BackgroundFocusBrush = new SolidColorBrush(Colors.White);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
            }
        }

        protected override void OnDetached()
        {
        }
    }
}

OnAttached 方法将控件的 Background 属性设置为蓝绿色,将 BackgroundFocusBrush 属性设置为白色。 效果所附加到的控件没有这些属性时,此功能包装在 try/catch 块中。 OnDetached 方法不提供任何实现,因为不需要进行清理。

使用效果

使用 Xamarin.Forms .NET Standard 库或共享库项目中的效果的过程如下:

  1. 声明将由效果自定义的控件。
  2. 通过将该效果添加到控件的 Effects 集合中,将其附加到控件。

注意

效果实例只能附加到单个控件。 因此,若要在两个控件上使用,必须解析效果两次。

在 XAML 中使用效果

下面的 XAML 代码示例显示了附加 FocusEffectEntry 控件:

<Entry Text="Effect attached to an Entry" ...>
    <Entry.Effects>
        <local:FocusEffect />
    </Entry.Effects>
    ...
</Entry>

.NET Standard 库中的 FocusEffect 类支持在 XAML 中使用效果,如下面的代码示例所示:

public class FocusEffect : RoutingEffect
{
    public FocusEffect () : base ($"MyCompany.{nameof(FocusEffect)}")
    {
    }
}

FocusEffect 类将 RoutingEffect 类作为子类,它表示一个独立于平台的效果,该效果包装通常特定于平台的内部效果。 FocusEffect 类调用基类构造函数,传入由分辨率组名称(在效果类上使用 ResolutionGroupName 属性指定)以及唯一 ID(在效果类上使用 ExportEffect 属性指定)的串联组成的参数。 因此,在运行时初始化 Entry 时,向控件的 Effects 集合添加了 MyCompany.FocusEffect 的新实例。

还可以使用行为或附加属性将效果附加到控件。 有关使用行为将效果附加到控件的详细信息,请参阅可重用的 EffectBehavior。 有关使用附加属性将效果附加到控件的详细信息,请参阅将参数传递给效果

在 C# 中使用效果

下面的代码示例介绍了 C# 中的等效 Entry

var entry = new Entry {
  Text = "Effect attached to an Entry",
  ...
};

将效果添加到控件的 Effects 集合,即可将 FocusEffect 附加到 Entry 实例,如以下代码示例所示:

public HomePageCS ()
{
  ...
  entry.Effects.Add (Effect.Resolve ($"MyCompany.{nameof(FocusEffect)}"));
  ...
}

Effect.Resolve 返回指定名称的 Effect,该名称是分辨率组名称(在效果类上使用 ResolutionGroupName 属性指定)以及唯一 ID(在效果类上使用 ExportEffect 属性指定)的串联。 如果平台不提供此效果,则 Effect.Resolve 方法将返回一个非 null 值。

总结

本文演示如何创建效果,在指向 Entry 控件时更改其背景颜色。