在手写视图中输入文本

注释

Windows 应用 SDK 中 WinUI 中的文本控件不支持手写视图。 本文仅适用于 UWP 应用。

使用笔点击时,文本框将展开

自定义内置于 UWP 文本输入控件(例如 TextBoxRichEditBoxAutoSuggestBox)中的手写视图(对于墨迹转文本输入)。

概述

当用户使用笔点击文本输入框时,UWP 文本输入控件支持使用 Windows Ink 的笔输入,从而转换为手写图面。

当用户在手写区域的任意位置书写时,文本会被识别,候选窗口会显示识别结果。 用户可以点击结果以选择它,或继续写作以接受建议的候选者。 文本(逐字母)识别结果包含在候选窗口中,因此识别不限于字典中的单词。 当用户写入时,接受的文本输入将转换为保留自然书写感觉的脚本字体。

注释

手写视图默认处于启用状态,但你可以按控件禁用它,并改为还原到文本输入面板。

带有墨迹和建议的文本框

用户可通过标准手势和操作来编辑他们的文本。

  • 划线删除划掉 - 画线以划掉单词或单词的一部分
  • 联接 - 在单词之间绘制弧线以删除它们之间的空格
  • insert - 绘制插入符号以插入空格
  • overwrite - 重写现有文本以替换它

带有墨迹更正的文本框

禁用手写视图

默认情况下,内置手写视图处于启用状态。

如果已在应用程序中提供等效的墨迹转文本功能,或者文本输入体验依赖于某种格式或特殊字符(如选项卡),则可能需要禁用手写视图。

在此示例中,我们通过将 TextBox 控件的 IsHandwritingViewEnabled 属性设置为 false 来禁用手写视图。 支持手写视图的所有文本控件都支持类似的属性。 ​

<TextBox Name="SampleTextBox"​
    Height="50" Width="500" ​
    FontSize="36" FontFamily="Segoe UI" ​
    PlaceholderText="Try taping with your pen" ​
    IsHandwritingViewEnabled="False">​
</TextBox>​

指定手写视图的对齐方式

手写视图位于基础文本控件上方,大小以适应用户手写的偏好(请参阅设置 - 蓝牙和设备 - 笔和 Windows 墨迹 - 手写 - 字号)。 视图也会自动与文本控件及其在应用中的位置对齐。

应用程序 UI 不会重新布局以容纳更大的控件,这可能会遮挡重要的界面元素。

以下代码片段演示如何使用 TextBoxHandwritingViewPlacementAlignment 属性来指定基础文本控件上的哪个定位点用于对齐手写视图。 ​

<TextBox Name="SampleTextBox"​
    Height="50" Width="500" ​
    FontSize="36" FontFamily="Segoe UI" ​
    PlaceholderText="Try taping with your pen">​
        <TextBox.HandwritingView>​
            <HandwritingView PlacementAlignment="TopLeft"/>​
        </TextBox.HandwritingView>​
</TextBox>​

禁用自动补全候选推荐项

默认情况下,文本建议弹出窗口处于启用状态。 它提供一个顶级墨迹识别结果的列表,供用户在主要识别结果不正确时进行选择。

如果应用程序已提供可靠的自定义识别功能,则可以使用 AreCandidatesEnabled 属性禁用内置建议,如以下示例所示。

<TextBox Name="SampleTextBox"​
    Height="50" Width="500" ​
    FontSize="36" FontFamily="Segoe UI" ​
    PlaceholderText="Try taping with your pen">​
        <TextBox.HandwritingView>​
            <HandwritingView AreCandidatesEnabled="False"/>​
        </TextBox.HandwritingView>​
</TextBox>

使用手写字体首选项

用户可以从基于手写的字体的预定义集合中进行选择,以便基于墨迹识别呈现文本时使用(请参阅“设置” - 蓝牙和设备 ->> 笔和 Windows 墨迹 -> 手写 -> 字体)。

你的应用可以访问此设置,并使用文本控件中已识别文本的所选字体。

在此示例中,我们侦听 TextBoxTextChanged 事件,如果文本更改源自 HandwritingView(或默认字体),则应用用户的所选字体(如果不是)。 ​

