將 InkToolbar 新增至通用 Windows 平台 (UWP) 應用程式Add an InkToolbar to a Universal Windows Platform (UWP) app

有兩個不同的控制項可協助在通用 Windows 平台 (UWP) 應用程式中使用手寫筆跡:InkCanvasInkToolbarThere are two different controls that facilitate inking in Universal Windows Platform (UWP) apps: InkCanvas and InkToolbar.

InkCanvas 控制項提供基本的 Windows 筆跡功能。The InkCanvas control provides basic Windows Ink functionality. 使用此控制項將手寫筆輸入轉譯為筆墨筆劃 (使用色彩與粗細的預設設定) 或擦去筆劃。Use it to render pen input as either an ink stroke (using default settings for color and thickness) or an erase stroke.

如需 InkCanvas 實作的詳細資訊,請參閱 UWP 應用程式中的畫筆和手寫筆互動For InkCanvas implementation details, see Pen and stylus interactions in UWP apps.

做為完全透明的重疊,InkCanvas 不會提供任何用於設定筆墨筆劃屬性的內建 UI。As a completely transparent overlay, the InkCanvas does not provide any built-in UI for setting ink stroke properties. 如果您想要變更預設的手寫筆跡體驗、讓使用者設定筆墨筆劃的屬性,以及支援其他自訂的手寫筆跡功能,您有兩個選項︰If you want to change the default inking experience, let users set ink stroke properties, and support other custom inking features, you have two options:

  • 在程式碼後置中,使用繫結至 InkCanvas 的基礎 InkPresenter 物件。In code-behind, use the underlying InkPresenter object bound to the InkCanvas.

    InkPresenter API 支援大量的手寫筆跡體驗自訂項目。The InkPresenter APIs support extensive customization of the inking experience. 如需更多詳細資料,請參閱 UWP 應用程式中的畫筆和手寫筆互動For more detail, see Pen and stylus interactions in UWP apps.

  • InkToolbar 繫結至 InkCanvas。Bind an InkToolbar to the InkCanvas. 根據預設,InkToolbar 會提供可自訂並可擴充的按鈕集合,以用於啟用與筆墨相關的功能 (例如筆觸大小、筆跡色彩及筆尖形狀)。By default, the InkToolbar provides a customizable and extensible collection of buttons for activating ink-related features such as stroke size, ink color, and pen tip.

    我們會在本主題中討論 InkToolbar。We discuss the InkToolbar in this topic.

重要 APIInkCanvas 類別InkToolbar 類別InkPresenter 類別Windows.UI.Input.InkingImportant APIs: InkCanvas class, InkToolbar class, InkPresenter class, Windows.UI.Input.Inking

預設 InkToolbarDefault InkToolbar

根據預設,InkToolbar 包含可用於繪圖、清除、反白顯示,以及顯示樣板 (尺規或量角器) 的按鈕。By default, the InkToolbar includes buttons for drawing, erasing, highlighting, and displaying a stencil (ruler or protractor). 根據功能而定,其他設定和命令 (例如筆跡色彩、筆劃粗細、清除所有筆跡) 將會在飛出視窗中提供。Depending on the feature, other settings and commands, such as ink color, stroke thickness, erase all ink, are provided in a flyout.

InkToolbarInkToolbar
預設 Windows Ink 工具列Default Windows Ink toolbar

若要新增預設的 InkToolbar 到手寫筆跡應用程式,只要將它放在與 InkCanvas 同一個頁面上並關聯兩個控制項即可。To add a default InkToolbar to an inking app, just place it on the same page as your InkCanvas and associate the two controls.

  1. 在 MainPage.xaml 中,針對手寫筆跡表面宣告容器物件 (如這個範例中,我們使用 [格線] 控制項)。In MainPage.xaml, declare a container object (for this example, we use a Grid control) for the inking surface.
  2. 將 InkCanvas 物件宣告為容器的子系。Declare an InkCanvas object as a child of the container. (InkCanvas 大小是繼承自容器。)(The InkCanvas size is inherited from the container.)
  3. 宣告 InkToolbar 並使用 TargetInkCanvas 屬性將它繫結至 InkCanvas。Declare an InkToolbar and use the TargetInkCanvas attribute to bind it to the InkCanvas.

注意

請確定 InkToolbar 是在 InkCanvas 之後宣告。Ensure the InkToolbar is declared after the InkCanvas. 如果不是,InkCanvas 重疊會將 InkToolbar 轉譯為無法存取。If not, the InkCanvas overlay renders the InkToolbar inaccessible.

<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" />
        <InkToolbar x:Name="inkToolbar"
          VerticalAlignment="Top"
          TargetInkCanvas="{x:Bind inkCanvas}" />
    </Grid>
</Grid>

基本自訂項目Basic customization

在本節中,我們會討論一些基本的 Windows Ink 工具列自訂案例。In this section, we cover some basic Windows Ink toolbar customization scenarios.

指定位置和方向Specify location and orientation

當您將筆跡工具列新增到您的應用程式時,您可以接受工具列的預設位置和方向,或是根據您應用程式或使用者的需要設定他們。When you add an ink toolbar to your app, you can accept the default location and orientaion of the toolbar or set them as required by your app or user.

XAMLXAML

透過其 VerticalAlignmentHorizontalAlignmentOrientation 屬性明確指定工具列的位置和方向。Explicitly specify the location and orientation of the toolbar through its VerticalAlignment, HorizontalAlignment, and Orientation properties.

DefaultDefault ExplicitExplicit
預設筆跡工具列位置和方向 明確筆跡工具列位置和方向
Windows Ink 工具列預設位置和方向Windows Ink toolbar default location and orientation Windows Ink 工具列明確位置和方向Windows Ink toolbar explicit location and orientation

以下是在 XAML 中明確設定筆跡工具列位置和方向的程式碼。Here's the code for explicitly setting the location and orientation of the ink toolbar in XAML.

<InkToolbar x:Name="inkToolbar" 
    VerticalAlignment="Center" 
    HorizontalAlignment="Right" 
    Orientation="Vertical" 
    TargetInkCanvas="{x:Bind inkCanvas}" />

根據使用者喜好設定或裝置狀態進行初始化Initialize based on user preferences or device state

