UWP 应用中的笔交互和 Windows InkPen interactions and Windows Ink in UWP apps

Surface PenSurface Pen
Surface 触控笔(可通过 Microsoft 官方商城购买)。Surface Pen (available for purchase at the Microsoft Store).

概述Overview

针对笔输入优化通用 Windows 平台 (UWP) 应用,以便为用户同时提供标准的指针设备功能和最佳的 Windows Ink 体验。Optimize your Universal Windows Platform (UWP) app for pen input to provide both standard pointer device functionality and the best Windows Ink experience for your users.

备注

本主题重点介绍 Windows Ink 平台。This topic focuses on the Windows Ink platform. 对于常规指针输入处理(类似于鼠标、触摸和触摸板),请参阅处理指针输入For general pointer input handling (similar to mouse, touch, and touchpad), see Handle pointer input.

视频Videos
在 UWP 应用中使用墨迹Using ink in your UWP app 使用 Windows 笔和墨迹生成更具吸引力的企业应用程序Use Windows Pen and Ink to build more engaging enterprise apps

Windows Ink 平台与笔设备一起提供了一种创建数字手写便笺、绘图和批注的自然方法。The Windows Ink platform, together with a pen device, provides a natural way to create digital handwritten notes, drawings, and annotations. 该平台支持将数字化器输入捕获为墨迹数据、生成墨迹数据、管理墨迹数据、在输出设备上以笔划墨迹形式呈现墨迹数据以及通过手写识别将墨迹转换为文本。The platform supports capturing digitizer input as ink data, generating ink data, managing ink data, rendering ink data as ink strokes on the output device, and converting ink to text through handwriting recognition.

除了捕获用户在书写或绘图时笔的基本位置和移动外,你的应用还可以跟踪和收集笔划前后使用的不同程度的压力。In addition to capturing the basic position and movement of the pen as the user writes or draws, your app can also track and collect the varying amounts of pressure used throughout a stroke. 此信息连同设置(针对笔尖形状、大小和旋转)、墨迹颜色以及用途(普通墨迹、擦除、突出显示和选择)一起支持你提供非常接近于在纸上使用钢笔、铅笔或画笔书写或绘图的用户体验。This information, along with settings for pen tip shape, size, and rotation, ink color, and purpose (plain ink, erasing, highlighting, and selecting), enables you to provide user experiences that closely resemble writing or drawing on paper with a pen, pencil, or brush.

备注

你的应用还支持其他基于指针的设备的输入,包括触摸数字化器和鼠标设备。Your app can also support ink input from other pointer-based devices, including touch digitizers and mouse devices. 

墨迹平台具有很高的灵活性。The ink platform is very flexible. 它旨在根据不同要求支持各类功能级别。It is designed to support various levels of functionality, depending on your requirements.

有关 Windows Ink 用户体验指南,请参阅墨迹书写控件For Windows Ink UX guidelines, see Inking controls.

Windows Ink 平台组件Components of the Windows Ink platform

