Добавление InkToolbar в приложение Windows

Существует два разных элемента управления, которые упрощают ввод в приложениях Windows: InkCanvas и InkToolbar.

Элемент управления InkCanvas предоставляет основные функции Windows Ink. Используйте его для отрисовки ввода пера в виде росчерка рукописного ввода (с использованием параметров по умолчанию для цвета и толщины) или штриха удаления.

Сведения о реализации InkCanvas см. в разделе "Взаимодействие пера и стилуса" в приложениях Windows.

Как полностью прозрачное наложение, InkCanvas не предоставляет встроенный пользовательский интерфейс для задания свойств росчерка рукописного ввода. Если вы хотите изменить интерфейс рукописного ввода по умолчанию, позвольте пользователям задавать свойства росчерка рукописного ввода и поддерживать другие пользовательские функции рукописного ввода, у вас есть два варианта:

  • В коде программной части используйте базовый объект InkPresenter , привязанный к InkCanvas.

    API InkPresenter поддерживают обширную настройку интерфейса рукописного ввода. Дополнительные сведения см. в разделе "Перо" и "Перо" и "Перо" в приложениях Windows.

  • Привязите панель InkToolbar к InkCanvas. По умолчанию панель InkToolbar предоставляет настраиваемую и расширяемую коллекцию кнопок для активации функций, связанных с рукописным вводом, таких как размер штриха, цвет рукописного ввода и подсказка пера.

    Мы обсудим панель InkToolbar в этом разделе.

Важные API: класс InkCanvas, класс InkToolbar, класс InkPresenter, Windows.UI.Input.Inking

Панель inkToolbar по умолчанию

По умолчанию панель InkToolbar включает кнопки для рисования, стирания, выделения и отображения набора элементов (линейка или протяжатель). В зависимости от функции в всплывающих элементах предоставляются другие параметры и команды, такие как цвет рукописного ввода, толщина штриха, удаление всех рукописных фрагментов.

InkToolbar
Панель инструментов Windows Ink по умолчанию

Чтобы добавить inkToolbar по умолчанию в приложение рукописного ввода, просто поместите его на ту же страницу, что и InkCanvas, и свяжите два элемента управления.

  1. В MainPage.xaml объявите объект контейнера (в этом примере мы используем элемент управления Grid) для области рукописного ввода.
  2. Объявите объект InkCanvas в качестве дочернего объекта контейнера. (Размер InkCanvas наследуется от контейнера.)
  3. Объявите панель InkToolbar и используйте атрибут TargetInkCanvas для привязки его к InkCanvas.

Примечание.

Убедитесь, что панель InkToolbar объявлена после inkCanvas. В противном случае наложение 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, HorizontalAlignment и Orientation.

По умолчанию Явный
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
Доминирующий параметр руки

Этот параметр можно запросить с помощью свойства HandPreference Windows.UI.ViewManagement и задать значение HorizontalAlignment на основе возвращаемого значения. В этом примере мы найдите панель инструментов в левой части приложения для левого лица и справа для правого лица.

Скачайте этот пример из расположения панели инструментов Ink и образца ориентации (базовый)

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

Динамическое изменение состояния пользователя или устройства

Вы также можете использовать привязку для просмотра обновлений пользовательского интерфейса на основе изменений пользовательских настроек, параметров устройства или состояний устройства. В следующем примере мы развернем предыдущий пример и покажем, как динамически размещать панель инструментов рукописного ввода на основе ориентации устройства с помощью привязки, объекта ViewMOdel и интерфейса INotifyPropertyChanged .

