Windows 앱에 InkToolbar 추가

Windows 앱에서 수동 입력을 간편하게 하는 두 가지 컨트롤은 InkCanvasInkToolbar입니다.

InkCanvas 컨트롤은 기본적인 Windows Ink 기능을 제공합니다. 이 컨트롤을 사용하여 펜 입력을 잉크 스트로크(색과 두께에 기본 설정 사용) 또는 지우기 스트로크로 렌더링할 수 있습니다.

InkCanvas 구현에 대한 자세한 내용은 Windows 앱에서 펜 및 스타일러스 조작을 참조하세요.

완전한 투명 오버레이인 InkCanvas는 잉크 스트로크 속성을 설정하기 위한 기본 제공 UI를 제공하지 않습니다. 기본 수동 입력 환경을 변경하고, 사용자가 잉크 스트로크 속성을 설정할 수 있게 하고, 다른 사용자 지정 수동 입력 기능을 지원하려는 경우 다음 두 가지 옵션이 있습니다.

  • 코드 숨김에서 InkCanvas에 바인딩된 기본 InkPresenter 개체를 사용합니다.

    InkPresenter API는 수동 입력 환경의 광범위한 사용자 지정을 지원합니다. 자세한 내용은 Windows 앱에서 펜 및 스타일러스 조작을 참조하세요.

  • InkToolbar를 InkCanvas에 바인딩합니다. 기본적으로 InkToolbar는 스트로크 크기, 잉크 색, 펜 팁 등의 잉크 관련 기능을 활성화하는 사용자 지정 가능하고 확장 가능한 단추 컬렉션을 제공합니다.

    이 항목에서는 InkToolbar에 대해 설명합니다.

중요 API: InkCanvas 클래스, InkToolbar 클래스, InkPresenter 클래스, Windows.UI.Input.Inking

기본 InkToolbar

기본적으로 InkToolbar에는 스텐실(눈금자 또는 각도기)을 그리고 지우고 강조하고 표시하는 단추가 포함되어 있습니다. 기능에 따라 잉크 색, 스트로크 두께, 모든 잉크 지우기 등의 기타 설정 및 명령이 플라이아웃에 제공됩니다.

InkToolbar
기본 Windows Ink 도구 모음

기본 InkToolbar를 수동 입력 앱에 추가하려면, InkCanvas와 동일한 페이지에 배치하고 두 개의 컨트롤을 연결합니다.

  1. MainPage.xaml에서 수동 입력 화면에 대한 컨테이너 개체(이 예제에서는 그리드 컨트롤 사용)를 선언합니다.
  2. InkCanvas 개체를 컨테이너의 자식으로 선언합니다. InkCanvas 크기는 컨테이너에서 상속됩니다.
  3. InkToolbar를 선언하고 TargetInkCanvas 특성을 사용하여 InkCanvas에 바인딩합니다.

참고

InkCanvas 뒤에 InkToolbar를 선언해야 합니다. 순서가 바뀌면 InkCanvas 오버레이로 인해 InkToolbar에 액세스할 수 없게 됩니다.

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

기본 사용자 지정

이 섹션에서는 몇 가지 기본적인 Windows Ink 도구 모음 사용자 지정 시나리오에 대해 설명합니다.

위치 및 방향 지정

앱에 잉크 도구 모음을 추가할 때는 도구 모음의 기본 위치 및 방향을 그대로 사용할 수도 있고 앱이나 사용자의 요구 사항에 맞게 이를 설정할 수도 있습니다.

XAML

VerticalAlignment, HorizontalAlignmentOrientation 속성을 통해 도구 모음의 위치 및 방향을 명시적으로 지정합니다.

기본값 명시적
Default ink toolbar location and orientation Explicit ink toolbar location and orientation
Windows Ink 도구 모음 기본 위치 및 방향 Windows Ink 도구 모음 명시적 위치 및 방향

다음은 XAML에서 잉크 도구 모음의 위치와 방향을 명시적으로 설정하는 코드입니다.

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

사용자의 기본 설정 또는 디바이스 상태에 따라 초기화