组件Component 说明Description
InkCanvasInkCanvas 默认情况下,一个 XAML UI 平台控件接收并显示从笔到墨迹笔划或擦除笔划的所有输入。A XAML UI platform control that, by default, receives and displays all input from a pen as either an ink stroke or an erase stroke.
有关如何使用 InkCanvas 的详细信息,请参阅将 Windows Ink 笔划识别为文本存储和检索 Windows Ink 笔划数据For more information about how to use the InkCanvas, see Recognize Windows Ink strokes as text and Store and retrieve Windows Ink stroke data.
InkPresenterInkPresenter 代码隐藏对象,与 InkCanvas 控件(通过 InkCanvas.InkPresenter 属性公开)一起进行实例化。A code-behind object, instantiated along with an InkCanvas control (exposed through the InkCanvas.InkPresenter property). 此对象提供 InkCanvas 公开的所有默认墨迹书写功能以及适用于其他自定义和个性化的完整 API 集。This object provides all default inking functionality exposed by the InkCanvas, along with a comprehensive set of APIs for additional customization and personalization.
有关如何使用 InkPresenter 的详细信息,请参阅将 Windows Ink 笔划识别为文本存储和检索 Windows Ink 笔划数据For more information about how to use the InkPresenter, see Recognize Windows Ink strokes as text and Store and retrieve Windows Ink stroke data.
InkToolbarInkToolbar 一个 XAML UI 平台控件,其中包含可自定义且可扩展的按钮集合,用于激活关联InkCanvas中与墨迹相关的功能。A XAML UI platform control containing a customizable and extensible collection of buttons that activate ink-related features in an associated InkCanvas.
有关如何使用 InkToolbar 的详细信息,请参阅将 InkToolbar 添加到通用 Windows 平台 (UWP) 墨迹书写应用For more information about how to use the InkToolbar, see Add an InkToolbar to a Universal Windows Platform (UWP) inking app.
IInkD2DRendererIInkD2DRenderer 支持将笔划墨迹呈现到通用 Windows 应用的指定 Direct2D 设备上下文,而非默认的 InkCanvas 控件。Enables the rendering of ink strokes onto the designated Direct2D device context of a Universal Windows app, instead of the default InkCanvas control. 这支持完全自定义墨迹书写体验。This enables full customization of the inking experience.
有关详细信息,请参阅复杂墨迹示例For more information, see the Complex ink sample.

通过 InkCanvas 实现基本墨迹书写Basic inking with InkCanvas

若要添加基本的墨迹书写功能,只需将 InkCanvas UWP 平台控件放在应用的相应页面上。To add basic inking functionality, just place an InkCanvas UWP platform control on the appropriate page in your app.

默认情况下,InkCanvas 仅支持来自笔的墨迹输入。By default, the InkCanvas supports ink input only from a pen. 该输入通过颜色和粗细的默认设置呈现为笔划墨迹(粗细为 2 个像素的黑色圆珠笔),或视为笔划橡皮擦(当输入来源于橡皮擦尖或使用擦除按钮修改的笔尖时)。The input is either rendered as an ink stroke using default settings for color and thickness (a black ballpoint pen with a thickness of 2 pixels), or treated as a stroke eraser (when input is from an eraser tip or the pen tip modified with an erase button).

备注

如果橡皮擦尖或按钮不存在,则 InkCanvas 可配置为将来自笔尖的输入作为擦除笔划处理。If an eraser tip or button is not present, the InkCanvas can be configured to process input from the pen tip as an erase stroke.

在本例中,InkCanvas 覆盖了背景图。In this example, an InkCanvas overlays a background image.

备注

InkCanvas 的默认高度宽度属性均为零,除非它是自动调整其子元素的元素(如system.windows.controls.stackpanel>Grid控件)的子元素。An InkCanvas has default Height and Width properties of zero, unless it is the child of an element that automatically sizes its child elements, such as StackPanel or Grid controls.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
        <TextBlock x:Name="Header"
                   Text="Basic ink sample"
                   Style="{ThemeResource HeaderTextBlockStyle}"
                   Margin="10,0,0,0" />            
    </StackPanel>
    <Grid Grid.Row="1">
        <Image Source="Assets\StoreLogo.png" />
        <InkCanvas x:Name="inkCanvas" />
    </Grid>
</Grid>

该系列图显示 InkCanvas 控件如何呈现笔输入。This series of images shows how pen input is rendered by this InkCanvas control.

带有背景图的空白 InkCanvas 带有墨迹笔划的 InkCanvas 擦除了一条笔划的 InkCanvas
带有背景图的空白 InkCanvasThe blank InkCanvas with a background image. 带有笔划墨迹的 InkCanvasThe InkCanvas with ink strokes. 擦除了一条笔划的 InkCanvas(注意如何在整条笔划而非某个部分上执行擦除)。The InkCanvas with one stroke erased (note how erase operates on an entire stroke, not a portion).

