共用方式為


Windows 應用程式中的手寫筆互動和 Windows Ink

Surface 手寫筆的 Hero 影像。
Surface 手寫筆 (可在 Microsoft市購買)。

概觀

將您的 Windows 應用程式優化,使之能兼具標準的 指標裝置 功能,並為使用者提供最佳的 Windows Ink 體驗,適合手寫筆輸入。

備註

本主題著重於 Windows Ink 平臺。 如需一般指標輸入處理(類似於滑鼠、觸控和觸控板),請參閱 處理指標輸入

在 Windows 應用程式中使用筆跡

使用 Windows 手寫筆和 Ink 來建置更具吸引力的企業應用程式

Windows Ink 平臺與手寫筆裝置一起提供自然的方式來建立數位手寫筆記、繪圖和批註。 平臺支援將數位板輸入擷取為筆跡數據、產生筆跡數據、管理筆跡數據、將筆跡數據轉譯為輸出裝置上的筆墨筆劃,以及透過手寫辨識將筆跡轉換成文字。

除了在使用者寫入或繪圖時擷取手寫筆的基本位置和移動之外,您的應用程式也可以追蹤並收集筆劃中使用的不同壓力量。 這項資訊,以及筆尖形狀、大小和旋轉、筆跡色彩和用途的設定(純墨、清除、醒目提示和選取),可讓您提供與使用畫筆、鉛筆或筆刷在紙上撰寫或繪製非常類似的用戶體驗。

備註

您的應用程式也可以支援來自其他指向裝置的筆跡輸入,包括觸控數位板和滑鼠。 

墨水平臺非常有彈性。 其設計目的是要根據您的需求來支持各種功能層級。

如需 Windows Ink UX 指導方針,請參閱 筆跡控件

Windows Ink 平臺的元件

元件 說明
InkCanvas XAML UI 平台控制元件預設會接收並顯示從筆輸入的所有內容,無論是墨水筆劃還是橡皮擦筆劃。
如需有關如何使用 InkCanvas 的詳細資訊,請參閱 將 Windows Ink 筆劃辨識為文字存取 Windows Ink 筆劃資料
InkPresenter 程序代碼後置物件,連同 InkCanvas 控件一起具現化(透過 InkCanvas.InkPresenter 屬性公開)。 此物件提供InkCanvas所有的預設筆跡功能,以及一套完整的 API,以支援進一步的自定義和個人化。
有關如何使用 InkPresenter 的詳細資訊,請參閱 (將 Windows 筆跡筆劃辨識為文字)(存放及擷取 Windows Ink 筆劃資料)
繪圖工具列 XAML UI 平臺控制件,其中包含可自定義且可延伸的按鈕集合,可在相關聯的 InkCanvas 中啟用筆跡相關功能。
如需如何使用 InkToolbar 的詳細資訊,請參閱將 InkToolbar 新增至 Windows 應用程式筆跡應用程式
IInkD2DRenderer 啟用將筆墨筆劃轉譯到通用 Windows 應用程式的指定 Direct2D 裝置內容,而不是預設的 InkCanvas 控件。 這可讓您完整自定義手寫筆跡體驗。
如需詳細資訊,請參閱 複雜筆跡範例

InkCanvas 的基本繪圖功能

若要新增基本手寫筆跡功能,只要將 InkCanvas UWP 平臺控件放在應用程式的適當頁面上即可。

根據預設, InkCanvas 僅支援筆跡輸入。 輸入會使用色彩和粗細的預設設定呈現為墨跡筆劃(粗細為 2 像素的黑色原子筆),或被視作擦除筆劃(當輸入來自橡皮擦筆尖或經由清除按鈕變成橡皮擦功能的筆尖時)。

備註

若橡皮擦的末端或按鈕不存在,InkCanvas 可以設定為將筆尖的輸入當做擦除筆劃來處理。

在此範例中,InkCanvas 覆蓋背景影像。

備註

InkCanvas 預設 HeightWidth 屬性為零,除非它是某個能自動調整子元素大小的元素的子代,例如 StackPanelGrid 控制項。

<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 控制項呈現。

具有背景影像之空白 InkCanvas 的螢幕快照。 包含墨水筆跡的 InkCanvas 螢幕快照。 已清除一筆筆劃的 InkCanvas 螢幕快照。
具有背景影像的空白 InkCanvas 的 InkCanvas 上有墨跡筆劃。 InkCanvas 清除一筆劃(請注意清除如何作用於整筆,而不是部分)。

InkCanvas 控制項支援的筆跡功能由稱為 InkPresenter的後端代碼物件提供。