在某些案例中,您可能會想要根據使用者喜好設定或裝置狀態設定筆跡工具列的位置和方向。In some cases, you might want to set the location and orientation of the ink toolbar based on user preference or device state. 下列範例示範如何根據透過 [設定] > [裝置] > [手寫筆與 Windows Ink] > [手寫筆] > [選擇您用來書寫的手] 指定的左手或右手書寫喜好設定,來設定筆跡工具列的位置和方向。The following example demonstrates how to set the location and orientation of the ink toolbar based on the left or right-hand writing preferences specified through Settings > Devices > Pen & Windows Ink > Pen > Choose which hand you write with.

的主要設定Dominant hand setting
主要設定Dominant hand setting

您可以透過 Windows.UI.ViewManagement 的 HandPreference 屬性查詢此設定,然後根據傳回的值設定 HorizontalAlignmentYou can query this setting through the HandPreference property of Windows.UI.ViewManagement and set the HorizontalAlignment based on the value returned. 在此範例中,我們會為慣用左手的人將工具列設在應用程式的左側,為慣用右手的人將工具列設在右側。In this example, we locate the toolbar on the left side of the app for a left-handed person and on the right side for a right-handed person.

筆墨工具列位置和方向範例下載此範例(基本)Download this sample from Ink toolbar location and orientation sample (basic)

public MainPage()
{
    this.InitializeComponent();

    Windows.UI.ViewManagement.UISettings settings = 
        new Windows.UI.ViewManagement.UISettings();
    HorizontalAlignment alignment = 
        (settings.HandPreference == 
            Windows.UI.ViewManagement.HandPreference.LeftHanded) ? 
            HorizontalAlignment.Left : HorizontalAlignment.Right;
    inkToolbar.HorizontalAlignment = alignment;
}

以動態方式調整使用者或裝置狀態Dynamically adjust to user or device state

您也可以使用繫結來追蹤根據使用者喜好設定、裝置設定或裝置狀態變更而產生的 UI 更新。You can also use binding to look after UI updates based on changes to user preferences, device settings, or device states. 在以下的範例中,我們會展開先前的範例,並示範如何使用繫結、一個 ViewMOdel 物件和 NotifyPropertyChanged 介面根據裝置的方向動態調整筆跡工具列的位置。In the following example, we expand on the previous example and show how to dynamically position the ink toolbar based on device orientation using binding, a ViewMOdel object, and the INotifyPropertyChanged interface.