InkCanvas 控件支持的墨迹书写功能由名为 InkPresenter 的代码隐藏对象提供。The inking functionality supported by the InkCanvas control is provided by a code-behind object called the InkPresenter.

对于基本墨迹书写,你不必考虑 InkPresenterFor basic inking, you don't have to concern yourself with the InkPresenter. 但是,若要在 InkCanvas 上自定义和配置墨迹书写行为,则必须访问其相应的 InkPresenter 对象。However, to customize and configure inking behavior on the InkCanvas, you must access its corresponding InkPresenter object.

通过 InkPresenter 实现基本自定义Basic customization with InkPresenter

InkPresenter 对象通过每个 InkCanvas 控件进行实例化。An InkPresenter object is instantiated with each InkCanvas control.

备注

InkPresenter 不能直接进行实例化。The InkPresenter cannot be instantiated directly. 而是通过 InkCanvasInkPresenter 属性进行访问。Instead, it is accessed through the InkPresenter property of the InkCanvas. 

InkPresenter 不仅提供了其相应 InkCanvas 控件的所有默认墨迹书写行为,它还提供了完整 API 集用于对笔输入(标准和修改后)进行其他笔划自定义和精细管理。Along with providing all default inking behaviors of its corresponding InkCanvas control, the InkPresenter provides a comprehensive set of APIs for additional stroke customization and finer-grained management of the pen input (standard and modified). 这包括笔划属性、支持的输入设备类型以及输入是由对象进行处理还是传递到应用进行处理。This includes stroke properties, supported input device types, and whether input is processed by the object or passed to the app for processing.

备注

标准墨迹输入(从笔尖或橡皮擦尖/按钮)不使用辅助硬件提供功能修改,如笔桶按钮、鼠标右键按钮或类似机制。Standard ink input (from either pen tip or eraser tip/button) is not modified with a secondary hardware affordance, such as a pen barrel button, right mouse button, or similar mechanism.

默认情况下,墨迹仅支持使用触控笔输入。By default, ink is supported for pen input only. 我们在此处将 InkPresenter 配置为将来自笔和鼠标的输入数据解释为墨迹笔划。Here, we configure the InkPresenter to interpret input data from both pen and mouse as ink strokes. 我们还将设置一些用于将笔划呈现到 InkCanvas 的初始墨迹笔划属性。We also set some initial ink stroke attributes used for rendering strokes to the InkCanvas.

若要启用鼠标和触摸墨迹书写,将 InkPresenterInputDeviceTypes 属性设置为你需要的 CoreInputDeviceTypes 值的组合。To enable mouse and touch inking, set the InputDeviceTypes property of the InkPresenter to the combination of CoreInputDeviceTypes values that you want.

public MainPage()
{
    this.InitializeComponent();

    // Set supported inking device types.
    inkCanvas.InkPresenter.InputDeviceTypes =
        Windows.UI.Core.CoreInputDeviceTypes.Mouse |
        Windows.UI.Core.CoreInputDeviceTypes.Pen;

    // Set initial ink stroke attributes.
    InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
    drawingAttributes.Color = Windows.UI.Colors.Black;
    drawingAttributes.IgnorePressure = false;
    drawingAttributes.FitToCurve = true;
    inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
}

可以动态设置墨迹笔划属性以适应用户首选项或应用要求。Ink stroke attributes can be set dynamically to accommodate user preferences or app requirements.

此处,我们让用户从墨迹颜色列表中进行选择。Here, we let a user choose from a list of ink colors.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
        <TextBlock x:Name="Header"
                   Text="Basic ink customization sample"
                   VerticalAlignment="Center"
                   Style="{ThemeResource HeaderTextBlockStyle}"
                   Margin="10,0,0,0" />
        <TextBlock Text="Color:"
                   Style="{StaticResource SubheaderTextBlockStyle}"
                   VerticalAlignment="Center"
                   Margin="50,0,10,0"/>
        <ComboBox x:Name="PenColor"
                  VerticalAlignment="Center"
                  SelectedIndex="0"
                  SelectionChanged="OnPenColorChanged">
            <ComboBoxItem Content="Black"/>
            <ComboBoxItem Content="Red"/>
        </ComboBox>
    </StackPanel>
    <Grid Grid.Row="1">
        <Image Source="Assets\StoreLogo.png" />
        <InkCanvas x:Name="inkCanvas" />
    </Grid>