어떤 경우에는 사용자 기본 설정 또는 디바이스 상태에 따라 잉크 도구 모음의 위치와 방향을 설정하고자 할 수도 있습니다. 다음은 설정 > 디바이스 > 펜 & Windows Ink > 펜 > 글을 쓸 때 사용하는 손 선택에서 지정한 왼손 또는 오른손 쓰기 기본 설정에 따라 잉크 도구 모음의 위치와 방향을 설정하는 방법을 보여주는 예입니다.

Dominant hand setting
주요 손 설정

Windows.UI.ViewManagement의 HandPreference 속성을 통해 이 설정을 쿼리하고 반환된 값에 따라 HorizontalAlignment를 설정할 수 있습니다. 이 예에서는 왼손을 주로 사용하는 사용자를 위해 앱의 왼쪽에 그리고 오른손을 주로 사용하는 사용자를 위해 오른쪽에 도구 모음을 배치합니다.

잉크 도구 모음 위치 및 방향 샘플(기본)에서 이 샘플 다운로드

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

사용자 또는 디바이스 상태를 동적으로 조정

바인딩을 사용하여 사용자 기본 설정, 디바이스 설정 또는 디바이스 상태 변경 내용에 따른 UI 업데이트를 검색할 수도 있습니다. 다음 예에서는 이전 예제를 확장하여 바인딩, ViewMOdel 개체 및 INotifyPropertyChanged 인터페이스를 사용하여 디바이스 방향에 따라 잉크 도구 모음을 동적으로 위치 조정하는 방법을 보여줍니다.

잉크 도구 모음 위치 및 방향 샘플(동적)에서 이 샘플 다운로드

  1. 먼저, ViewModel을 추가하겠습니다.

    1. 프로젝트에 새 폴더를 추가하고 이름을 ViewModels라고 지정합니다.

    2. ViewModels 폴더에 새 클래스를 추가합니다(이 예에서는 InkToolbarSnippetHostViewModel.cs).

      참고

      응용 프로그램의 수명 기간 동안 이러한 유형의 개체는 하나만 필요하므로 싱글톤 패턴을 사용했습니다.

    3. 파일에 using System.ComponentModel 네임스페이스를 추가합니다.

    4. 인스턴스라는 고정적인 멤버 변수 및 인스턴스라는 고정적인 읽기 전용 속성을 추가합니다. 인스턴스 속성을 통해서만 이 클래스에 액세스할 수 있도록 하려면 생성자를 비공개 항목으로 설정합니다.

      참고

      이 클래스는 INotifyPropertyChanged 인터페이스로부터 상속됩니다. 이 인터페이스는 클라이언트, 일반적으로 바인딩 클라이언트에게 속성 값이 변경되었음을 알리는 데 사용됩니다. 이를 사용하여 디바이스 방향 변경을 처리하게 됩니다(이후 단계에서 이 코드를 확장하고 추가로 설명할 예정임).

      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. InkToolbarSnippetHostViewModel 클래스에 두 개의 부울 속성(LeftHandedLayout(이전의 XAML 전용 예제와 기능 동일) 및 PortraitLayout(디바이스의 방향))을 추가합니다.

      참고

      PortraitLayout 속성은 설정 가능하며 PropertyChanged 이벤트에 대한 정의를 포함합니다.

      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. 이제 프로젝트에 몇 가지 변환기 클래스를 추가하겠습니다. 각 클래스에는 맞춤 값(HorizontalAlignment 또는 VerticalAlignment 중 하나)을 반환하는 Convert 개체가 들어 있습니다.

    1. 프로젝트에 새 폴더를 추가하고 이름을 Converters라고 지정합니다.

    2. Converters 폴더에 두 가지 새로운 클래스(이 예의 경우 HorizontalAlignmentFromHandednessConverter.csVerticalAlignmentFromAppViewConverter.cs)를 추가합니다.

    3. 각 파일에 using Windows.UI.Xamlusing Windows.UI.Xaml.Data 네임스페이스를 추가합니다.

    4. 각 클래스를 public으로 변경하고 IValueConverter 인터페이스를 구현하도록 지정합니다.

    5. 그림처럼(ConvertBack 메서드는 구현 안 함) ConvertConvertBack 메서드를 각 파일에 추가합니다.

      • HorizontalAlignmentFromHandednessConverter가 잉크 도구 모음의 위치를 오른손 사용자에게는 앱의 오른쪽에, 왼손 사용자에게는 앱의 왼쪽에 배치합니다.
      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가 잉크 도구 모음의 위치를 세로 방향에서는 앱의 가운데에, 가로 방향에서는 앱의 위쪽에 배치합니다(이는 유용성 향상을 염두에 둔 데모 목적의 임의의 선택 사항일 뿐임).
      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 파일을 엽니다.

    1. using using locationandorientation.ViewModels를 네임스페이스 목록에 추가하여 ViewModel을 연결합니다.
    2. 네임스페이스 목록에 using Windows.UI.ViewManagement를 추가하여 디바이스 방향의 변경 내용을 수신 대기할 수 있도록 합니다.
    3. WindowSizeChangedEventHandler 코드를 추가합니다.
    4. 뷰의 DataContext를 InkToolbarSnippetHostViewModel 클래스의 싱글톤 인스턴스로 설정합니다.
    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 파일을 엽니다.

    1. 변환기 바인딩을 위해 Page 요소에 xmlns:converters="using:locationandorientation.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 요소를 추가하고 변환기 참조를 지정합니다.

      <Page.Resources>
          <converters:HorizontalAlignmentFromHandednessConverter x:Key="HorizontalAlignmentConverter"/>
          <converters:VerticalAlignmentFromAppViewConverter x:Key="VerticalAlignmentConverter"/>
      </Page.Resources>
      
    3. InkCanvas 및 InkToolbar 요소를 추가하고 InkToolbar의 VerticalAlignment 및 HorizontalAlignment 속성을 바인딩합니다.

      <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 부울 속성을 InkToolbarSnippetHostViewModel 클래스에 추가하고 해당 속성 값이 변경되면 PortraitLayout을 다시 바인딩할 수 있도록 지원합니다.

    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
    