筆墨工具列位置和方向範例下載此範例(動態)Download this sample from Ink toolbar location and orientation sample (dynamic)

  1. 首先,讓我們先新增 ViewModel。First, let's add our ViewModel.

    1. 將新資料夾新增到您的專案,然後命名為 ViewModelsAdd a new folder to your project and call it ViewModels.

    2. 在 ViewModels 資料夾中新增新的類別 (在此範例中,我們命名為 InkToolbarSnippetHostViewModel.cs)。Add a new class to the ViewModels folder (for this example, we called it InkToolbarSnippetHostViewModel.cs).

      注意

      我們使用單一模式 (英文),因為我們在應用程式的使用期間只需要一個此類型的物件We used the Singleton pattern as we only need one object of this type for the life of the application

    3. using System.ComponentModel 新增到檔案。Add using System.ComponentModel namespace to the file.

    4. 新增名為 instance 的靜態成員變數,以及名為 Instance 的靜態唯讀屬性。Add a static member variable called instance, and a static read only property named Instance. 將建構函式設為私密 (private),確保此類別只能透過 Instance 屬性存取。Make the constructor private to ensure this class can only be accessed via the Instance property.

      注意

      此類型繼承 INotifyPropertyChanged 介面,用於通知用戶端 (通常是繫結用戶端) 屬性已變更。This class inherits from INotifyPropertyChanged interface, which is used to notify clients, typically binding clients, that a property value has changed. 我們會使用這個處理裝置方向的變更 (我們會展開此程式碼並在之後的步驟中解釋)。We'll be using this to handle changes to the device orientation (we'll expand this code and explain further in a later step).

      using System.ComponentModel;
      
      namespace locationandorientation.ViewModels
      {
          public class InkToolbarSnippetHostViewModel : INotifyPropertyChanged
          {
              private static InkToolbarSnippetHostViewModel instance;
      
              public static InkToolbarSnippetHostViewModel Instance
              {
                  get
                  {
                      if (null == instance)
                      {
                          instance = new InkToolbarSnippetHostViewModel();
                      }
                      return instance;
                  }
              }
          }
      
          private InkToolbarSnippetHostViewModel() { }
      }
      
    5. 將兩個 bool 屬性新增到 InkToolbarSnippetHostViewModel 類別:LeftHandedLayout (與先前的僅 XAML 範例中具備相同功能) 和 PortraitLayout (裝置的方向)。Add two bool properties to the InkToolbarSnippetHostViewModel class: LeftHandedLayout (same functionality as the previous XAML-only example) and PortraitLayout (orientation of the device).

      注意

      PortraitLayout 可進行設定,並包含 PropertyChanged 事件的定義。The PortraitLayout property is settable and includes the defintion for the PropertyChanged event.

      public bool LeftHandedLayout
      {
          get
          {
              bool leftHandedLayout = false;
              Windows.UI.ViewManagement.UISettings settings =
                  new Windows.UI.ViewManagement.UISettings();
              leftHandedLayout = (settings.HandPreference ==
                  Windows.UI.ViewManagement.HandPreference.LeftHanded);
              return leftHandedLayout;
          }
      }
      
      public bool portraitLayout = false;
      public bool PortraitLayout
      {
          get
          {
              Windows.UI.ViewManagement.ApplicationViewOrientation winOrientation = 
                  Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().Orientation;
              portraitLayout = 
                  (winOrientation == 
                      Windows.UI.ViewManagement.ApplicationViewOrientation.Portrait);
              return portraitLayout;
          }
          set
          {
              if (value.Equals(portraitLayout)) return;
              portraitLayout = value;
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("PortraitLayout"));
          }
      }
      
  2. 現在,讓我們為專案新增幾個轉換器類別。Now, let's add a couple of converter classes to our project. 每個類別都包含一個 Convert 物件,該物件會傳回對齊值 (HorizontalAlignmentVerticalAlignment)。Each class contains a Convert object that returns an alignment value (either HorizontalAlignment or VerticalAlignment).

    1. 將新資料夾新增到您的專案,然後命名為 ConvertersAdd a new folder to your project and call it Converters.

    2. 將兩個新類別新增到 Converters 資料夾 (在此範例中,我們命名為 HorizontalAlignmentFromHandednessConverter.csVerticalAlignmentFromAppViewConverter.cs)。Add two new classes to the Converters folder (for this example, we call them HorizontalAlignmentFromHandednessConverter.cs and VerticalAlignmentFromAppViewConverter.cs).

    3. 為每個檔案新增 using Windows.UI.Xamlusing Windows.UI.Xaml.Data 命名空間。Add using Windows.UI.Xaml and using Windows.UI.Xaml.Data namespaces to each file.

    4. 將每個類別都變更為 public,指定其實作 IValueConverter 介面。Change each class to public and specify that it implements the IValueConverter interface.

    5. 為每個檔案新增 ConvertConvertBack 方法,如下所示 (我們先不實作 ConvertBack 方法)。Add the Convert and ConvertBack methods to each file, as shown here (we leave the ConvertBack method unimplemented).

      • HorizontalAlignmentFromHandednessConverter 會為慣用右手的使用者將筆跡工具列的位置設在應用程式的右側,並為慣用左手的使用者設在應用程式的左側。HorizontalAlignmentFromHandednessConverter positions the ink toolbar to the right side of the app for right-handed users and to the left side of the app for left-handed users.
      using System;
      
      using Windows.UI.Xaml;
      using Windows.UI.Xaml.Data;
      
      namespace locationandorientation.Converters
      {
          public class HorizontalAlignmentFromHandednessConverter : IValueConverter
          {
              public object Convert(object value, Type targetType,
                  object parameter, string language)
              {
                  bool leftHanded = (bool)value;
                  HorizontalAlignment alignment = HorizontalAlignment.Right;
                  if (leftHanded)
                  {
                      alignment = HorizontalAlignment.Left;
                  }
                  return alignment;
              }
      
              public object ConvertBack(object value, Type targetType,
                  object parameter, string language)
              {
                  throw new NotImplementedException();
              }
          }
      }
      
      • VerticalAlignmentFromAppViewConverter 會為直式方向將筆跡工具列設在應用程式的中央,並為橫向設在應用程式的頂部 (雖然我們意圖改善可用性,但這僅只是為了展示用途的任意選擇)。VerticalAlignmentFromAppViewConverter positions the ink toolbar to the center of the app for portrait orientation and to the top of the app for landscape orientation (while intended to improve usability, this is just an arbitrary choice for demonstration purposes).
      using System;
      
      using Windows.UI.Xaml;
      using Windows.UI.Xaml.Data;
      
      namespace locationandorientation.Converters
      {
          public class VerticalAlignmentFromAppViewConverter : IValueConverter
          {
              public object Convert(object value, Type targetType,
                  object parameter, string language)
              {
                  bool portraitOrientation = (bool)value;
                  VerticalAlignment alignment = VerticalAlignment.Top;
                  if (portraitOrientation)
                  {
                      alignment = VerticalAlignment.Center;
                  }
                  return alignment;
              }
      
              public object ConvertBack(object value, Type targetType,
                  object parameter, string language)
              {
                  throw new NotImplementedException();
              }
          }
      }
      
  3. 現在,開啟 MainPage.xaml.cs 檔案。Now, open the MainPage.xaml.cs file.

    1. using using locationandorientation.ViewModels 新增至命名空間清單,以關聯我們的 ViewModel。Add using using locationandorientation.ViewModels to the list of namespaces to associate our ViewModel.
    2. using Windows.UI.ViewManagement 新增至命名空間清單,以允許接聽裝置方向的變更。Add using Windows.UI.ViewManagement to the list of namespaces to enable listening for changes to the device orientation.
    3. 新增WindowSizeChangedEventHandler程式碼。Add the WindowSizeChangedEventHandler code.
    4. 將此視圖的DataCoNtext設定為 InkToolbarSnippetHostViewModel 類別的單一實例。Set the DataContext for the view to the singleton instance of the InkToolbarSnippetHostViewModel class.
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    using locationandorientation.ViewModels;
    using Windows.UI.ViewManagement;
    
    namespace locationandorientation
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
    
                Window.Current.SizeChanged += (sender, args) =>
                {
                    ApplicationView currentView = ApplicationView.GetForCurrentView();
    
                    if (currentView.Orientation == ApplicationViewOrientation.Landscape)
                    {
                        InkToolbarSnippetHostViewModel.Instance.PortraitLayout = false;
                    }
                    else if (currentView.Orientation == ApplicationViewOrientation.Portrait)
                    {
                        InkToolbarSnippetHostViewModel.Instance.PortraitLayout = true;
                    }
                };
    
                DataContext = InkToolbarSnippetHostViewModel.Instance;
            }
        }
    }
    
  4. 接下來,開啟 MainPage. xaml 檔案。Next, open the MainPage.xaml file.

    1. xmlns:converters="using:locationandorientation.Converters" 新增至 Page 元素,以系結至我們的轉換器。Add xmlns:converters="using:locationandorientation.Converters" to the Page element for binding to our converters.

      <Page
      x:Class="locationandorientation.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:locationandorientation"
      xmlns:converters="using:locationandorientation.Converters"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
      
    2. 新增 PageResources 元素,並指定對我們的轉換器的參考。Add a PageResources element and specify references to our converters.

      <Page.Resources>
          <converters:HorizontalAlignmentFromHandednessConverter x:Key="HorizontalAlignmentConverter"/>
          <converters:VerticalAlignmentFromAppViewConverter x:Key="VerticalAlignmentConverter"/>
      </Page.Resources>
      
    3. 新增 InkCanvas 和 InkToolbar 元素,並系結 InkToolbar 的 VerticalAlignment 和 HorizontalAlignment 屬性。Add the InkCanvas and InkToolbar elements and bind the VerticalAlignment and HorizontalAlignment properties of the InkToolbar.

      <InkCanvas x:Name="inkCanvas" />
      <InkToolbar x:Name="inkToolbar" 
                  VerticalAlignment="{Binding PortraitLayout, Converter={StaticResource VerticalAlignmentConverter} }" 
                  HorizontalAlignment="{Binding LeftHandedLayout, Converter={StaticResource HorizontalAlignmentConverter} }" 
                  Orientation="Vertical" 
                  TargetInkCanvas="{x:Bind inkCanvas}" />
      
  5. 返回 InkToolbarSnippetHostViewModel.cs 檔案,將我們的 PortraitLayoutLeftHandedLayout bool 屬性新增至 InkToolbarSnippetHostViewModel 類別,以及在該屬性值變更時支援重新系結 PortraitLayoutReturn to the InkToolbarSnippetHostViewModel.cs file to add our PortraitLayout and LeftHandedLayout bool properties to the InkToolbarSnippetHostViewModel class, along with support for rebinding PortraitLayout when that property value changes.

    public bool LeftHandedLayout
    {
        get
        {
            bool leftHandedLayout = false;
            Windows.UI.ViewManagement.UISettings settings =
                new Windows.UI.ViewManagement.UISettings();
            leftHandedLayout = (settings.HandPreference ==
                Windows.UI.ViewManagement.HandPreference.LeftHanded);
            return leftHandedLayout;
        }
    }
    
    public bool portraitLayout = false;
    public bool PortraitLayout
    {
        get
        {
            Windows.UI.ViewManagement.ApplicationViewOrientation winOrientation = 
                Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().Orientation;
            portraitLayout = 
                (winOrientation == 
                    Windows.UI.ViewManagement.ApplicationViewOrientation.Portrait);
            return portraitLayout;
        }
        set
        {
            if (value.Equals(portraitLayout)) return;
            portraitLayout = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("PortraitLayout"));
        }
    }
    
    #region INotifyPropertyChanged Members
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected void OnPropertyChanged(string property)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }
    
    #endregion
    