</Grid>

然后处理对所选颜色的更改并相应地更新墨迹笔划属性。We then handle changes to the selected color and update the ink stroke attributes accordingly.

// Update ink stroke color for new strokes.
private void OnPenColorChanged(object sender, SelectionChangedEventArgs e)
{
    if (inkCanvas != null)
    {
        InkDrawingAttributes drawingAttributes =
            inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();

        string value = ((ComboBoxItem)PenColor.SelectedItem).Content.ToString();

        switch (value)
        {
            case "Black":
                drawingAttributes.Color = Windows.UI.Colors.Black;
                break;
            case "Red":
                drawingAttributes.Color = Windows.UI.Colors.Red;
                break;
            default:
                drawingAttributes.Color = Windows.UI.Colors.Black;
                break;
        };

        inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
    }
}

这些图像显示了 InkPresenter 如何处理和自定义笔输入。These images shows how pen input is processed and customized by the InkPresenter.

具有默认的黑色笔划墨迹的 inkcanvas 具有用户选择的红色笔划墨迹的 inkcanvas
具有默认的黑色笔划墨迹的 InkCanvasThe InkCanvas with default black ink strokes. 具有用户选择的红色墨迹笔划的 InkCanvasThe InkCanvas with user selected red ink strokes.  

若要提供墨迹书写和擦除之外的功能(例如笔划选择),你的应用必须为 InkPresenter 标识特定输入,从而在未处理的情况下进行传递以供应用处理。To provide functionality beyond inking and erasing, such as stroke selection, your app must identify specific input for the InkPresenter to pass through unprocessed for handling by your app.

传递输入以进行高级处理Pass-through input for advanced processing

默认情况下,InkPresenter 将所有输入作为笔划墨迹或擦除笔划进行处理,包括由辅助硬件(例如笔桶按钮、鼠标右键按钮或类似硬件)修改的输入。By default, InkPresenter processes all input as either an ink stroke or an erase stroke, including input modified by a secondary hardware affordance such as a pen barrel button, a right mouse button, or similar. 但是,用户通常期望通过这些辅助提供获得一些其他功能或修改的行为。However, users typically expect some additional functionality or modified behavior with these secondary affordances.

在某些情况下,你可能还需要公开未使用辅助提供(通常不会与笔尖关联的功能)时笔的其他功能、其他输入设备类型或某种类型的修改行为,具体取决于用户在应用 UI 中的选择。In some cases, you might also need to expose additional functionality for pens without secondary affordances (functionality not usually associated with the pen tip), other input device types, or some type of modified behavior based on a user selection in your app's UI.

若要支持此要求,可将 InkPresenter 配置为保留特定输入的未处理状态。To support this, InkPresenter can be configured to leave specific input unprocessed. 这个未处理的输入随后将传递到你的应用以进行处理。This unprocessed input is then passed through to your app for processing.

示例 - 使用未处理的输入实现笔划选择Example - Use unprocessed input to implement stroke selection

Windows Ink 平台未为需要修改输入的操作(例如笔划选择)提供内置支持。The Windows Ink platform does not provide built-in support for actions that require modified input, such as stroke selection. 若要支持这类功能,必须在应用中提供自定义解决方案。To support features like this, you must provide a custom solution in your apps.