private void SampleTextBox_TextChanged(object sender, TextChangedEventArgs e)​
{​
    ((TextBox)sender).FontFamily = 
        ((TextBox)sender).HandwritingView.IsOpen ?
            new FontFamily(PenAndInkSettings.GetDefault().FontFamilyName) : 
            new FontFamily("Segoe UI");​
}​

访问复合控件中的 HandwritingView

使用 TextBoxRichEditBox 控件(如 AutoSuggestBox)的复合控件也支持 HandwritingView

若要访问复合控件中的 HandwritingView ,请使用 VisualTreeHelper API。

以下 XAML 代码片段显示 AutoSuggestBox 控件。

<AutoSuggestBox Name="SampleAutoSuggestBox"​ 
    Height="50" Width="500"​ 
    PlaceholderText="Auto Suggest Example"​ 
    FontSize="16" FontFamily="Segoe UI" ​ 
    Loaded="SampleAutoSuggestBox_Loaded">​
</AutoSuggestBox>​

在相应的代码隐藏文件中,我们演示如何在 AutoSuggestBox 上禁用 HandwritingView

  1. 首先,处理元素的 Loaded 事件并调用函数 FindInnerTextBox 来启动可视化树遍历。

    private void SampleAutoSuggestBox_Loaded(object sender, RoutedEventArgs e)​
    {​
        if (FindInnerTextBox((AutoSuggestBox)sender))​
            autoSuggestInnerTextBox.IsHandwritingViewEnabled = false;​
    }​
    
  2. FindInnerTextBox 函数中,我们通过调用函数 FindVisualChildByName 循环访问可视化树(从 AutoSuggestBox 开始)。

    private bool FindInnerTextBox(AutoSuggestBox autoSuggestBox)​
    {​
        if (autoSuggestInnerTextBox == null)​
        {​
            // Cache textbox to avoid multiple tree traversals. ​
            autoSuggestInnerTextBox = 
                (TextBox)FindVisualChildByName<TextBox>(autoSuggestBox);​
        }​
        return (autoSuggestInnerTextBox != null);​
    }​
    ​```
    
    
  3. 最后,函数 FindVisualChildByName 循环访问可视化树,直到检索 TextBox

    private FrameworkElement FindVisualChildByName<T>(DependencyObject obj)​
    {​
        FrameworkElement element = null;​
        int childrenCount = 
            VisualTreeHelper.GetChildrenCount(obj);​
        for (int i = 0; (i < childrenCount) && (element == null); i++)​
        {​
            FrameworkElement child = 
                (FrameworkElement)VisualTreeHelper.GetChild(obj, i);​
            if ((child.GetType()).Equals(typeof(T)) || (child.GetType().GetTypeInfo().IsSubclassOf(typeof(T))))​
            {​
                element = child;​
            }​
            else​
            {​
                element = FindVisualChildByName<T>(child);​
            }​
        }​
        return (element);​
    }​
    ​```
    
    

重新定位 HandwritingView

在某些情况下,可能需要确保 HandwritingView 涵盖它否则可能没有的 UI 元素。

在这里,我们将创建一个支持听写的 TextBox(通过将 TextBox 和听写按钮放入 StackPanel 来实现)。

支持听写的文本框的屏幕截图

由于 StackPanel 现在大于 TextBox,因此 HandwritingView 可能无法完全覆盖整个组合控件。

部分遮挡 TextBox 的 HandwritingView 控件的屏幕截图,以及重新定位为完全遮挡 TextBox 的控件的屏幕截图

若要解决此问题,请将 HandwritingView 的 PlacementTarget 属性设置为它应与之对齐的 UI 元素。