您現在應該有一個筆跡應用程式,可適應使用者的主要喜好設定,並動態地回應使用者裝置的方向。You should now have an inking app that adapts to both the dominant hand preference of the user and dynamically responds to the orientation of the user's device.

指定選取的按鈕Specify the selected button

在初始化時選取 鉛筆按鈕Pencil button selected at initialization
在初始化時已選取 [鉛筆] 按鈕的 Windows Ink 工具列Windows Ink toolbar with pencil button selected at initialization

根據預設,當您的應用程式啟動時或是工具列初始化時,會選取第一個 (或最左邊) 按鈕。By default, the first (or leftmost) button is selected when your app is launched and the toolbar is initialized. 在預設 Windows Ink 工具列中,這是鋼珠筆按鈕。In the default Windows Ink toolbar, this is the ballpoint pen button.

因為架構定義了內建按鈕的順序,根據預設,第一個按鈕可能不是您想要啟用的畫筆或工具。Because the framework defines the order of the built-in buttons, the first button might not be the pen or tool you want to activate by default.

您可以覆寫這個預設行為,並在工具列上指定選取的按鈕。You can override this default behavior and specify the selected button on the toolbar.

這個範例中,我們透過選取的鉛筆按鈕和啟用的鉛筆 (而不是鋼珠筆) 將預設工具列初始化。For this example, we initialize the default toolbar with the pencil button selected and the pencil activated (instead of the ballpoint pen).

  1. 針對上一個範例的 InkCanvas 和 InkToolbar 使用 XAML 宣告。Use the XAML declaration for the InkCanvas and InkToolbar from the previous example.
  2. 在程式碼後置中,針對 InkToolbar 物件的 Loaded 事件設定處理常式。In code-behind, set up a handler for the Loaded event of the InkToolbar object.
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// Here, we set up InkToolbar event listeners.
/// </summary>
public MainPage_CodeBehind()
{
    this.InitializeComponent();
    // Add handlers for InkToolbar events.
    inkToolbar.Loaded += inkToolbar_Loaded;
}
  1. Loaded 事件的處理常式中:In the handler for the Loaded event:

    1. 取得內建 InkToolbarPencilButton 的參考。Get a reference to the built-in InkToolbarPencilButton.

    GetToolButton 方法中傳遞 InkToolbarTool.Pencil 物件會傳回 InkToolbarPencilButtonInkToolbarToolButton 物件。Passing an InkToolbarTool.Pencil object in the GetToolButton method returns an InkToolbarToolButton object for the InkToolbarPencilButton.

    1. ActiveTool 設為在上一個步驟中傳回的物件。Set ActiveTool to the object returned in the previous step.
/// <summary>
/// Handle the Loaded event of the InkToolbar.
/// By default, the active tool is set to the first tool on the toolbar.
/// Here, we set the active tool to the pencil button.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void inkToolbar_Loaded(object sender, RoutedEventArgs e)
{
    InkToolbarToolButton pencilButton = inkToolbar.GetToolButton(InkToolbarTool.Pencil);
    inkToolbar.ActiveTool = pencilButton;
}

指定內建的按鈕Specify the built-in buttons

初始化時所包含 特定按鈕Specific buttons included at initialization
初始化時包含的特定按鈕Specific buttons included at initialization

如前所述,Windows Ink 工具列會包含一組預設的內建按鈕。As mentioned, the Windows Ink toolbar includes a collection of default, built-in buttons. 這些按鈕會以下列順序顯示 (由左至右):These buttons are displayed in the following order (from left to right):

這個範例中,我們僅會初始化內建鋼珠筆、鉛筆以及橡皮擦按鈕。For this example, we initialize the toolbar with only the built-in ballpoint pen, pencil, and eraser buttons.

您可以使用 XAML 或程式碼後置這麼做。You can do this using either XAML or code-behind.

XAMLXAML

修改第一個範例中 InkCanvas 和 InkToolbar 的 XAML 宣告。Modify the XAML declaration for the InkCanvas and InkToolbar from the first example.

注意

按鈕會依照架構所定義的順序 (而非此處指定的順序) 新增至工具列。Buttons are added to the toolbar in the order defined by the framework, not the order specified here.

<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" />
        <!-- Clear the default InkToolbar buttons by setting InitialControls to None. -->
        <!-- Set the active tool to the pencil button. -->
        <InkCanvas x:Name="inkCanvas" />
        <InkToolbar x:Name="inkToolbar"
                    VerticalAlignment="Top"
                    TargetInkCanvas="{x:Bind inkCanvas}"
                    InitialControls="None">
            <!--
             Add only the ballpoint pen, pencil, and eraser.
             Note that the buttons are added to the toolbar in the order
             defined by the framework, not the order we specify here.
            -->
            <InkToolbarEraserButton />
            <InkToolbarBallpointPenButton />
            <InkToolbarPencilButton/>
        </InkToolbar>
    </Grid>
</Grid>

程式碼後置Code-behind

  1. 針對第一個範例的 InkCanvas 和 InkToolbar 使用 XAML 宣告。Use the XAML declaration for the InkCanvas and InkToolbar from the first example.
<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" />
        <InkToolbar x:Name="inkToolbar"
        VerticalAlignment="Top"
        TargetInkCanvas="{x:Bind inkCanvas}" />
    </Grid>