以下代码示例(所有代码都在 MainPage.xaml 和 MainPage.xaml.cs 文件中)分步演示了通过笔桶按钮(或鼠标右键按钮)修改输入时,如何启用笔划选择。The following code example (all code is in the MainPage.xaml and MainPage.xaml.cs files) steps through how to enable stroke selection when input is modified with a pen barrel button (or right mouse button).

  1. 首先,我们在 MainPage.xaml 中设置 UI。First, we set up the UI in MainPage.xaml.

    我们在此处添加一个用于绘制选择笔划的画布(在 InkCanvas 下面)。Here, we add a canvas (below the InkCanvas) to draw the selection stroke. 使用单独的图层绘制选择笔划可使 InkCanvas 及其内容不会受到影响。Using a separate layer to draw the selection stroke leaves the InkCanvas and its content untouched.

    带有基础选择画布的空白 inkcanvas

      <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
          <TextBlock x:Name="Header"
            Text="Advanced ink customization sample"
            VerticalAlignment="Center"
            Style="{ThemeResource HeaderTextBlockStyle}"
            Margin="10,0,0,0" />
        </StackPanel>
        <Grid Grid.Row="1">
          <!-- Canvas for displaying selection UI. -->
          <Canvas x:Name="selectionCanvas"/>
          <!-- Inking area -->
          <InkCanvas x:Name="inkCanvas"/>
        </Grid>
      </Grid>
    
  2. 在 MainPage.xaml.cs 中,我们声明了几个全局变量来保持对选择 UI 各个方面的引用。In MainPage.xaml.cs, we declare a couple of global variables for keeping references to aspects of the selection UI. 具体内容有选择套索笔划和突出显示所选笔划的边界矩形。Specifically, the selection lasso stroke and the bounding rectangle that highlights the selected strokes.

      // Stroke selection tool.
      private Polyline lasso;
      // Stroke selection area.
      private Rect boundingRect;
    
  3. 接下来,我们配置 InkPresenter 以将来自笔和鼠标的输入数据解释为墨迹笔划,然后设置一些用于将笔划呈现到 InkCanvas 的初始墨迹笔划属性。Next, we configure the InkPresenter to interpret input data from both pen and mouse as ink strokes, and set some initial ink stroke attributes used for rendering strokes to the InkCanvas.

    最重要的是,我们使用 InkPresenterInputProcessingConfiguration 属性指示任何修改的输入均应由应用进行处理。Most importantly, we use the InputProcessingConfiguration property of the InkPresenter to indicate that any modified input should be processed by the app. 通过向 InputProcessingConfiguration.RightDragAction 分配值 InkInputRightDragAction.LeaveUnprocessed 指定修改的输入。Modified input is specified by assigning InputProcessingConfiguration.RightDragAction a value of InkInputRightDragAction.LeaveUnprocessed. 设置此值后,InkPresenter 会向 InkUnprocessedInput 类传递一组指针事件让你进行处理。When this value is set, the InkPresenter passes through to the InkUnprocessedInput class, a set of pointer events for you to handle.

    我们为由 InkPresenter 传递的未经处理的 PointerPressedPointerMovedPointerReleased 事件分配侦听器。We assign listeners for the unprocessed PointerPressed, PointerMoved, and PointerReleased events passed through by the InkPresenter. 所有选择功能均在这些事件的处理程序中实现。All selection functionality is implemented in the handlers for these events.

    最后,我们为 InkPresenterStrokeStartedStrokesErased 事件分配侦听器。Finally, we assign listeners for the StrokeStarted and StrokesErased events of the InkPresenter. 如果启动了新笔划或擦除了现有笔划,我们将使用这些事件的处理程序清理选择 UI。We use the handlers for these events to clean up the selection UI if a new stroke is started or an existing stroke is erased.

    具有默认的黑色笔划墨迹的 inkcanvas

      public MainPage()
      {
        this.InitializeComponent();
    
        // Set supported inking device types.
        inkCanvas.InkPresenter.InputDeviceTypes =
          Windows.UI.Core.CoreInputDeviceTypes.Mouse |
          Windows.UI.Core.CoreInputDeviceTypes.Pen;
    
        // Set initial ink stroke attributes.
        InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
        drawingAttributes.Color = Windows.UI.Colors.Black;
        drawingAttributes.IgnorePressure = false;
        drawingAttributes.FitToCurve = true;
        inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
    
        // By default, the InkPresenter processes input modified by
        // a secondary affordance (pen barrel button, right mouse
        // button, or similar) as ink.
        // To pass through modified input to the app for custom processing
        // on the app UI thread instead of the background ink thread, set
        // InputProcessingConfiguration.RightDragAction to LeaveUnprocessed.
        inkCanvas.InkPresenter.InputProcessingConfiguration.RightDragAction =
            InkInputRightDragAction.LeaveUnprocessed;
    
        // Listen for unprocessed pointer events from modified input.
        // The input is used to provide selection functionality.
        inkCanvas.InkPresenter.UnprocessedInput.PointerPressed +=
            UnprocessedInput_PointerPressed;
        inkCanvas.InkPresenter.UnprocessedInput.PointerMoved +=
            UnprocessedInput_PointerMoved;
        inkCanvas.InkPresenter.UnprocessedInput.PointerReleased +=
            UnprocessedInput_PointerReleased;
    
        // Listen for new ink or erase strokes to clean up selection UI.
        inkCanvas.InkPresenter.StrokeInput.StrokeStarted +=
            StrokeInput_StrokeStarted;
        inkCanvas.InkPresenter.StrokesErased +=
            InkPresenter_StrokesErased;
      }
    
  4. 然后,我们为由 InkPresenter 传递的未经处理的 PointerPressedPointerMovedPointerReleased 事件定义处理程序。We then define handlers for the unprocessed PointerPressed, PointerMoved, and PointerReleased events passed through by the InkPresenter.

    所有选择功能(包括套索笔划和边界矩形)均在这些处理程序中实现。All selection functionality is implemented in these handlers, including the lasso stroke and the bounding rectangle.

    选择套索

      // Handle unprocessed pointer events from modified input.
      // The input is used to provide selection functionality.
      // Selection UI is drawn on a canvas under the InkCanvas.
      private void UnprocessedInput_PointerPressed(
        InkUnprocessedInput sender, PointerEventArgs args)
      {
        // Initialize a selection lasso.
        lasso = new Polyline()
        {
            Stroke = new SolidColorBrush(Windows.UI.Colors.Blue),
            StrokeThickness = 1,
            StrokeDashArray = new DoubleCollection() { 5, 2 },
            };
    
            lasso.Points.Add(args.CurrentPoint.RawPosition);
    
            selectionCanvas.Children.Add(lasso);
        }
    
        private void UnprocessedInput_PointerMoved(
          InkUnprocessedInput sender, PointerEventArgs args)
        {
          // Add a point to the lasso Polyline object.
          lasso.Points.Add(args.CurrentPoint.RawPosition);
        }
    
        private void UnprocessedInput_PointerReleased(
          InkUnprocessedInput sender, PointerEventArgs args)
        {
          // Add the final point to the Polyline object and
          // select strokes within the lasso area.
          // Draw a bounding box on the selection canvas
          // around the selected ink strokes.
          lasso.Points.Add(args.CurrentPoint.RawPosition);
    
          boundingRect =
            inkCanvas.InkPresenter.StrokeContainer.SelectWithPolyLine(
              lasso.Points);
    
          DrawBoundingRect();
        }
    
  5. 为结束 PointerReleased 事件处理程序,我们将清除所有内容(套索笔划)的选择图层,然后在套索区域包含的墨迹笔划周围绘制一个边界矩形。To conclude the PointerReleased event handler, we clear the selection layer of all content (the lasso stroke) and then draw a single bounding rectangle around the ink strokes encompassed by the lasso area.

    选择边界矩形

      // Draw a bounding rectangle, on the selection canvas, encompassing
      // all ink strokes within the lasso area.
      private void DrawBoundingRect()
      {
        // Clear all existing content from the selection canvas.
        selectionCanvas.Children.Clear();
    
        // Draw a bounding rectangle only if there are ink strokes
        // within the lasso area.
        if (!((boundingRect.Width == 0) ||
          (boundingRect.Height == 0) ||
          boundingRect.IsEmpty))
          {
            var rectangle = new Rectangle()
            {
              Stroke = new SolidColorBrush(Windows.UI.Colors.Blue),
                StrokeThickness = 1,
                StrokeDashArray = new DoubleCollection() { 5, 2 },
                Width = boundingRect.Width,
                Height = boundingRect.Height
            };
    
            Canvas.SetLeft(rectangle, boundingRect.X);
            Canvas.SetTop(rectangle, boundingRect.Y);
    
            selectionCanvas.Children.Add(rectangle);
          }
        }
    
  6. 最后,我们将定义 StrokeStartedStrokesErased InkPresenter 事件的处理程序。Finally, we define handlers for the StrokeStarted and StrokesErased InkPresenter events.

    这二者都可以在检测到新笔划时调用相同的清理功能以清除当前选择。These both just call the same cleanup function to clear the current selection whenever a new stroke is detected.

      // Handle new ink or erase strokes to clean up selection UI.
      private void StrokeInput_StrokeStarted(
        InkStrokeInput sender, Windows.UI.Core.PointerEventArgs args)
      {
        ClearSelection();
      }
    
      private void InkPresenter_StrokesErased(
        InkPresenter sender, InkStrokesErasedEventArgs args)
      {
        ClearSelection();
      }
    
  7. 以下是在启动新笔划或擦除现有笔划时,从选择画布中删除所有选择 UI 的功能。Here's the function to remove all selection UI from the selection canvas when a new stroke is started or an existing stroke is erased.

      // Clean up selection UI.
      private void ClearSelection()
      {
        var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
        foreach (var stroke in strokes)
        {
          stroke.Selected = false;
        }
        ClearDrawnBoundingRect();
       }
    
      private void ClearDrawnBoundingRect()
      {
        if (selectionCanvas.Children.Any())
        {
          selectionCanvas.Children.Clear();
          boundingRect = Rect.Empty;
        }
      }
    