對於基本墨跡處理,您不必擔心 InkPresenter。 不過,若要在 InkCanvas上自定義和配置筆跡行為,您必須存取其對應的 InkPresenter 物件。

使用 InkPresenter 進行基本自定義

InkPresenter 對象會與每個 InkCanvas 控件实例化。

備註

無法直接實例化 InkPresenter。 而是透過 InkCanvasInkPresenter 屬性來存取它。 

除了提供其對應的 InkCanvas 控件的所有預設筆跡行為之外,InkPresenter 提供一組完整且全面的 API,用於提供額外的筆劃自定義以及對筆輸入(標準和修改)的更精細管理。 這包括筆劃屬性、支援的輸入裝置類型,以及輸入是由物件處理或傳遞至應用程式進行處理。

備註

標準筆跡輸入(從手寫筆尖或橡皮擦尖端或按鈕)不會受到次要硬體操作的修改,例如筆桿按鈕、滑鼠右鍵或類似機制。

根據預設,墨水技術僅支援手寫筆輸入。 在這裡,我們將 InkPresenter 設定為將畫筆和滑鼠的輸入數據解譯為筆墨筆劃。 我們也設定了一些初始筆墨筆劃屬性,用於將筆劃轉譯至 InkCanvas

若要啟用滑鼠和觸控墨跡,請將 InkPresenterInputDeviceTypes 屬性設定為您想要的 CoreInputDeviceTypes 值的組合。

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);
}

您可以動態設定筆跡筆劃屬性,以配合使用者喜好設定或應用程式需求。

在這裡,我們讓使用者從筆跡色彩清單中選擇。

<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>

然後,我們會處理所選色彩的變更,並據以更新筆跡筆劃屬性。

// 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自定義。

顯示包含預設黑色墨水筆觸的 InkCanvas 的螢幕快照。

具有預設黑色筆墨筆劃的 InkCanvas

InkCanvas 的螢幕快照,其中包含用戶選取的紅色筆墨筆劃。

InkCanvas 用戶選取的紅色筆劃。

若要提供筆跡和擦除以外的功能,例如筆劃選取,您的應用程式必須識別 InkPresenter 的特定輸入,以便未經處理直接傳遞給應用程式處理。

進階處理的傳遞輸入

根據預設,InkPresenter 將所有輸入當作墨跡或擦除筆劃來處理,包括由次要硬體裝置提供的功能修改的輸入,例如畫筆桶按鈕、滑鼠右鍵或類似按鈕。 不過,使用者通常會預期這些次要功能會有一些額外的功能或行為的改變。

在某些情況下,您可能也需要公開沒有次要功能的手寫筆的額外功能(通常與手寫筆筆尖無關的功能)、其他輸入裝置類型,或根據應用程式 UI 中用戶選取的某種類型的修改行為。

若要支援此功能,可以設定 InkPresenter 讓特定輸入保持未處理。 接著,這個未處理的輸入會傳遞至您的應用程式進行處理。

範例 - 使用未處理的輸入來實作筆劃選取

Windows Ink 平臺不提供需要修改輸入之動作的內建支援,例如筆劃選取。 若要支援這類功能,您必須在應用程式中提供自定義解決方案。

下列程式碼範例(所有程式碼都在 MainPage.xaml 和 MainPage.xaml.cs 檔案中)逐步說明如何在輸入使用筆身按鈕(或滑鼠右鍵)修改時啟用筆劃選取。

  1. 首先,我們在 MainPage.xaml 中設定 UI。

    在這裡,我們會新增一個畫布(位於 InkCanvas下方),用來繪製選取筆劃。 使用個別圖層來繪製選取筆劃,會讓 InkCanvas 及其內容保持不變。

    空白 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 中,我們會宣告幾個全域變數,以保存選擇用戶界面各方面的參考。 具體來說,選擇的套索筆劃和突顯所選筆劃的邊界矩形。

      // Stroke selection tool.
      private Polyline lasso;
      // Stroke selection area.
      private Rect boundingRect;
    
  3. 接下來,我們會設定 InkPresenter,將手寫筆和滑鼠的輸入數據轉換為筆劃,並為 InkCanvas設定一些用於顯示筆劃的初始屬性。

    最重要的是,我們使用 InkPresenterInputProcessingConfiguration 屬性,來表示任何修改過的輸入應由應用程式處理。 修改的輸入是通過指派 InputProcessingConfiguration.RightDragAction 一個值:InkInputRightDragAction.LeaveUnprocessed,來指定的。 設定此值時, InkPresenter 會傳遞至 InkUnprocessedInput 類別,這是一組指標事件,可供您處理。

    我們會為由 InkPresenter傳遞的未處理 PointerPressedPointerMovedPointerReleased 事件指派接聽程式。 所有選取功能都會在這些事件的處理程序中實作。

    最後,我們會為 InkPresenterStrokeStartedStrokesErased 事件設置接聽程式。 如果啟動新的筆劃或清除現有的筆劃,我們會使用這些事件的處理程式來清除選取UI介面。

    進階墨跡自定義範例應用程式的螢幕截圖,其中顯示具有預設黑色墨跡筆劃的繪圖畫布。

      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傳遞的 PointerPressedPointerMoved,以及 PointerReleased

    所有選取功能都會在這些處理程式中實作,包括套索筆劃和周框。

    選取套索的螢幕快照。

      // 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 事件處理程式,我們會清除選取層中的所有內容(套索筆劃),然後在套索區域所包含的墨跡周圍繪製單一矩形框。

    選取邊界矩形的螢幕快照。

      // 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 事件的處理程式。

    這兩者只要呼叫相同的清除函式,即可在偵測到新筆劃時清除目前的選取範圍。

      // 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. 以下是當開始新的筆劃或清除現有筆劃時,從選取畫布中移除所有選取界面的函式。

      // 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;
        }
      }
    

