XAML 中的事件处理

已完成

创建 XAML UI 后,可添加代码来响应用户访问页面时发生的交互。 .NET MAUI 通过标准 .NET 事件通知应用程序发生了用户输入和交互。

本单元将介绍如何处理这些事件并执行用户预期的操作。

XAML 页面中的命名元素

事件处理代码经常需要引用页面上的特定控件及其属性。 可以为每个控件分配唯一名称。 为此,请使用 XAML 属性 x:Namex:Name 属性具有两个作用:

  • 它将专用字段添加到生成的代码隐藏文件中,该文件会映射到此元素。 在代码中使用该字段与视觉元素交互,以便设置运行时属性和处理事件。

  • XAML 通过此名称获知该元素。 可以从同一 XAML 文件中定义的其他元素来引用这些元素。

为元素命名时,不能使用任意字符串。 分配给 x:Name 属性的值用于在代码中创建字段。 名称必须符合变量的命名约定。 该名称还必须是唯一的,因为它会被编入代码隐藏定义。

为元素提供名称后,可以在代码隐藏文件中与该元素进行交互。 以下 XAML 片段定义 Label 控件。 它被命名为 CounterLabel(此示例摘自 .NET MAUI 模板生成的默认应用):

    <Label Text="Current count: 0"
        ...
        x:Name="CounterLabel"
        ... />

在此页面的代码隐藏中,可通过 CounterLabel 字段引用此控件,并修改其属性:

count++;
CounterLabel.Text = $"Current count: {count}";

重要

在运行页面的 InitializeComponent 方法之前,不会初始化该字段。 此方法是 XAML 分析和对象实例化过程的一部分。 在此调用后,放置与使用 XAML 定义的元素交互的任何代码。 此规则的例外情况是 ContentPage 类本身。 在执行 InitializeComponent 方法之前,可以访问此类的所有属性。 但是,如果使用 XAML 为此类设置任何属性,则这些属性值将覆盖执行 InitializeComponent 之前可能设置的任何值。

使用属性来连接事件

许多控件公开与这些控件可以响应的事件相对应的属性,例如按钮的 Clicked 事件。 不同控件支持不同事件集。 例如,Button 控件可以响应 ClickedPressedReleased 事件,而 Entry 控件响应 TextChanged 等事件。 可以在页面的 XAML 标记中初始化事件属性,并指定触发事件时要运行的方法的名称。 事件方法必须满足以下签名要求:

  • 它不能返回值;方法必须为 void
  • 它必须采用两个参数;一个是 object 引用,指示触发事件的对象(称为发送方),另一个是 EventArgs 参数,包含发送方传递给事件处理程序的任何自变量。
  • 事件处理程序应为 private。 没有强制要求这一点,但如果将事件处理程序公开,外界就可进行访问它,而且触发的预期事件以外的操作可以调用它。
  • 如果需要运行异步操作,事件处理程序可以是 async

下面的示例展示 .NET MAUI 模板中示例应用按钮的 Clicked 事件处理程序的定义。 方法名称遵循标准约定;On 后跟控件的名称(按钮名为 Counter)和事件的名称 (Clicked)。 此约定不会强制执行,但是很好的做法:

private void OnCounterClicked(object sender, EventArgs e)
{
    ...
}

分离关注点

连接使用 XAML 的事件很方便,但这会将控件行为与 UI 定义混合在一起。 许多开发人员倾向于将这些元素进行区分,并将代码隐藏中的所有事件处理程序订阅到命名元素。 这样更容易看到连接的内容以及行为映射到的位置。 这种方法还可使得更难在未意识到的情况下因移除 XAML 中的处理程序而意外破坏代码。 编译程序不会捕获已移除的处理程序,并且只有在代码未正确执行该行为时才会显示存在问题。

无论是选择使用 XAML 还是代码来连接事件处理程序,都取决于个人选择。

若要在代码中连接事件处理程序,请使用 += 运算符订阅事件。 调用 InitializeComponent 后,通常会在页面的构造函数中执行此操作:

public partial class MainPage : ContentPage, IPage
{
    public MainPage()
    {
        InitializeComponent();
        Counter.Clicked += OnCounterClicked;
    }

    ...

    private void OnCounterClicked(object sender, EventArgs e)
    {
        ...
    }
}

注意

可使用此方法订阅同一事件的多个事件处理方法。 每个事件处理方法都会在事件发生时运行,但不应假定这些方法将按任何特定顺序执行,因此不要在它们之间引入任何依赖项。

同样,稍后在应用程序中,可使用 -= 运算符从事件中取消订阅事件处理程序来移除它:

Counter.Clicked -= OnCounterClicked;

知识检查

1.

传递给事件处理方法的参数是什么?

2.

可以使用 C# 中的哪个运算符将事件处理程序连接到事件?