Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Существует два разных элемента управления, которые упрощают рисование в приложениях для 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 по умолчанию
По умолчанию панель инструментов Ink включает кнопки для рисования, стирания, выделения и отображения чертежных инструментов (линейка или транспортир). В зависимости от функции в всплывающем меню предоставляются другие параметры и команды, такие как цвет рукописного ввода, толщина штриха, удаление всех рукописных данных.
Панель инструментов по умолчанию Windows Ink
Чтобы добавить InkToolbar по умолчанию в приложение для черчения, просто разместите его на той же странице, что и InkCanvas, и свяжите два элемента управления.
- В MainPage.xaml объявите объект контейнера (в этом примере мы используем элемент управления Grid) для области рукописного ввода.
- Определите объект InkCanvas как дочерний элемент контейнера. (Размер InkCanvas наследуется от контейнера.)
- Объявите 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.
Укажите расположение и ориентацию
При добавлении панели инструментов рукописного ввода в приложение можно принять расположение и ориентацию панели инструментов по умолчанию или задать их в соответствии с вашим приложением или пользователем.
XAML
Явным образом укажите расположение и ориентацию панели инструментов с помощью его свойств VerticalAlignment, HorizontalAlignment и Orientation.
| По умолчанию | Явный |
|---|---|
|
|
| Расположение и ориентация по умолчанию панели инструментов Windows Ink | Панель инструментов Windows Ink: явное расположение и ориентация |
Ниже приведен код для явного задания расположения и ориентации панели инструментов рукописного ввода в XAML.
<InkToolbar x:Name="inkToolbar"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Orientation="Vertical"
TargetInkCanvas="{x:Bind inkCanvas}" />
Инициализация на основе настроек пользователя или состояния устройства
В некоторых случаях может потребоваться задать расположение и ориентацию панели инструментов рукописного ввода на основе предпочтений пользователя или состояния устройства. В следующем примере показано, как установить расположение и ориентацию панели инструментов рукописного ввода на основе предпочтений письма левой или правой рукой, указанных в Settings > Devices > Перо и Windows Рукопись > Перо > Выберите, какой рукой вы пишете.
Настройка доминирующей руки
Этот параметр можно запросить с помощью свойства 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 (динамический)
Сначала добавим наш ViewModel.
Добавьте в проект новую папку и вызовите ее ViewModels.
Добавьте новый класс в папку ViewModels (в этом примере мы назвали ее InkToolbarSnippetHostViewModel.cs).
Замечание
Мы использовали шаблон Singleton , так как нам нужен только один объект этого типа для жизни приложения.
Добавьте
using System.ComponentModelпространство имен в файл.Добавьте статическую переменную-член с именем instance и статическое свойство только для чтения с именем 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() { } }Добавьте два логических свойства в класс InkToolbarSnippetHostViewModel: LeftHandedLayout (та же функциональность, что и предыдущий пример xaml-only) и 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")); } }
Теперь добавим несколько классов преобразователей в наш проект. Каждый класс содержит объект Convert, возвращающий значение выравнивания ( горизонтальное или вертикальное выравнивание).
Добавьте в проект новую папку и назовите ее Converters.
Добавьте два новых класса в папку Конвертеров (в этом примере мы называем их HorizontalAlignmentFromHandednessConverter.cs и VerticalAlignmentFromAppViewConverter.cs).
Добавьте пространства имен
using Windows.UI.Xamlиusing Windows.UI.Xaml.Dataв каждый файл.Поменяйте каждый класс на
public, чтобы указать, что он реализует интерфейс IValueConverter.Добавьте методы 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(); } } }
Теперь откройте файл MainPage.xaml.cs.
- Добавьте
using using locationandorientation.ViewModelsв список пространств имен, чтобы связать наш ViewModel. - Добавьте
using Windows.UI.ViewManagementв список пространств имен, чтобы разрешить прослушивание за изменениями ориентации устройства. - Добавьте код WindowSizeChangedEventHandler .
- Задайте 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; } } }- Добавьте
Затем откройте файл MainPage.xaml.
Добавьте
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">Добавьте элемент
PageResourcesи укажите ссылки на наши преобразователи.<Page.Resources> <converters:HorizontalAlignmentFromHandednessConverter x:Key="HorizontalAlignmentConverter"/> <converters:VerticalAlignmentFromAppViewConverter x:Key="VerticalAlignmentConverter"/> </Page.Resources>Добавьте элементы 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}" />
Вернитесь к файлу 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
Теперь у вас должно быть приложение рукописного ввода, которое адаптируется как к доминирующим предпочтениям руки пользователя, так и динамически реагирует на ориентацию устройства пользователя.
Укажите выбранную кнопку
Панель Windows Ink с кнопкой карандаша, выбранной при инициализации
По умолчанию первая (или левая) кнопка выбирается при запуске приложения и инициализируется панель инструментов. На панели инструментов Windows Ink по умолчанию это кнопка шариковой ручки.
Так как платформа определяет порядок встроенных кнопок, первая кнопка может не быть пером или инструментом, который вы хотите активировать по умолчанию.
Это поведение по умолчанию можно переопределить и указать выбранную кнопку на панели инструментов.
В этом примере мы инициализируем панель инструментов по умолчанию с выбранной кнопкой карандаша и активированным карандашом (вместо пера с шариком).
- Используйте объявление XAML для InkCanvas и InkToolbar из предыдущего примера.
- В коде программной части настройте обработчик для события 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;
}
В обработчике события Loaded :
- Получите ссылку на встроенный InkToolbarPencilButton.
Передача объекта InkToolbarTool.Pencil в методе GetToolButton возвращает объект InkToolbarToolButton для InkToolbarPencilButton.
- Задайте параметру 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;
}
Укажите встроенные кнопки
Определенные кнопки, включенные при инициализации
Как уже упоминалось, панель инструментов Windows рукописного ввода включает коллекцию встроенных кнопок по умолчанию. Эти кнопки отображаются в следующем порядке (слева направо):
- Кнопка чертежной ручки в панели инструментов
- InkToolbarPencilButton
- Кнопка выделителя на панели инструментов
- InkToolbarEraserButton
- InkToolbarRulerButton
В этом примере мы инициализируем панель инструментов только с встроенными кнопками шариковой ручки, карандаша и ластика.
Это можно сделать с помощью XAML или кода программной части.
XAML
Измените объявление XAML для InkCanvas и InkToolbar из первого примера.
- Добавьте атрибут InitialControls и задайте для него значение None. Это очищает коллекцию встроенных кнопок по умолчанию.
- Добавьте определенные кнопки InkToolbar, необходимые вашему приложению. Здесь мы добавим только InkToolbarBallpointPenButton, InkToolbarPencilButton и InkToolbarEraserButton.
Замечание
Кнопки добавляются на панель инструментов в порядке, определенном платформой, а не в указанном здесь порядке.
<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>
Код за кадром
- Используйте объявление 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>
- В коде за пределами настройте обработчик события загрузки объекта 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;
}
- Установите InitialControls в "None".
- Создайте ссылки на объекты для кнопок, необходимых приложению. Здесь мы добавим только InkToolbarBallpointPenButton, InkToolbarPencilButton и InkToolbarEraserButton.
Замечание
Кнопки добавляются на панель инструментов в порядке, определенном платформой, а не в указанном здесь порядке.
- Добавьте кнопки в панель 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 состоит из двух отдельных групп типов кнопок:
- Группа кнопок "инструменты", содержащая встроенные кнопки для рисования, стирания и выделения. Здесь добавлены пользовательские ручки и инструменты.
Заметка Выбор компонентов является взаимоисключающим.
- Группа кнопок "переключения", содержащая встроенную кнопку линейки. Здесь добавляются пользовательские переключатели.
Заметка Функции не являются взаимоисключающими и могут использоваться параллельно с другими активными инструментами.
В зависимости от приложения и необходимых функций рукописного ввода можно добавить любую из следующих кнопок (привязанных к пользовательским функциям рукописного ввода) на панель InkToolbar:
- Настраиваемое перо — это перо, для которого цветовая палитра чернил и свойства наконечника, такие как форма, вращение и размер, определяются приложением-хостом.
- Пользовательский инструмент — это инструмент, отличный от пера, определяемый ведущим приложением.
- Настраиваемый переключатель — задает состояние определяемой приложением функции включено или выключено. При включении функция работает вместе с активным инструментом.
Заметка Невозможно изменить порядок отображения встроенных кнопок. Порядок отображения по умолчанию: шариковая ручка, карандаш, текстовыделитель, ластик и линейка. Пользовательские пера добавляются к последнему перу по умолчанию, пользовательские кнопки инструментов добавляются между последней кнопкой пера и кнопкой ластика, и пользовательские кнопки переключателей добавляются после кнопки линейки. (Пользовательские кнопки добавляются в указанном порядке.)
Настраиваемое перо
Вы можете создать настраиваемое перо (активируемое с помощью пользовательской кнопки пера), где вы определяете цветовую палитру чернил и свойства пера, такие как форма, поворот и размер.
Кнопка настраиваемого каллиграфического пера
В этом примере мы определим пользовательское перо с широким наконечником, который позволяет базовые каллиграфические мазки. Мы также настраиваем коллекцию кистей в палитре, отображаемой на всплывающем элементе кнопки.
Код за кадром
Во-первых, мы определяем пользовательское перо и указываем атрибуты рисования в коде. Мы ссылаемся на это пользовательское перо в XAML позже.
- Щелкните проект правой кнопкой мыши в Solution Explorer и выберите "Добавить -> Новый элемент".
- В разделе Visual C# —> код добавьте новый файл класса и вызовите его CalligraphicPen.cs.
- В Calligraphic.cs замените значение по умолчанию с помощью блока следующим образом:
using System.Numerics;
using Windows.UI;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
- Укажите, что класс CalligraphicPen является производным от InkToolbarCustomPen.
class CalligraphicPen : InkToolbarCustomPen
{
}
- Переопределите CreateInkDrawingAttributesCore , чтобы указать собственный размер кисти и штриха.
class CalligraphicPen : InkToolbarCustomPen
{
protected override InkDrawingAttributes
CreateInkDrawingAttributesCore(Brush brush, double strokeWidth)
{
}
}
- Создайте объект 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.
- Мы объявляем словарь ресурсов локальной страницы, который создает ссылку на настраиваемое перо (
), определенное в CalligraphicPen.cs, и коллекцию кистей , поддерживаемую пользовательским пером ( ).
<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>
- Затем мы добавим панель 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.
При запуске приложения поддерживается только рисование пером, а сенсорное управление используется для перемещения или масштабирования поверхности ввода. При включении сенсорного рукописного ввода поверхность не может перемещаться или увеличиваться с помощью сенсорного касания.
Замечание
Посмотрите элементы управления Inking как для InkCanvas, так и для InkToolbar в рекомендациях по UX. Следующие рекомендации относятся к этому примеру:
- InkToolbar и рукописный ввод в целом лучше всего использовать с активным пером. Однако рукописный ввод с помощью мыши и сенсорного ввода может поддерживаться при необходимости в приложении.
- Если вы поддерживаете рукописный ввод с помощью сенсорного ввода, мы рекомендуем использовать значок "ED5F" из шрифта "Segoe MLD2 Assets" для кнопки переключателя с подсказкой "Сенсорное письмо".
XAML
- Сначала мы объявляем элемент 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>
Код за кадром
В предыдущем фрагменте кода мы объявили прослушиватель и обработчик события 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
Во-первых, мы объявляем элемент InkToolbarCustomToolButton (customToolButton) с обработчиком события Click, указывающим на обработчик событий (customToolButton_Click), где осуществляется настройка выбора штриха. Мы также добавили набор кнопок для копирования, вырезания и вставки выделенного штриха.
Мы также добавим элемент 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>
Код за кадром
Затем мы обрабатываем событие Click для InkToolbarCustomToolButton в файле кода MainPage.xaml.cs.
Этот обработчик настраивает 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 modified input.
// The input is used to provide selection functionality.
// Selection UI is drawn on a canvas under the InkCanvas.
private void UnprocessedInput_PointerPressed(
InkUnprocessedInput sender, PointerEventArgs args)
{
// Initialize a selection lasso.
lasso = new Polyline()
{
Stroke = new SolidColorBrush(Windows.UI.Colors.Blue),
StrokeThickness = 1,
StrokeDashArray = new DoubleCollection() { 5, 2 },
};
lasso.Points.Add(args.CurrentPoint.RawPosition);
selectionCanvas.Children.Add(lasso);
}
private void UnprocessedInput_PointerMoved(
InkUnprocessedInput sender, PointerEventArgs args)
{
// Add a point to the lasso Polyline object.
lasso.Points.Add(args.CurrentPoint.RawPosition);
}
private void UnprocessedInput_PointerReleased(
InkUnprocessedInput sender, PointerEventArgs args)
{
// Add the final point to the Polyline object and
// select strokes within the lasso area.
// Draw a bounding box on the selection canvas
// around the selected ink strokes.
lasso.Points.Add(args.CurrentPoint.RawPosition);
boundingRect =
inkCanvas.InkPresenter.StrokeContainer.SelectWithPolyLine(
lasso.Points);
DrawBoundingRect();
}
// 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.
Замечание
Настраиваемая сушка и панель инструментов для работы с чернилами
Если приложение переопределяет поведение отрисовки рукописного ввода по умолчанию InkPresenter с пользовательской реализацией сушки, отрисованные росчерки рукописного ввода больше не доступны в inkToolbar и встроенные команды очистки InkToolbar не работают должным образом. Чтобы обеспечить функциональность стирания, необходимо обрабатывать все события указателя, выполнять тестирование попаданий на каждом штрихе и переопределять встроенную команду "Стереть все рукописные вводы".
Связанные статьи
Примеры тем
- Пример расположения и ориентации панели инструментов рукописного ввода (базовый)
- Пример расположения и ориентации панели инструментов рукописного ввода (динамический)
Другие примеры
- Простой чернильный пример (C#/C++)
- Комплексный пример использования чернил (C++)
- пример Ink (JavaScript)
- Get Started Tutorial: поддержка рукописного ввода в приложении Windows
- пример раскраски Coloring book sample
- Пример заметок для семьи