</Grid>
  1. 在程式碼後置中,針對 InkToolbar 物件的 Loading 事件設定處理常式。In code-behind, set up a handler for the Loading event of the InkToolbar object.
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// Here, we set up InkToolbar event listeners.
/// </summary>
public MainPage_CodeBehind()
{
    this.InitializeComponent();
    // Add handlers for InkToolbar events.
    inkToolbar.Loading += inkToolbar_Loading;
}
  1. InitialControls 設定為 "None"。Set InitialControls to "None".
  2. 建立應用程式所需按鈕的物件參考。Create object references for the buttons required by your app. 現在,我們僅新增 InkToolbarBallpointPenButtonInkToolbarPencilButtonInkToolbarEraserButtonHere, we add InkToolbarBallpointPenButton, InkToolbarPencilButton, and InkToolbarEraserButton only.

注意

按鈕會依照架構所定義的順序 (而非此處指定的順序) 新增至工具列。Buttons are added to the toolbar in the order defined by the framework, not the order specified here.

  1. 將按鈕新增到 InkToolbar。Add the buttons to the InkToolbar.
/// <summary>
/// Handles the Loading event of the InkToolbar.
/// Here, we identify the buttons to include on the InkToolbar.
/// </summary>
/// <param name="sender">The InkToolbar</param>
/// <param name="args">The InkToolbar event data.
/// If there is no event data, this parameter is null</param>
private void inkToolbar_Loading(FrameworkElement sender, object args)
{
    // Clear all built-in buttons from the InkToolbar.
    inkToolbar.InitialControls = InkToolbarInitialControls.None;

    // Add only the ballpoint pen, pencil, and eraser.
    // Note that the buttons are added to the toolbar in the order
    // defined by the framework, not the order we specify here.
    InkToolbarBallpointPenButton ballpoint = new InkToolbarBallpointPenButton();
    InkToolbarPencilButton pencil = new InkToolbarPencilButton();
    InkToolbarEraserButton eraser = new InkToolbarEraserButton();
    inkToolbar.Children.Add(eraser);
    inkToolbar.Children.Add(ballpoint);
    inkToolbar.Children.Add(pencil);
}

自訂按鈕和手寫筆跡功能Custom buttons and inking features

您可以自訂和延伸透過 InkToolbar 所提供按鈕 (以及相關聯的手寫筆跡功能) 的集合。You can customize and extend the collection of buttons (and associated inking features) that are provided through the InkToolbar.

InkToolbar 包含兩個不同群組的按鈕類型︰The InkToolbar consists of two distinct groups of button types:

  1. 包含內建繪圖、清除以及醒目提示按鈕的「工具」按鈕群組。A group of "tool" buttons containing the built-in drawing, erasing, and highlighting buttons. 在此處新增自訂的畫筆與工具。Custom pens and tools are added here.

注意  特徵選取是互斥的。Note  Feature selection is mutually exclusive.

  1. 包含內建尺規按鈕的「切換」按鈕群組。A group of "toggle" buttons containing the built-in ruler button. 在此處新增自訂的切換。Custom toggles are added here.

注意  功能不是互斥的,而且可以與其他作用中的工具同時使用。Note  Features are not mutually exclusive and can be used concurrently with other active tools.

根據您的應用程式和所需的手寫筆跡功能,您可以將下列任何按鈕 (繫結到您自訂的筆跡功能) 新增到 InkToolbar:Depending on your application and the inking functionality required, you can add any of the following buttons (bound to your custom ink features) to the InkToolbar:

  • 自訂畫筆 – 適用於由主控應用程式定義的筆跡調色盤和筆尖屬性 (例如圖形、旋轉和大小) 的畫筆。Custom pen – a pen for which the ink color palette and pen tip properties, such as shape, rotation, and size, are defined by the host app.
  • 自訂工具 – 由主控應用程式定義的非畫筆工具。Custom tool – a non-pen tool, defined by the host app.
  • 自訂切換 – 將應用程式定義功能的狀態設定為開啟或關閉。Custom toggle – Sets the state of an app-defined feature to on or off. 開啟時,功能會與使用中的工具搭配使用。When turned on, the feature works in conjunction with the active tool.

注意  您無法變更內建按鈕的顯示順序。Note  You cannot change the display order of the built-in buttons. 預設的顯示順序是︰鋼珠筆、鉛筆、螢光筆、橡皮擦和尺規。The default display order is: Ballpoint pen, pencil, highlighter, eraser, and ruler. 自訂畫筆會附加到最後一個預設畫筆,自訂工具按鈕會新增到最後一個畫筆按鈕與橡皮擦按鈕之間,而自訂切換按鈕會新增到尺規按鈕之後。Custom pens are appended to the last default pen, custom tool buttons are added between the last pen button and the eraser button and custom toggle buttons are added after the ruler button. (自訂按鈕會以指定的順序新增。)(Custom buttons are added in the order they are specified.)

自訂畫筆Custom pen

您可以在您定義筆跡調色盤和畫筆祕訣屬性 (例如形狀、旋轉和大小) 的位置,建立自訂的畫筆 (透過自訂的畫筆按鈕啟用)。You can create a custom pen (activated through a custom pen button) where you define the ink color palette and pen tip properties, such as shape, rotation, and size.

自訂的書法畫筆按鈕Custom calligraphic pen button
自訂書法畫筆按鈕Custom calligraphic pen button

這個範例中,我們會定義具有寬筆尖的自訂畫筆,支援基本的書法筆墨筆劃。For this example, we define a custom pen with a broad tip that enables basic calligraphic ink strokes. 我們也會自訂按鈕飛出視窗上所顯示調色盤中的筆刷集合。We also customize the collection of brushes in the palette displayed on the button flyout.

程式碼後置Code-behind