自訂墨水渲染

根據預設,筆跡輸入會在低延遲的背景執行緒上處理,並在繪製時呈現即為進行中或「濕潤」狀態。 當筆劃完成時(手寫筆或手指離開,或鬆開滑鼠按鍵),筆劃會在使用者介面執行緒上進行處理,並以“乾墨”形式呈現在 InkCanvas 層(置於應用程式內容之上並取代“濕墨”)。

您可以覆寫此預設行為,並透過「自定義乾燥」濕墨筆劃完全控制筆跡體驗。 雖然預設行為通常足以用於大多數應用程式,但在某些情況下可能需要自定義乾燥,包括:

  • 更有效率地管理大量或複雜的筆墨筆劃集合
  • 在大型筆跡畫布上更有效率的移動瀏覽和縮放支援
  • 交錯墨跡和其他物件,例如圖形或文字,同時維持排列順序
  • 將墨水同步乾燥後轉換為 DirectX 圖形(例如,將直線或圖形點陣化並整合到應用程式內容中,而非作為單獨的 InkCanvas 層)。

自定義乾燥需要 IInkD2DRenderer 物件來管理筆跡輸入,並將其轉譯為通用 Windows 應用程式的 Direct2D 裝置內容,而不是預設 InkCanvas 控件。

藉由呼叫 ActivateCustomDrying(在載入 InkCanvas 之前),應用程式會建立 InkSynchronizer 物件,用於自訂筆墨筆劃乾燥後轉譯至 SurfaceImageSourceVirtualSurfaceImageSource的方式。

SurfaceImageSourceVirtualSurfaceImageSource 提供 DirectX 共用表面,讓您的應用程式可以進行繪製並整合到應用程式的內容中,而 VSIS 提供的虛擬表面比螢幕更大,以便進行高效能的平移和縮放。 由於這些表面的視覺更新會與 XAML UI 執行緒同步處理,因此當筆跡被轉譯到任何一個表面時,可以同時從 InkCanvas 中移除濕墨。

您也可以將干墨自訂到 SwapChainPanel,但無法保證與 UI 執行緒同步,並且在筆跡渲染到 SwapChainPanel 以及從 InkCanvas 移除筆跡之間,可能會出現延遲。

如需這項功能的完整範例,請參閱 複雜筆跡範例

備註

自訂乾燥和 InkToolbar
如果您的 app 使用自定義乾燥功能覆寫 InkPresenter 的預設筆跡轉譯行為,則轉譯的墨跡已不再可供 InkToolbar 使用,且 InkToolbar 的內建擦除命令無法如預期地運作。 若要提供擦除功能,您必須處理所有指针事件、在每個笔触上執行命中測試,並覆寫內建的「清除所有筆跡」命令。

主題 說明
辨識筆跡筆劃 使用手寫辨識將筆跡筆劃轉換成文字,或使用自定義辨識將筆跡筆劃轉換成圖形。
儲存並取回墨水筆劃 使用內嵌的筆跡串行化格式 (ISF) 元數據,將筆跡筆劃數據儲存在圖形交換格式 (GIF) 檔案中。
將 InkToolbar 新增至 Windows 筆跡應用程式 將預設的 InkToolbar 新增至 Windows 筆跡應用程式,將自定義畫筆按鈕新增至 InkToolbar,並將自定義畫筆按鈕綁定至自定義畫筆定義。

應用程式介面(API)

範例

歸檔範例