自定义墨迹呈现Custom ink rendering

默认情况下,墨迹输入在低延迟后台线程上进行处理,并在绘制时呈现正在进行或“墨迹未干”。By default, ink input is processed on a low-latency background thread and rendered in-progress, or "wet", as it is drawn. 笔划完成时(抬起笔或手指,或者释放鼠标按钮),笔划将在 UI 线程上进行处理并向 InkCanvas 图层呈现“墨迹已干”(在应用程序内容之上,并且替换未干墨迹)。When the stroke is completed (pen or finger lifted, or mouse button released), the stroke is processed on the UI thread and rendered "dry" to the InkCanvas layer (above the application content and replacing the wet ink).

你可以通过“自定义烘干”未干的笔划墨迹,覆盖这种默认行为并完全控制墨迹书写体验。You can override this default behavior and completely control the inking experience by "custom drying" the wet ink strokes. 虽然默认行为通常足以适用于大多数应用程序,但在少数情况下,可能需要自定义烘干,其中包括:While the default behavior is typically sufficient for most applications, there are a few cases where custom drying might be required, these include:

  • 更高效地管理大型或复杂的笔划墨迹集合More efficient management of large, or complex, collections of ink strokes
  • 在大型墨迹画布上提供更高效的平移和缩放支持More efficient panning and zooming support on large ink canvases
  • 交错墨迹和其他对象(如形状或文本),同时保持 Z 顺序Interleaving ink and other objects, such as shapes or text, while maintaining z-order
  • 烘干墨迹并将其同步转换为 DirectX 形状(例如,将直线或形状光栅化并集成到应用程序内容中,而不是作为单独的 InkCanvas 层)。Drying and converting ink synchronously into a DirectX shape (for example, a straight line or shape rasterized and integrated into application content instead of as a separate InkCanvas layer).

