自定义文本输入

Windows.UI.Text.Core 命名空间中的核心文本 API 使 Windows 应用能够接收 Windows 设备上支持的任何文本服务的文本输入。 这些 API 类似于 文本服务框架 API,该应用不需要详细了解文本服务。 这使应用能够接收任何语言和任何输入类型(如键盘、语音或笔)的文本。

重要 APIWindows.UI.Text.CoreCoreTextEditContext

为何使用核心文本 API?

对于许多应用,XAML 或 HTML 文本框控件足以用于文本输入和编辑。 但是,如果应用处理复杂的文本方案(如字处理应用),则可能需要自定义文本编辑控件的灵活性。 可以使用 CoreWindow 键盘 API 创建文本编辑控件,但这些 API 不提供接收基于合成的文本输入的方法,这是支持东亚语言所必需的。

需要创建自定义文本编辑控件时,请改用 Windows.UI.Text.Core API。 这些 API 旨在让你灵活地处理文本输入(采用任何语言)并提供最适合应用的文本体验。 使用核心文本 API 构建的文本输入和编辑控件可以从 Windows 设备上的所有现有文本输入法接收文本输入,包括由文本服务框架 支持的输入法编辑器(IME)和电脑上的手写输入,以及在移动设备上通过 WordFlow 键盘提供的自动更正、预测和听写功能。

建筑

下面是文本输入系统的简单表示形式。

  • “应用程序”表示托管使用核心文本 API 生成的自定义编辑控件的 Windows 应用。
  • Windows.UI.Text.Core API 有助于通过 Windows 与文本服务进行通信。 文本编辑控件与文本服务之间的通信主要通过 CoreTextEditContext 对象进行处理,该对象提供用于促进通信的方法和事件。

CoreText 体系结构关系图

文本范围与选择

编辑控件为文本输入提供空间,用户希望在此空间中的任何位置编辑文本。 在这里,我们将介绍核心文本 API 使用的文本定位系统,以及如何在此系统中表示范围和选择。

应用插入点位置

使用核心文本 API 的文本范围以光标位置表示。 “Application Caret Position (ACP)”是一个从零开始的数字,用于表示从文本流开头到插入符号位置之间的字符数,如下所示。

显示应用光标位置(ACP)字符计数的屏幕截图

文本范围与选择

文本范围和选择由 CoreTextRange 结构表示,其中包含两个字段:

领域 数据类型 DESCRIPTION
StartCaretPosition 数字 [JavaScript] |System.Int32 [.NET] |int32 [C++] 范围的起始位置是第一个字符前的 ACP。
EndCaretPosition 数字 [JavaScript] |System.Int32 [.NET] |int32 [C++] 范围的结束位置是紧接在最后一个字符之后的 ACP。

 

例如,在前面显示的文本区域中,区域 [0, 5] 指定单词“Hello”。 StartCaretPosition 必须始终小于或等于 EndCaretPosition。 范围 [5, 0] 无效。

插入点

当前插入点的位置(常被称为插入点)表示为将 StartCaretPosition 设置为等于 EndCaretPosition

非连续选择

某些编辑控件支持非连续选择。 例如,Microsoft Office 应用支持多个任意选择,许多源代码编辑器支持列选择。 但是,核心文本 API 不支持非连续选择。 编辑控件必须仅报告单个连续选择,通常是非连续选择的活动子范围。

例如,下图显示了一个文本流,其中包含两个非连续选择:[0,1] 和 [6, 11],编辑控件必须仅报告一个([0,1] 或 [6, 11])。

显示非连续文本选择的屏幕截图,其中选择了第一个字符和最后五个字符。

处理文本

CoreTextEditContext 类通过 TextUpdating 事件、TextRequested 事件以及 NotifyTextChanged 方法,实现了 Windows 和编辑控件之间的文本流通。

编辑控件通过 TextUpdating 事件接收文本,这些事件在用户使用键盘、语音输入或输入法等文本输入方法与系统交互时生成。

更改编辑控件中的文本(例如,通过将文本粘贴到控件中)时,需要通过调用 NotifyTextChanged 通知 Windows。

如果文本服务需要新文本,则会引发 TextRequested 事件。 您必须在 TextRequested 事件处理程序中提供新的文本。

接受文本更新