이제 사용자의 주요 손 선호도에 맞게 조정되고 사용자 디바이스의 방향에 동적으로 응답하는 수동 입력 앱이 있어야 합니다.

선택되는 단추 지정

Pencil button selected at initialization
초기화 시 연필 단추가 선택된 Windows Ink 도구 모음

기본적으로 앱을 실행하고 도구 모음을 초기화하면 첫 번째(또는 맨 왼쪽) 단추가 선택됩니다. 기본 Windows Ink 도구 모음에서는 볼펜 단추입니다.

프레임워크에서 기본 제공 단추의 순서를 정의하기 때문에 첫 번째 단추가 기본적으로 활성화하려는 펜이나 도구가 아닐 수도 있습니다.

이 기본 동작을 재정의하고 도구 모음에서 선택되는 단추를 지정할 수 있습니다.

이 예제에서는 볼펜 대신 연필 단추를 선택하고 연필을 활성화하여 기본 도구 모음을 초기화합니다.

  1. 이전 예제의 InkCanvas 및 InkToolbar에 대한 XAML 선언을 사용합니다.
  2. 코드 숨김에서 InkToolbar 개체의 Loaded 이벤트 처리기를 설정합니다.
/// <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 이벤트 처리기에서 다음을 수행합니다.

    1. 기본 제공 InkToolbarPencilButton에 대한 참조를 가져옵니다.

    GetToolButton 메서드에 InkToolbarTool.Pencil 개체를 전달하면 InkToolbarPencilButton에 대한 InkToolbarToolButton 개체가 반환됩니다.

    1. ActiveTool을 이전 단계에서 반환된 개체로 설정합니다.
/// <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;
}

기본 제공 단추 지정

Specific buttons included at initialization
초기화 시 포함되는 특정 단추

설명했듯이 Windows Ink 도구 모음에는 기본 제공 단추 컬렉션이 포함되어 있습니다. 이러한 단추는 다음 순서(왼쪽에서 오른쪽)로 표시됩니다.

이 예제에서는 기본 제공 볼펜, 연필 및 지우개 단추만 포함하여 도구 모음을 초기화합니다.

XAML 또는 코드 숨김을 사용하여 이 작업을 수행할 수 있습니다.

XAML

첫 번째 예제의 InkCanvas 및 InkToolbar에 대한 XAML 선언을 수정합니다.

참고

단추는 여기서 지정된 순서가 아니라 프레임워크에서 정의된 순서대로 도구 모음에 추가됩니다.

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