自定义烘干需要 IInkD2DRenderer 对象管理墨迹输入,并将其呈现到通用 Windows 应用的 Direct2D 设备上下文,而非默认 InkCanvas 控件。Custom drying requires an IInkD2DRenderer object to manage the ink input and render it to the Direct2D device context of your Universal Windows app, instead of the default InkCanvas control.

通过调用 ActivateCustomDrying(在加载 InkCanvas 之前),应用创建 InkSynchronizer 对象以自定义如何向 SurfaceImageSourceVirtualSurfaceImageSource 呈现墨迹已干的笔划墨迹。By calling ActivateCustomDrying (before the InkCanvas is loaded), an app creates an InkSynchronizer object to customize how an ink stroke is rendered dry to a SurfaceImageSource or VirtualSurfaceImageSource.

尽管 VSIS 提供比屏幕更大的虚拟图面以实现高性能平移和缩放,但 SurfaceImageSourceVirtualSurfaceImageSource 都为你的应用提供 DirectX 共享图面以便绘制并组合到应用程序的内容中。Both SurfaceImageSource and VirtualSurfaceImageSource provide a DirectX shared surface for your app to draw into and compose into your application's content, although VSIS provides a virtual surface that’s larger than the screen for performant panning and zooming. 由于这些图面的视觉更新与 XAML UI 线程同步,在向其中的任意一个呈现墨迹时,可以同时从 InkCanvas 中删除未干墨迹。Because visual updates to these surfaces are synchronized with the XAML UI thread, when ink is rendered to either, the wet ink can be removed from the InkCanvas simultaneously.