<StackPanel Name="DictationBox" 
    Orientation="Horizontal" ​
    VerticalAlignment="Top" 
    HorizontalAlignment="Left" ​
    BorderThickness="1" BorderBrush="DarkGray" ​
    Height="55" Width="500" Margin="50">​
    <TextBox Name="DictationTextBox" 
        Width="450" BorderThickness="0" ​
        FontSize="24" VerticalAlignment="Center">​
        <TextBox.HandwritingView>​
            <HandwritingView PlacementTarget="{Binding ElementName=DictationBox}"/>​
        </TextBox.HandwritingView>​
    </TextBox>​
    <Button Name="DictationButton" 
        Height="48" Width="48" 
        FontSize="24" ​
        FontFamily="Segoe MDL2 Assets" 
        Content="&#xE720;" ​
        Background="White" Foreground="DarkGray" ​    Tapped="DictationButton_Tapped" />​
</StackPanel>​

调整手写视图的大小

还可以设置 HandwritingView 的大小,这在需要确保视图不遮挡重要 UI 时非常有用。

与前面的示例一样,我们创建一个支持听写的 TextBox(通过将 TextBox 和听写按钮放入 StackPanel 来实现)。

支持听写的 TextBox 的屏幕截图

在这种情况下,我们调整 HandwritingView 的尺寸,以确保听写按钮能够被看到。

包含遮挡听写按钮的 HandwritingView 控件的屏幕截图,以及调整大小后确保听写按钮可见的屏幕截图

为此,我们将 HandwritingView 的 MaxWidth 属性绑定到它应遮挡的 UI 元素的宽度。

<StackPanel Name="DictationBox" 
    Orientation="Horizontal" ​
    VerticalAlignment="Top" 
    HorizontalAlignment="Left" ​
    BorderThickness="1" 
    BorderBrush="DarkGray" ​
    Height="55" Width="500" 
    Margin="50">​
    <TextBox Name="DictationTextBox" 
        Width="450" 
        BorderThickness="0" ​
        FontSize="24" 
        VerticalAlignment="Center">​
        <TextBox.HandwritingView>​
            <HandwritingView 
                PlacementTarget="{Binding ElementName=DictationBox}"​
                MaxWidth="{Binding ElementName=DictationTextBox, Path=Width"/>​
        </TextBox.HandwritingView>​
    </TextBox>​
    <Button Name="DictationButton" 
        Height="48" Width="48" 
        FontSize="24" ​
        FontFamily="Segoe MDL2 Assets" 
        Content="&#xE720;" ​
        Background="White" Foreground="DarkGray" ​
        Tapped="DictationButton_Tapped" />​
</StackPanel>​

重新定位自定义 UI

如果你的自定义 UI 在响应文本输入(如信息性弹出窗口)时出现,则可能需要重新定位该 UI,这样它就不会遮挡手写视图。

具有自定义 UI 的 TextBox

以下示例演示如何侦听 HandwritingViewOpenedClosedSizeChanged 事件以设置 Popup 的位置。

private void Search_HandwritingViewOpened(
    HandwritingView sender, HandwritingPanelOpenedEventArgs args)​
{​
    UpdatePopupPositionForHandwritingView();​
}​
​
private void Search_HandwritingViewClosed(
    HandwritingView sender, HandwritingPanelClosedEventArgs args)​
{​
    UpdatePopupPositionForHandwritingView();​
}​
​
private void Search_HandwritingViewSizeChanged(
    object sender, SizeChangedEventArgs e)​
{​
    UpdatePopupPositionForHandwritingView();​
}​
​
private void UpdatePopupPositionForHandwritingView()​
{​
if (CustomSuggestionUI.IsOpen)​
    CustomSuggestionUI.VerticalOffset = GetPopupVerticalOffset();​
}​
​
private double GetPopupVerticalOffset()​
{​
    if (SearchTextBox.HandwritingView.IsOpen)​
        return (SearchTextBox.Margin.Top + SearchTextBox.HandwritingView.ActualHeight);​
    else​
        return (SearchTextBox.Margin.Top + SearchTextBox.ActualHeight);​    ​
}​

重新模板化 HandwritingView 控件

与所有 XAML 框架控件一样,可以根据特定要求自定义 HandwritingView 的视觉结构和视觉行为。

若要查看创建自定义模板的完整示例,请查看 “创建自定义传输控件作说明”或 “自定义编辑控件”示例。 ​ ​ ​ ​ ​ ​ ​ ​