코드 숨김

  1. 첫 번째 예제의 InkCanvas 및 InkToolbar에 대한 XAML 선언을 사용합니다.
<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 이벤트 처리기를 설정합니다.
/// <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"으로 설정합니다.
  2. 앱에 필요한 단추에 대한 개체 참조를 만듭니다. 여기서는 InkToolbarBallpointPenButton, InkToolbarPencilButton, InkToolbarEraserButton만 추가합니다.

참고

단추는 여기서 지정된 순서가 아니라 프레임워크에서 정의된 순서대로 도구 모음에 추가됩니다.

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

사용자 지정 단추 및 수동 입력 기능

InkToolbar를 통해 제공되는 단추 컬렉션(및 관련된 수동 입력 기능)을 사용자 지정하고 확장할 수 있습니다.

InkToolbar는 다음 두 가지 그룹의 단추 유형으로 이루어져 있습니다.

  1. 기본 제공 그리기, 지우기 및 강조 표시 단추를 포함하는 "도구" 단추 그룹. 사용자 지정 펜과 도구가 여기에 추가됩니다.

참고 기능 선택은 함께 사용할 수 없습니다.

  1. 기본 제공 눈금자 단추를 포함하는 "토글" 단추 그룹. 사용자 지정 토글이 여기에 추가됩니다.

참고 기능은 함께 사용할 수 있으며 다른 활성 도구와 동시에 사용할 수 있습니다.

애플리케이션 및 필요한 수동 입력 기능에 따라 사용자 지정 잉크 기능에 바인딩된 다음 단추를 InkToolbar에 추가할 수 있습니다.

  • 사용자 지정 펜 – 호스트 앱에서 잉크 색상표와 펜 팁 속성(예: 모양, 회전, 크기)이 정의된 펜입니다.
  • 사용자 지정 도구 - 호스트 앱에서 정의된 펜 이외의 도구입니다.
  • 사용자 지정 토글 - 앱에서 정의된 기능의 상태를 켜짐 또는 꺼짐으로 설정합니다. 켜진 경우 기능이 활성 도구와 함께 작동합니다.

참고 기본 제공 단추의 표시 순서는 변경할 수 없습니다. 기본 표시 순서는 볼펜, 연필, 형광펜, 지우개, 눈금자 순입니다. 사용자 지정 펜은 마지막 기본 펜 뒤에 추가되고, 사용자 지정 도구 단추는 마지막 펜 단추와 지우개 단추 사이에 추가되고, 사용자 지정 토글 단추는 눈금자 단추 뒤에 추가됩니다. 사용자 지정 단추는 지정된 순서대로 추가됩니다.

사용자 지정 펜

사용자 지정 펜(사용자 지정 펜 단추를 통해 활성화됨)을 만들어 잉크 색상표와 펜 팁 속성(예: 모양, 회전, 크기)을 정의합니다.

Custom calligraphic pen button
사용자 지정 붓글씨 펜 단추

이 예제에서는 기본 붓글씨 잉크 스트로크를 사용하도록 설정하는 광범위한 팁을 가진 사용자 지정 펜을 정의합니다. 단추 플라이아웃에 표시되는 색상표의 브러시 컬렉션도 사용자 지정합니다.

코드 숨김

먼저, 사용자 지정 펜을 정의하고 코드 숨김에서 그리기 특성을 지정합니다. 나중에서 XAML에서 이 사용자 지정 펜을 참조합니다.

  1. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 추가 -> 새 항목을 선택합니다.
  2. Visual C# -> 코드에서 새 클래스 파일을 추가하고 CalligraphicPen.cs를 호출합니다.
  3. Calligraphic.cs에서 기본 using 블록을 다음과 같이 바꿉니다.
using System.Numerics;
using Windows.UI;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
  1. CalligraphicPen 클래스가 InkToolbarCustomPen에서 파생되도록 지정합니다.
class CalligraphicPen : InkToolbarCustomPen
{
}
  1. CreateInkDrawingAttributesCore를 재정의하여 고유한 브러시 및 스트로크 크기를 지정합니다.