编辑控件通常应接受文本更新请求,因为它们表示用户想要输入的文本。 在 TextUpdating 事件处理程序中,你的编辑控件需要执行以下操作:

  1. CoreTextTextUpdatingEventArgs.Text 中指定的文本插入到 CoreTextTextUpdatingEventArgs.Range中指定的位置。
  2. 将所选内容置于 CoreTextTextUpdatingEventArgs.NewSelection中指定的位置。
  3. 通过将 CoreTextTextUpdatingEventArgs.Result 设置为 CoreTextTextUpdatingResult.Succeeded,通知系统更新成功。

例如,这是用户键入“d”之前编辑控件的状态。 插入点位于 [10, 10]。

插入 之前显示插入点 [10, 10] 的文本流图的屏幕截图

当用户键入“d”时,将引发一个 TextUpdating 事件,并附带以下 CoreTextTextUpdatingEventArgs 数据:

在编辑控件中,应用指定的更改并将 结果 设置为 成功。 下面是应用更改后控件的状态。

插入 后显示插入点为 \[11, 11\]的文本流图的屏幕截图

拒绝文本更新

有时,无法应用文本更新,因为请求的范围位于不应更改的编辑控件区域中。 在这种情况下,不应应用任何更改。 相反,通知系统更新失败,方法是将 CoreTextTextUpdatingEventArgs.Result 设置为 CoreTextTextUpdatingResult.Failed

例如,请考虑仅接受电子邮件地址的编辑控件。 应拒绝空格,因为电子邮件地址不能包含空格,因此,当为空格键引发 TextUpdating 事件时,只需在编辑控件中将 结果 设置为 失败

通知文本更改

有时,编辑控件会在文本粘贴或自动更正时更改文本。 在这些情况下,必须通过调用 NotifyTextChanged 方法通知文本服务这些更改。

例如,这是用户粘贴“World”之前编辑控件的状态。 插入点位于 [6,6]。

插入 之前显示插入点为 [6, 6] 的文本流图的屏幕截图

应用更改后,用户执行粘贴操作和编辑控件:

插入 后显示插入点为 \[11, 11\]的文本流图的屏幕截图

发生这种情况时,应使用以下参数调用 NotifyTextChanged

  • modifiedRange = [6, 6]
  • 新长度 = 5
  • newSelection = [11, 11]

一个或多个 TextRequested 事件会发生,作为响应,您需要处理这些事件,以更新文本服务正在使用的文本。

覆盖文本更新

在您的编辑控件中,您可能想要覆盖文本更新,以提供自动更正功能。

例如,请考虑一个编辑控件,该控件提供将缩写词语规范化的更正功能。 这是用户键入用于触发更正的空间键之前编辑控件的状态。 插入点位于 [3, 3]。

文本流图的截图,显示插入点位于【3,3】,在插入 之前。

用户按下空格键并引发相应的 TextUpdating 事件。 编辑控件接受文本的更新。 这是编辑控件在完成更正前的短暂状态。 插入点位于 [4,4]。

显示插入点在 [4, 4] 的文本流图的屏幕截图,在插入

文本更新 事件处理程序之外,编辑控件将进行以下更正。 这是更正完成后编辑控件的状态。 插入点位于 [5, 5]。

显示插入点 [5, 5] 的文本流图的屏幕截图

发生这种情况时,应使用以下参数调用 NotifyTextChanged

  • modifiedRange = [1, 2]
  • 新长度 = 2
  • newSelection = [5, 5]

一个或多个 TextRequested 事件会发生,作为响应,您需要处理这些事件,以更新文本服务正在使用的文本。

提供所请求的文本

文本处理服务必须拥有正确的文本,以提供诸如自动更正或预测等功能,特别是对于那些已经存在于编辑控件中的文本,例如来自加载文档的文本,或者是如前述章节中所解释的由编辑控件插入的文本。 因此,每当引发 TextRequested 事件时,都必须为指定范围提供当前编辑控件中的文本。

在某些情况下,CoreTextTextRequest 中的 范围 可能会指定一个您的编辑控件无法容纳的范围 as-is。 例如,RangeTextRequested 事件时比编辑控件的大小大,或者 Range 的末尾超出边界。 在这些情况下,应返回任何有意义的范围,这通常是所请求范围的子集。

示例

档案样本