你也可以将干墨迹自定义到 SwapChainPanel,但无法保证与 UI 线程同步,并且向 SwapChainPanel 呈现墨迹的时间与从 InkCanvas 中删除墨迹的时间之间可能出现延迟。You can also custom dry ink to a SwapChainPanel, but synchronization with the UI thread is not guaranteed and there might be a delay between when the ink is rendered to your SwapChainPanel and when ink is removed from the InkCanvas.

有关该功能的完整示例,请参阅复杂墨迹示例For a full example of this functionality, see the Complex ink sample.

备注

自定义烘干和 InkToolbarCustom drying and the InkToolbar
如果你的应用将 InkPresenter 的默认墨迹呈现行为替代为自定义烘干实现,呈现的笔划墨迹不再可用于 InkToolbar,并且 InkToolbar 的内置擦除命令不会按预期工作。If your app overrides the default ink rendering behavior of the InkPresenter with a custom drying implementation, the rendered ink strokes are no longer available to the InkToolbar and the built-in erase commands of the InkToolbar do not work as expected. 若要提供擦除功能,必须处理所有指针事件、在每个笔划上执行命中测试,并替代内置的“清除所有墨迹”命令。To provide erase functionality, you must handle all pointer events, perform hit-testing on each stroke, and override the built-in "Erase all ink" command.

主题Topic 说明Description
识别墨迹笔划Recognize ink strokes 使用手写识别将笔划墨迹转换为文本,或使用自定义识别转换为形状。Convert ink strokes to text using handwriting recognition, or to shapes using custom recognition.
存储和检索墨迹笔划Store and retrieve ink strokes 使用嵌入的墨迹序列化格式 (ISF) 元数据在图形交换格式 (GIF) 文件中存储笔划墨迹数据。Store ink stroke data in a Graphics Interchange Format (GIF) file using embedded Ink Serialized Format (ISF) metadata.
向 UWP 墨迹应用添加 InkToolbarAdd an InkToolbar to a UWP inking app 将默认的 InkToolbar 添加到通用 Windows 平台 (UWP) 墨迹书写应用、将自定义笔按钮添加到 InkToolbar,并将自定义笔按钮绑定到自定义笔定义。Add a default InkToolbar to a Universal Windows Platform (UWP) inking app, add a custom pen button to the InkToolbar, and bind the custom pen button to a custom pen definition.

APIAPIs

示例Samples

存档示例Archive Samples