首先,我們換定義我們的自訂畫筆並在程式碼後置中指定繪圖屬性。First, we define our custom pen and specify the drawing attributes in code-behind. 稍後我們會從 XAML 參考此自訂畫筆。We reference this custom pen from XAML later.

  1. 在 [方案總管] 中的專案上按一下滑鼠右鍵,然後選取 [加入] > [新增項目]。Right click the project in Solution Explorer and select Add -> New item.
  2. 在 [Visual C#] -> [程式碼] 底下,新增新的類別檔案,並命名為 CalligraphicPen.cs。Under Visual C# -> Code, add a new Class file and call it CalligraphicPen.cs.
  3. 在 Calligraphic.cs 中,使用下列項目取代預設 using 區塊:In Calligraphic.cs, replace the default using block with the following:
using System.Numerics;
using Windows.UI;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
  1. 指定 CalligraphicPen 類別是衍生自 InkToolbarCustomPenSpecify that the CalligraphicPen class is derived from InkToolbarCustomPen.
class CalligraphicPen : InkToolbarCustomPen
{
}
  1. 覆寫 CreateInkDrawingAttributesCore 以指定您自己的筆刷和筆觸大小。Override CreateInkDrawingAttributesCore to specify your own brush and stroke size.
class CalligraphicPen : InkToolbarCustomPen
{
    protected override InkDrawingAttributes
      CreateInkDrawingAttributesCore(Brush brush, double strokeWidth)
    {
    }
}
  1. 建立 InkDrawingAttributes 物件,並設定筆尖形狀筆尖旋轉筆觸大小筆跡色彩Create an InkDrawingAttributes object and set the pen tip shape, tip rotation, stroke size, and ink color.
class CalligraphicPen : InkToolbarCustomPen
{
    protected override InkDrawingAttributes
      CreateInkDrawingAttributesCore(Brush brush, double strokeWidth)
    {
        InkDrawingAttributes inkDrawingAttributes =
          new InkDrawingAttributes();
        inkDrawingAttributes.PenTip = PenTipShape.Circle;
        inkDrawingAttributes.Size =
          new Windows.Foundation.Size(strokeWidth, strokeWidth * 20);
        SolidColorBrush solidColorBrush = brush as SolidColorBrush;
        if (solidColorBrush != null)
        {
            inkDrawingAttributes.Color = solidColorBrush.Color;
        }
        else
        {
            inkDrawingAttributes.Color = Colors.Black;
        }

        Matrix3x2 matrix = Matrix3x2.CreateRotation(45);
        inkDrawingAttributes.PenTipTransform = matrix;

        return inkDrawingAttributes;
    }
}

XAMLXAML

接下來,我們會在 MainPage.xaml 中新增對自訂畫筆的必要參考。Next, we add the necessary references to the custom pen in MainPage.xaml.

  1. 我們會宣告一個本機頁面資源字典,它會建立對 CalligraphicPen.cs 中所定義自訂畫筆 (CalligraphicPen) 的參考,以及自訂畫筆 (CalligraphicPenPalette) 支援的筆刷集合We declare a local page resource dictionary that creates a reference to the custom pen (CalligraphicPen) defined in CalligraphicPen.cs, and a brush collection supported by the custom pen (CalligraphicPenPalette).
<Page.Resources>
    <!-- Add the custom CalligraphicPen to the page resources. -->
    <local:CalligraphicPen x:Key="CalligraphicPen" />
    <!-- Specify the colors for the palette of the custom pen. -->
    <BrushCollection x:Key="CalligraphicPenPalette">
        <SolidColorBrush Color="Blue" />
        <SolidColorBrush Color="Red" />
    </BrushCollection>
</Page.Resources>
  1. 然後,我們會使用子系 InkToolbarCustomPenButton 元素新增 InkToolbar。We then add an InkToolbar with a child InkToolbarCustomPenButton element.

自訂畫筆按鈕包含兩個在頁面資源中宣告的靜態資源參考︰CalligraphicPenCalligraphicPenPaletteThe custom pen button includes the two static resource references declared in the page resources: CalligraphicPen and CalligraphicPenPalette.

我們也會指定筆觸大小滑桿的範圍 (MinStrokeWidthMaxStrokeWidthSelectedStrokeWidth)、選取的筆刷 (SelectedBrushIndex),以及自訂畫筆按鈕的圖示 (SymbolIcon)。We also specify the range for the stroke size slider (MinStrokeWidth, MaxStrokeWidth, and SelectedStrokeWidth), the selected brush (SelectedBrushIndex), and the icon for the custom pen button (SymbolIcon).

<Grid Grid.Row="1">
    <InkCanvas x:Name="inkCanvas" />
    <InkToolbar x:Name="inkToolbar"
                VerticalAlignment="Top"
                TargetInkCanvas="{x:Bind inkCanvas}">
        <InkToolbarCustomPenButton
            CustomPen="{StaticResource CalligraphicPen}"
            Palette="{StaticResource CalligraphicPenPalette}"
            MinStrokeWidth="1" MaxStrokeWidth="3" SelectedStrokeWidth="2"
            SelectedBrushIndex ="1">
            <SymbolIcon Symbol="Favorite" />
            <InkToolbarCustomPenButton.ConfigurationContent>
                <InkToolbarPenConfigurationControl />
            </InkToolbarCustomPenButton.ConfigurationContent>
        </InkToolbarCustomPenButton>
    </InkToolbar>
</Grid>

自訂切換Custom toggle

您可以建立自訂的切換開關 (透過自訂的切換按鈕啟用),將應用程式定義功能的狀態設定為開啟或關閉。You can create a custom toggle (activated through a custom toggle button) to set the state of an app-defined feature to on or off. 開啟時,功能會與使用中的工具搭配使用。When turned on, the feature works in conjunction with the active tool.

在此範例中,我們會定義自訂的切換按鈕,利用觸控輸入使用手寫筆跡 (觸控筆跡預設未啟用)。In this example, we define a custom toggle button that enables inking with touch input (by default, touch inking is not enabled).

注意

如果您需要支援使用觸控的手寫筆跡,我們建議您使用此範例中指定的圖示與工具提示,使用 CustomToggleButton 加以啟用。If you need to support inking with touch, we recommended that you enable it using a CustomToggleButton, with the icon and tooltip specified in this example.

觸控輸入通常是用來直接操作物件或應用程式 UI。Typically, touch input is used for direct manipulation of an object or the app UI. 為了示範觸控筆跡啟用時的行為差異,我們在 ScrollViewer 容器內放置 InkCanvas,並設定 ScrollViewer 的尺寸小於 InkCanvas。To demonstrate the differences in behavior when touch inking is enabled, we place the InkCanvas within a ScrollViewer container and set the dimensions of the ScrollViewer to be smaller than the InkCanvas.

當應用程式啟動時,只支援畫筆筆跡,觸控則用來移動瀏覽或縮放筆跡表面。When the app starts, only pen inking is supported and touch is used to pan or zoom the inking surface. 觸控筆跡啟用時,就無法透過觸控輸入移動瀏覽或縮放筆跡表面。When touch inking is enabled, the inking surface cannot be panned or zoomed through touch input.

注意

請參閱筆跡控制項了解 InkCanvasInkToolbar 兩者的 UX 指導方針。See Inking controls for both InkCanvas and InkToolbar UX guidelines. 下列是與此範例相關的建議︰The following recommendations are relevant to this example:

  • InkToolbar (以及一般的手寫筆跡) 最能夠透過主動式手寫筆來體驗。The InkToolbar, and inking in general, is best experienced through an active pen. 不過,如果您的應用程式要求,也可支援使用滑鼠與觸控的手寫筆跡。However, inking with mouse and touch can be supported if required by your app.
  • 如果支援使用觸控輸入的手寫筆跡,建議您針對切換按鈕 (包含「觸控書寫」工具提示) 使用 "Segoe MLD2 Assets" 字型的"ED5F" 圖示。If supporting inking with touch input, we recommend using the "ED5F" icon from the "Segoe MLD2 Assets" font for the toggle button, with a "Touch writing" tooltip.

XAMLXAML

  1. 首先,我們宣告 InkToolbarCustomToggleButton 項目 (toggleButton),包含指定事件處理常式 (Toggle_Custom) 的 Click 事件接聽程式。First, we declare an InkToolbarCustomToggleButton element (toggleButton) with a Click event listener that specifies the event handler (Toggle_Custom).
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel Grid.Row="0" 
                x:Name="HeaderPanel" 
                Orientation="Horizontal">
        <TextBlock x:Name="Header" 
                   Text="Basic ink sample" 
                   Style="{ThemeResource HeaderTextBlockStyle}" 
                   Margin="10" />
    </StackPanel>

    <ScrollViewer Grid.Row="1" 
                  HorizontalScrollBarVisibility="Auto" 
                  VerticalScrollBarVisibility="Auto">
        
        <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            
            <InkToolbar Grid.Row="0" 
                        Margin="10"
                        x:Name="inkToolbar" 
                        VerticalAlignment="Top"
                        TargetInkCanvas="{x:Bind inkCanvas}">
                <InkToolbarCustomToggleButton 
                x:Name="toggleButton" 
                Click="CustomToggle_Click" 
                ToolTipService.ToolTip="Touch Writing">
                    <SymbolIcon Symbol="{x:Bind TouchWritingIcon}"/>
                </InkToolbarCustomToggleButton>
            </InkToolbar>
            
            <ScrollViewer Grid.Row="1" 
                          Height="500"
                          Width="500"
                          x:Name="scrollViewer" 
                          ZoomMode="Enabled" 
                          MinZoomFactor=".1" 
                          VerticalScrollMode="Enabled" 
                          VerticalScrollBarVisibility="Auto" 
                          HorizontalScrollMode="Enabled" 
                          HorizontalScrollBarVisibility="Auto">
                
                <Grid x:Name="outputGrid" 
                      Height="1000"
                      Width="1000"
                      Background="{ThemeResource SystemControlBackgroundChromeWhiteBrush}">
                    <InkCanvas x:Name="inkCanvas"/>
                </Grid>
                
            </ScrollViewer>
        </Grid>
    </ScrollViewer>
</Grid>

程式碼後置Code-behind

  1. 在先前的程式碼片段中,我們為觸控筆跡 (toggleButton) 在自訂的切換按鈕上宣告了 Click 事件接聽程式和處理常式 (Toggle_Custom)。In the previous snippet, we declared a Click event listener and handler (Toggle_Custom) on the custom toggle button for touch inking (toggleButton). 這個處理常式只會透過 InkPresenter 的 InputDeviceTypes 屬性切換 CoreInputDeviceTypes.Touch 的支援。This handler simply toggles support for CoreInputDeviceTypes.Touch through the InputDeviceTypes property of the InkPresenter.

    此外,我們還使用 SymbolIcon 元素指定按鈕的圖示,並使用 {x:Bind} 標記延伸將它繫結到程式碼後置檔案 (TouchWritingIcon) 中定義的欄位。We also specified an icon for the button using the SymbolIcon element and the {x:Bind} markup extension that binds it to a field defined in the code-behind file (TouchWritingIcon).

    下列程式碼片段包含 Click 事件處理常式和 TouchWritingIcon 的定義。The following snippet includes both the Click event handler and the definition of TouchWritingIcon.

namespace Ink_Basic_InkToolbar
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage_AddCustomToggle : Page
    {
        Symbol TouchWritingIcon = (Symbol)0xED5F;

        public MainPage_AddCustomToggle()
        {
            this.InitializeComponent();
        }

        // Handler for the custom toggle button that enables touch inking.
        private void CustomToggle_Click(object sender, RoutedEventArgs e)
        {
            if (toggleButton.IsChecked == true)
            {
                inkCanvas.InkPresenter.InputDeviceTypes |= CoreInputDeviceTypes.Touch;
            }
            else
            {
                inkCanvas.InkPresenter.InputDeviceTypes &= ~CoreInputDeviceTypes.Touch;
            }
        }
    }
}