class CalligraphicPen : InkToolbarCustomPen
{
    protected override InkDrawingAttributes
      CreateInkDrawingAttributesCore(Brush brush, double strokeWidth)
    {
    }
}
  1. InkDrawingAttributes 개체를 만들고 펜 팁 모양, 팁 회전, 스트로크 크기잉크 색을 설정합니다.
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;
    }
}

XAML

MainPage.xaml에서 사용자 지정 펜에 필요한 참조를 추가합니다.

  1. CalligraphicPen.cs에서 정의된 사용자 지정 펜(CalligraphicPen) 및 사용자 지정 펜에서 지원하는 브러시 컬렉션(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를 추가합니다.

사용자 지정 펜 단추에는 페이지 리소스에서 선언된 두 개의 고정 리소스 참조 CalligraphicPenCalligraphicPenPalette가 포함되어 있습니다.

스트로크 크기 슬라이더의 범위(MinStrokeWidth, MaxStrokeWidth, SelectedStrokeWidth), 선택한 브러시(SelectedBrushIndex) 및 사용자 지정 펜 단추의 아이콘(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>

사용자 지정 토글

사용자 지정 토글(사용자 지정 토글 단추를 통해 활성화됨)을 만들어 앱에서 정의된 기능의 상태를 켜짐 또는 꺼짐으로 설정합니다. 켜진 경우 기능이 활성 도구와 함께 작동합니다.

이 예제에서는 터치식 입력을 사용한 수동 입력을 설정하는 사용자 지정 토글 단추를 정의합니다(기본적으로 터치 수동 입력은 사용할 수 없음).

참고

터치로 수동 입력을 지원해야 하는 경우 이 예제에 지정된 아이콘 및 도구 설명 과 함께 CustomToggleButton을 사용하여 사용하도록 설정하는 것이 좋습니다.

일반적으로 터치식 입력은 개체나 앱 UI의 직접 조작에 사용됩니다. 터치식 수동 입력을 사용하도록 설정한 경우 동작의 차이를 보여 주기 위해 InkCanvas를 ScrollViewer 컨테이너 내에 배치하고 ScrollViewer 치수를 InkCanvas보다 작게 설정해보겠습니다.

앱이 시작 될 때 펜 수동 입력만 지원되며 터치는 수동 입력 화면을 확대/축소하거나 이동하는 데 사용됩니다. 터치식 수동 입력을 사용하도록 설정하면 수동 입력 화면은 터치 입력을 통해 이동하거나 확대/축소되지 않습니다.

참고

InkCanvasInkToolbar UX 지침 관련 내용은 수동 입력 컨트롤을 참조하세요. 다음 권장 사항이 이 예제와 관련이 있습니다.

  • InkToolbar 및 일반적인 수동 입력은 활성 펜을 통해 가장 잘 작동합니다. 그러나 앱에 필요한 경우 마우스와 터치를 사용한 수동 입력을 지원할 수 있습니다.
  • 터치식 입력을 사용한 수동 입력을 지원하는 경우 “터치 쓰기” 도구 설명과 함께 "Segoe MLD2 자산" 글꼴의 "ED5F" 아이콘을 토글 단추에 사용하는 것이 좋습니다.

XAML

  1. 먼저, 이벤트 처리기(Toggle_Custom)를 지정하는 클릭 이벤트 수신기를 사용하여 InkToolbarCustomToggleButton 요소(toggleButton)를 선언합니다.
<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>

코드 숨김

  1. 이전 코드 조각에서터치식 수동 입력을 위한 사용자 지정 토글 단추(toggleButton)에 클릭 이벤트 수신기와 처리기(Toggle_Custom)를 선언했습니다. 이 처리기는 InkPresenter의 InputDeviceTypes 속성을 통해 CoreInputDeviceTypes.Touch에 대한 지원을 토글합니다.

    또한 SymbolIcon 요소 및 코드 숨김 파일 (TouchWritingIcon)에 정의된 필드로 바인딩하는 {x: Bind} 태그 확장을 사용하여 단추에 아이콘도 지정했습니다.

    다음 코드 조각에는 클릭 이벤트 처리기와 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;
            }
        }
    }
}

사용자 지정 도구

사용자 지정 도구 단추를 만들어 앱에서 정의한 펜 이외의 도구를 호출할 수 있습니다.