Скачайте этот пример из расположения панели инструментов Ink и образца ориентации (динамический)

  1. Сначала добавим наш ViewModel.

    1. Добавьте в проект новую папку и вызовите ее ViewModels.

    2. Добавьте новый класс в папку ViewModels (например, мы назвали его InkToolbarSnippetHostViewModel.cs).

      Примечание.

      Мы использовали шаблон Singleton, так как нам нужен только один объект этого типа для жизни приложения.

    3. Добавьте using System.ComponentModel пространство имен в файл.

    4. Добавьте статическую переменную-член, называемую экземпляром, и статическое свойство только для чтения с именем Instance. Создайте закрытый конструктор, чтобы обеспечить доступ к этому классу только через свойство Instance.

      Примечание.

      Этот класс наследует от интерфейса 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-only) и PortraitLayout (ориентация устройства).

      Примечание.

      Свойство PortraitLayout является settable и включает определение для события 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. Теперь добавим несколько классов преобразователей в наш проект. Каждый класс содержит объект Convert, возвращающий значение выравнивания ( горизонтальноеили вертикальное выравнивание).

    1. Добавьте в проект новую папку и вызовите ее конвертерами.

    2. Добавьте два новых класса в папку Converters (например, мы называем их HorizontalAlignmentFromHandednessConverter.cs и VerticalAlignmentFromAppViewConverter.cs).

    3. Добавьте using Windows.UI.Xaml и using Windows.UI.Xaml.Data пространства имен в каждый файл.

    4. Измените каждый класс и public укажите, что он реализует интерфейс IValueConverter .

    5. Добавьте методы Convert и ConvertBack в каждый файл, как показано здесь (мы не удаляем метод ConvertBack).

      • 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. Задайте для представления одноэлементный экземпляр класса 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. Добавьте xmlns:converters="using:locationandorientation.Converters" в Page элемент для привязки к нашим преобразователям.

      <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 и привязывайте свойства VerticalAlignment и HorizontalAlignment 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, чтобы добавить наши PortraitLayout и LeftHandedLayout логические свойства в 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. Используйте объявление XAML для InkCanvas и InkToolbar из предыдущего примера.
  2. В коде программной части настройте обработчик для события Loadedобъекта InkToolbar .
/// <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.

    Передача объекта InkToolbarTool.Pencil в методе GetToolButton возвращает объект InkToolbarToolButton для InkToolbarPencilButton.

    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

Измените объявление XAML для 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" />
        <!-- 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. Используйте объявление XAML для 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>
  1. В коде за пределами настройте обработчик события загрузкиобъекта InkToolbar .
/// <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:

  • Настраиваемое перо — перо, для которого свойства цветовой палитры рукописного ввода и подсказки пера, такие как фигура, поворот и размер, определяются ведущим приложением.
  • Пользовательское средство — средство, отличное от пера, определенное ведущим приложением.
  • Настраиваемый переключатель — задает состояние определяемой приложением функции включено или выключено. При включении функция работает вместе с активным инструментом.

Обратите внимание, что невозможно изменить порядок отображения встроенных кнопок. Порядок отображения по умолчанию: перо Ballpoint, карандаш, выделение, ластик и линейка. Пользовательские пера добавляются к последнему перу по умолчанию, пользовательские кнопки инструментов добавляются между последней кнопкой пера и кнопкой ластика и настраиваемыми кнопками переключателя добавляются после кнопки линейки. (Пользовательские кнопки добавляются в указанном порядке.)

Пользовательское перо

Вы можете создать настраиваемое перо (активируемое с помощью пользовательской кнопки пера), где вы определяете цветовую палитру рукописного ввода и свойства подсказки пера, такие как фигура, поворот и размер.

Custom calligraphic pen button
Кнопка настраиваемого каллиграфического пера

В этом примере мы определим пользовательский перо с широким советом, который позволяет базовым каллиграфическим рукописным росчеркам. Мы также настраиваем коллекцию кистей в палитре, отображаемой на всплывающем элементе кнопки.

Поддерживающий код

Во-первых, мы определяем пользовательское перо и указываем атрибуты рисования в коде. Мы ссылаемся на этот пользовательский перо из XAML позже.

  1. Щелкните проект правой кнопкой мыши в Обозреватель решений и выберите "Добавить -> Создать элемент".
  2. В разделе Visual C# —> код добавьте новый файл класса и вызовите его каллиграфическийpen.cs.
  3. В файле Каллиграфия.cs замените стандартный блок с помощью следующего:
using System.Numerics;
using Windows.UI;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
  1. Укажите, что класс Каллиграфии Является производным от 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, и коллекцию кистей, поддерживаемую пользовательским пером (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. Затем мы добавим панель InkToolbar с дочерним элементом InkToolbarCustomPenButton .

Пользовательская кнопка пера включает две статические ссылки на ресурсы ресурсов страницы: CalligraphicPen и CalligraphicPenPalette.

Кроме того, мы указываем диапазон ползунка размера штриха (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, с значком и подсказкой , указанными в этом примере.

Как правило, сенсорный ввод используется для прямого управления объектом или пользовательским интерфейсом приложения. Чтобы продемонстрировать различия в поведении при включении рукописного ввода, мы помещаем inkCanvas в контейнер ScrollViewer и устанавливаем размеры ScrollViewer меньше, чем InkCanvas.

При запуске приложения поддерживается только перо и сенсорный ввод используется для сдвига или масштабирования поверхности рукописного ввода. При включении рукописного ввода сенсорного ввода поверхность рукописного ввода не может быть передвигаться или увеличиваться с помощью сенсорного ввода.

Примечание.

См. инструкции по элементам управления рукописным вводом в inkCanvas и UX InkToolbar. Следующие рекомендации относятся к этому примеру:

  • InkToolbar и рукописный ввод в целом лучше всего испытать через активное перо. Однако рукописный ввод с помощью мыши и сенсорного ввода может поддерживаться при необходимости в приложении.
  • При поддержке рукописного ввода с сенсорным вводом рекомендуется использовать значок "ED5F" из шрифта Segoe MLD2 Assets для кнопки переключателя с подсказкой "Сенсорное письмо".

XAML

  1. Сначала мы объявляем элемент InkToolbarCustomToggleButton (toggleButton ) с прослушивателем событий Click, указывающим обработчик событий (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>

Поддерживающий код

  1. В предыдущем фрагменте кода мы объявили прослушиватель событий Click (Toggle_Custom) на пользовательской кнопке переключателя для сенсорного ввода (toggleButton). Этот обработчик просто переключает поддержку CoreInputDeviceTypes.Touch через свойство InputDeviceTypes inkPresenter.

    Мы также указали значок для кнопки с помощью элемента SymbolIcon и расширения разметки {x:Bind}, который привязывает его к полю, определенному в файле кода программной части (TouchWritingIcon).

    Следующий фрагмент содержит обработчик событий Click и определение 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 можно настроить, чтобы оставить определенные входные данные без обработки, которые затем можно передать в приложение для пользовательской обработки.

В этом примере мы определяем настраиваемую кнопку средства, которая при выборе приводит к обработке последующих штрихов и отрисовке в виде фрагмента (дефисируемой строки) вместо рукописного ввода. Все росчерки рукописного ввода в границах области выбора имеют значение Selected.

Примечание.

См. инструкции по элементам управления рукописным вводом в inkCanvas и UX InkToolbar. Следующая рекомендация относится к этому примеру:

  • При выборе штриха рекомендуется использовать значок EF20 из шрифта Segoe MLD2 Assets для кнопки инструмента с подсказкой "Инструмент выбора".

XAML

  1. Во-первых, мы объявляем элемент InkToolbarCustomToolButton (customToolButton ) с прослушивателем событий Click, указывающим обработчик событий (customToolButton_Click), где настроен выбор штриха. (Мы также добавили набор кнопок для копирования, вырезания и вставки выделения штриха.)

  2. Мы также добавим элемент Canvas для рисования штриха выбора. Использование отдельного слоя для рисования росчерка выделения гарантирует , что 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. Затем мы обрабатываем событие Click для inkToolbarCustomToolButton в файле MainPage.xaml.cs code-behind.

    Этот обработчик настраивает InkPresenter для передачи необработанных входных данных в приложение.

    Более подробный шаг в этом коде. Сведения о сквозных входных данных см. в разделе расширенной обработки взаимодействия с пером и Windows Ink в приложениях Windows.

    Мы также указали значок для кнопки с помощью элемента SymbolIcon и расширения разметки {x:Bind}, которое привязывает его к полю, определенному в файле кода программной части (SelectIcon).

    Следующий фрагмент содержит обработчик событий Click и определение 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);
            }
        }
    }
}

Настраиваемая отрисовка рукописного ввода

По умолчанию входные данные рукописного ввода обрабатываются в фоновом потоке с низкой задержкой и отрисовывается "мокрой" по мере его рисования. После завершения росчерка (перо или палец снят, или кнопка мыши выпущена), штрих обрабатывается в потоке пользовательского интерфейса и отображается "сухой" на слое InkCanvas (над содержимым приложения и заменой мокрого рукописного ввода).

Платформа рукописного ввода позволяет переопределить это поведение и полностью настроить процесс рукописного ввода путем пользовательской сушки входных данных рукописного ввода.

Дополнительные сведения о пользовательской сушке см. в разделе "Взаимодействие с пером" и Windows Ink в приложениях Windows.

Примечание.

Настраиваемая сушка и панель InkToolbar
Если приложение переопределяет поведение отрисовки рукописного ввода по умолчанию для InkPresenter с помощью пользовательской реализации сушки, отрисованные росчерки рукописного ввода больше не доступны для панели InkToolbar, а встроенные команды удаления inkToolbar не работают должным образом. Чтобы обеспечить функциональность удаления, необходимо обрабатывать все события указателя, выполнять тестирование попаданий на каждом штрихе и переопределять встроенную команду "Удалить все рукописные вводы".

Примеры в статье

Другие примеры