注释
Windows 应用 SDK 中 WinUI 中的文本控件不支持手写视图。 本文仅适用于 UWP 应用。
自定义内置于 UWP 文本输入控件(例如 TextBox、 RichEditBox 和 AutoSuggestBox)中的手写视图(对于墨迹转文本输入)。
概述
当用户使用笔点击文本输入框时,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>
指定手写视图的对齐方式
手写视图位于基础文本控件上方,大小以适应用户手写的偏好(请参阅
应用程序 UI 不会重新布局以容纳更大的控件,这可能会遮挡重要的界面元素。
以下代码片段演示如何使用 TextBoxHandwritingView 的 PlacementAlignment 属性来指定基础文本控件上的哪个定位点用于对齐手写视图。
<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 墨迹 -> 手写 -> 字体)。
你的应用可以访问此设置,并使用文本控件中已识别文本的所选字体。
在此示例中,我们侦听 TextBox 的 TextChanged 事件,如果文本更改源自 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
使用 TextBox 或 RichEditBox 控件(如 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。
首先,处理元素的 Loaded 事件并调用函数
FindInnerTextBox来启动可视化树遍历。private void SampleAutoSuggestBox_Loaded(object sender, RoutedEventArgs e) { if (FindInnerTextBox((AutoSuggestBox)sender)) autoSuggestInnerTextBox.IsHandwritingViewEnabled = false; }在
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); } ```最后,函数
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 可能无法完全覆盖整个组合控件。
若要解决此问题,请将 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=""
Background="White" Foreground="DarkGray" Tapped="DictationButton_Tapped" />
</StackPanel>
调整手写视图的大小
还可以设置 HandwritingView 的大小,这在需要确保视图不遮挡重要 UI 时非常有用。
与前面的示例一样,我们创建一个支持听写的 TextBox(通过将 TextBox 和听写按钮放入 StackPanel 来实现)。
在这种情况下,我们调整 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=""
Background="White" Foreground="DarkGray"
Tapped="DictationButton_Tapped" />
</StackPanel>
重新定位自定义 UI
如果你的自定义 UI 在响应文本输入(如信息性弹出窗口)时出现,则可能需要重新定位该 UI,这样它就不会遮挡手写视图。
以下示例演示如何侦听 HandwritingView 的 Opened、Closed 和 SizeChanged 事件以设置 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 的视觉结构和视觉行为。
若要查看创建自定义模板的完整示例,请查看 “创建自定义传输控件作说明”或 “自定义编辑控件”示例。