기본적으로 InkPresenter는 모든 입력을 잉크 스트로크 또는 지우기 스트로크로 처리합니다. 여기에는 펜 단추, 마우스 오른쪽 단추 등과 같은 보조 하드웨어 기능에 의해 수정되는 입력이 포함됩니다. 하지만 특정 입력을 처리되지 않은 상태로 두도록 InkPresenter를 구성할 수 있습니다. 이렇게 하면 사용자 지정 처리를 위해 처리되지 않은 입력이 앱에 전달될 수 있습니다.

이 예제에서는 선택할 경우 후속 스트로크가 처리되어 잉크 대신 선택 올가미(파선)로 렌더링되게 하는 사용자 지정 도구 단추를 정의하겠습니다. 선택 영역 경계 내에서 모든 잉크 스트로크는 선택됨으로 설정됩니다.

참고

InkCanvas 및 InkToolbar UX 지침 관련 내용은 수동 입력 컨트롤을 참조하세요. 다음 권장 사항은 이 예제와 관련이 있습니다.

  • 스트로크 선택을 입력할 때 “선택 도구” 도구 설명과 함께 “Segoe MLD2 자산 글꼴”의 EF20 아이콘을 도구 단추에 사용하는 것이 좋습니다.

XAML

  1. 먼저, 스트로크 선택이 구성된 이벤트 처리기(customToolButton_Click)를 지정하는 클릭 이벤트 수신기를 사용하여 InkToolbarCustomToolButton 요소(customToolButton)를 선언합니다. 스트로크 선택을 복사하고 잘라내고 붙여넣는 단추 집합도 추가했습니다.

  2. 또한 선택 스트로크를 그릴 수 있도록 캔버스 요소도 추가되었습니다. 별도의 계층을 사용하여 선택 스트로크를 그리면 InkCanvas와 콘텐츠는 원래 상태로 유지됩니다.

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

코드 숨김

  1. 그런 다음 MainPage.xaml.cs 코드 숨김 파일에서 InkToolbarCustomToolButton에 대한 클릭 이벤트를 처리합니다.

    이 처리기는 InkPresenter에서 처리되지 않은 입력을 앱으로 전달하도록 구성합니다.

    이 코드를 통한 자세한 단계는 Windows 앱에서 펜 조작 및 Windows Ink의 고급 처리를 위한 통과 입력 섹션을 참조하세요.

    또한 SymbolIcon 요소 및 코드 숨김 파일 (SelectIcon)에 정의된 필드로 바인딩하는 {x: Bind} 태그 확장을 사용하여 단추에 아이콘도 지정했습니다.

    다음 코드 조각에는 클릭 이벤트 처리기와 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);
            }
        }
    }
}

사용자 지정 잉크 렌더링

기본적으로 잉크 입력은 짧은 대기 시간의 백그라운드 스레드에서 처리되고 그릴 때 "젖은" 상태로 렌더링됩니다. 스트로크가 완료되면(펜 또는 손가락을 들거나 마우스 단추를 뗄 때) 스트로크는 UI 스레드에서 처리되고 InkCanvas 계층(응용 프로그램 콘텐츠 위 계층으로, 젖은 잉크를 대체함)에 대해 "건조" 상태로 렌더링됩니다.

잉크 플랫폼을 사용하여 이 동작을 재정의하고 잉크 입력의 사용자 지정 건조를 수행하여 수동 입력 환경을 완전히 사용자 지정할 수 있습니다.

사용자 지정 건조 상태에 대한 자세한 내용은 Windows 앱에서 펜 조작 및 Windows Ink를 참조하세요.

참고

사용자 지정 건조 및 InkToolbar
앱이 사용자 지정 건조를 구현하여 InkPresenter의 기본 잉크 렌더링 동작을 재정의하는 경우 렌더링된 잉크 스트로크를 InkToolbar에서 더 이상 사용할 수 없고 InkToolbar의 기본 제공 지우기 명령이 예상대로 작동하지 않습니다. 지우기 기능을 제공하려면 모든 포인터 이벤트를 처리하고, 각 스트로크에 대해 적중 횟수 테스트를 수행하고, 기본 제공 "모든 잉크 지우기" 명령을 재정의해야 합니다.

토픽 샘플

기타 샘플