自訂工具Custom tool

您可以建立自訂工具按鈕來叫用您的應用程式所定義的非手寫筆工具。You can create a custom tool button to invoke a non-pen tool that is defined by your app.

根據預設,InkPresenter 會將所有輸入處理為筆墨筆劃或清除筆劃。By default, an InkPresenter processes all input as either an ink stroke or an erase stroke. 這包括透過次要硬體能供性所修改的輸入,例如畫筆筆身按鈕、滑鼠右鍵按鈕或類似按鈕。This includes input modified by a secondary hardware affordance such as a pen barrel button, a right mouse button, or similar. 不過,InkPresenter 可以設定為不處理特定的輸入,接著傳遞至您的應用程式進行自訂的處理。However, InkPresenter can be configured to leave specific input unprocessed, which can then be passed through to your app for custom processing.

在此範例中,我們定義自訂的工具按鈕,選取時,讓後續的筆觸進行處理,並轉譯為選取套索 (虛線) 而不是筆跡。In this example, we define a custom tool button that, when selected, causes subsequent strokes to be processed and rendered as a selection lasso (dashed line) instead of ink. 選取區域界限內所有的筆墨筆劃都設定為 SelectedAll ink strokes within the bounds of the selection area are set to Selected.

注意

請參閱<筆跡控制項>了解 InkCanvas 與 InkToolbar 兩者的 UX 指導方針。See Inking controls for both InkCanvas and InkToolbar UX guidelines. 下列是與此範例相關的建議︰The following recommendation is relevant to this example:

  • 如果提供筆劃選取項目,建議您針對工具按鈕 (包含「選取工具」工具提示) 使用 "Segoe MLD2 Assets" 字型的 "EF20" 圖示。If providing stroke selection, we recommend using the "EF20" icon from the "Segoe MLD2 Assets" font for the tool button, with a "Selection tool" tooltip.

XAMLXAML

  1. 首先,我們宣告 InkToolbarCustomToolButton 項目 (customToolButton),包含指定已設定筆劃選取項目之事件處理常式 (customToolButton_Click) 的 Click 事件接聽程式。First, we declare an InkToolbarCustomToolButton element (customToolButton) with a Click event listener that specifies the event handler (customToolButton_Click) where stroke selection is configured. (我們也已經新增一組用於複製、剪下並貼上筆劃選取項目的按鈕)。(We've also added a set of buttons for copying, cutting, and pasting the stroke selection.)

  2. 我們也新增 Canvas 元素用於繪製我們選取的筆劃。We also add a Canvas element for drawing our selection stroke. 使用不同的一層來繪製選取筆劃,確保 InkCanvas 及其內容保持原貌。Using a separate layer to draw the selection stroke ensures the InkCanvas and its content remain untouched.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <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>
    <StackPanel x:Name="ToolPanel" Orientation="Horizontal" Grid.Row="1">
        <InkToolbar x:Name="inkToolbar" 
                    VerticalAlignment="Top" 
                    TargetInkCanvas="{x:Bind inkCanvas}">
            <InkToolbarCustomToolButton 
                x:Name="customToolButton" 
                Click="customToolButton_Click" 
                ToolTipService.ToolTip="Selection tool">
                <SymbolIcon Symbol="{x:Bind SelectIcon}"/>
            </InkToolbarCustomToolButton>
        </InkToolbar>
        <Button x:Name="cutButton" 
                Content="Cut" 
                Click="cutButton_Click"
                Width="100"
                Margin="5,0,0,0"/>
        <Button x:Name="copyButton" 
                Content="Copy"  
                Click="copyButton_Click"
                Width="100"
                Margin="5,0,0,0"/>
        <Button x:Name="pasteButton" 
                Content="Paste"  
                Click="pasteButton_Click"
                Width="100"
                Margin="5,0,0,0"/>
    </StackPanel>
    <Grid Grid.Row="2" x:Name="outputGrid" 
              Background="{ThemeResource SystemControlBackgroundChromeWhiteBrush}" 
              Height="Auto">
        <!-- Canvas for displaying selection UI. -->
        <Canvas x:Name="selectionCanvas"/>
        <!-- Canvas for displaying ink. -->
        <InkCanvas x:Name="inkCanvas" />
    </Grid>
</Grid>

程式碼後置Code-behind

  1. 然後我們會在 MainPage.xaml.cs 程式碼後置檔案中處理 InkToolbarCustomToolButton 的 Click 事件。We then handle the Click event for the InkToolbarCustomToolButton in the MainPage.xaml.cs code-behind file.

    這個處理常式會設定 InkPresenter 將未處理的輸入傳遞到應用程式。This handler configures the InkPresenter to pass unprocessed input through to the app.

    如需此程式碼的更詳細步驟︰請參閱 UWP 應用程式中的手寫筆互動與 Windows Ink 的<傳入輸入以進行進階處理>一節。For a more detailed step through of this code: See the Pass-through input for advanced processing section of Pen interactions and Windows Ink in UWP apps.

    此外,我們還使用 SymbolIcon 元素指定按鈕的圖示,並使用 {x:Bind} 標記延伸將它繫結到程式碼後置檔案 (SelectIcon) 中定義的欄位。We also specified an icon for the button using the SymbolIcon element and the {x:Bind} markup extension that binds it to a field defined in the code-behind file (SelectIcon).

    下列程式碼片段包含 Click 事件處理常式和 SelectIcon 的定義。The following snippet includes both the Click event handler and the definition of SelectIcon.

namespace Ink_Basic_InkToolbar
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage_AddCustomTool : Page
    {
        // Icon for custom selection tool button.
        Symbol SelectIcon = (Symbol)0xEF20;

        // Stroke selection tool.
        private Polyline lasso;
        // Stroke selection area.
        private Rect boundingRect;

        public MainPage_AddCustomTool()
        {
            this.InitializeComponent();

            // Listen for new ink or erase strokes to clean up selection UI.
            inkCanvas.InkPresenter.StrokeInput.StrokeStarted +=
                StrokeInput_StrokeStarted;
            inkCanvas.InkPresenter.StrokesErased +=
                InkPresenter_StrokesErased;
        }

        private void customToolButton_Click(object sender, RoutedEventArgs e)
        {
            // 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;
        }

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

        private void cutButton_Click(object sender, RoutedEventArgs e)
        {
            inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
            inkCanvas.InkPresenter.StrokeContainer.DeleteSelected();
            ClearSelection();
        }

        private void copyButton_Click(object sender, RoutedEventArgs e)
        {
            inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
        }

        private void pasteButton_Click(object sender, RoutedEventArgs e)
        {
            if (inkCanvas.InkPresenter.StrokeContainer.CanPasteFromClipboard())
            {
                inkCanvas.InkPresenter.StrokeContainer.PasteFromClipboard(
                    new Point(0, 0));
            }
            else
            {
                // Cannot paste from clipboard.
            }
        }

        // Clean up selection UI.
        private void ClearSelection()
        {
            var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
            foreach (var stroke in strokes)
            {
                stroke.Selected = false;
            }
            ClearBoundingRect();
        }

        private void ClearBoundingRect()
        {
            if (selectionCanvas.Children.Any())
            {
                selectionCanvas.Children.Clear();
                boundingRect = Rect.Empty;
            }
        }

        // Handle unprocessed pointer events from modifed 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();
        }

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

轉譯自訂的筆跡Custom ink rendering

根據預設,筆墨輸入是在低延遲背景執行緒上處理,並在其繪製期間轉譯為「濕潤」狀態。By default, ink input is processed on a low-latency background thread and rendered "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).

筆跡平台可讓您覆寫這個行為,並以自訂乾筆跡輸入完整自訂筆跡體驗。The ink platform enables you to override this behavior and completely customize the inking experience by custom drying the ink input.

如需自訂乾燥的詳細資訊,請參閱 UWP 應用程式中的手寫筆互動與 Windows InkFor more info on custom drying, see Pen interactions and Windows Ink in UWP apps.

注意

自訂乾燥與 InkToolbarCustom drying and the InkToolbar
如果您的 app 使用自訂的乾燥實作覆寫 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 samples

其他範例Other samples