Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Há dois controles diferentes que facilitam a escrita à tinta em aplicativos do Windows: InkCanvas e InkToolbar.
O controle InkCanvas fornece funcionalidade básica do Windows Ink. Use-o para renderizar a entrada de caneta em formato de traço de tinta (usando configurações padrão para cor e espessura) ou traço de borracha.
Para obter detalhes da implementação do InkCanvas, consulte as interações de caneta e estilete em aplicativos do Windows.
Como uma sobreposição completamente transparente, o InkCanvas não fornece nenhuma interface de usuário incorporada para definir propriedades de traço de tinta. Se você quiser alterar a experiência de escrita à tinta padrão, permitir que os usuários definam propriedades de traço de tinta e dêem suporte a outros recursos de escrita à tinta personalizados, você terá duas opções:
No code-behind, utilize o objeto InkPresenter subjacente associado ao InkCanvas.
As APIs inkPresenter dão suporte à personalização abrangente da experiência de escrita à tinta. Para obter mais detalhes, consulte As interações de caneta e caneta em aplicativos do Windows.
Associe um InkToolbar ao InkCanvas. Por padrão, o InkToolbar fornece uma coleção personalizável e extensível de botões para ativar recursos relacionados à tinta, como tamanho do traço, cor da tinta e ponta da caneta.
Discutiremos o InkToolbar neste tópico.
APIs importantes: classe InkCanvas, classe InkToolbar, classe InkPresenter, Windows.UI.Input.Inking
InkToolbar padrão
Por padrão, o InkToolbar inclui botões para desenhar, apagar, realçar e exibir um estêncil (régua ou prolongador). Dependendo do recurso, outras configurações e comandos, como a cor da tinta, a espessura do traço, apagar toda a tinta, são fornecidos em um menu suspenso.
Barra de ferramentas padrão do Windows Ink
Para adicionar um InkToolbar padrão a um aplicativo de escrita à tinta, basta colocá-lo na mesma página que o InkCanvas e associar os dois controles.
- Em MainPage.xaml, declare um objeto de contêiner (para este exemplo, usamos um controle Grid) para a superfície de escrita à tinta.
- Declare um objeto InkCanvas como filho do contêiner. (O tamanho do InkCanvas é herdado do contêiner.)
- Declare um InkToolbar e use o atributo TargetInkCanvas para associá-lo ao InkCanvas.
Observação
Verifique se o InkToolbar é declarado após o InkCanvas. Caso contrário, a sobreposição InkCanvas torna o InkToolbar inacessível.
<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>
Personalização básica
Nesta seção, abordaremos alguns cenários básicos de personalização da barra de ferramentas do Windows Ink.
Especificar localização e orientação
Ao adicionar uma barra de ferramentas de tinta ao seu aplicativo, você pode aceitar o local padrão e a orientação da barra de ferramentas ou defini-las conforme exigido pelo seu aplicativo ou usuário.
XAML
Especifique explicitamente o local e a orientação da barra de ferramentas por meio de suas propriedades VerticalAlignment, HorizontalAlignment e Orientation .
| Padrão | Explicit |
|---|---|
|
|
| Localização e orientação padrão da barra de ferramentas do Windows Ink | Localização e orientação explícitas da barra de ferramentas do Windows Ink |
Este é o código para definir explicitamente o local e a orientação da barra de ferramentas de tinta em XAML.
<InkToolbar x:Name="inkToolbar"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Orientation="Vertical"
TargetInkCanvas="{x:Bind inkCanvas}" />
Inicializar com base nas preferências do usuário ou no estado do dispositivo
Em alguns casos, talvez você queira definir o local e a orientação da barra de ferramentas de tinta com base na preferência do usuário ou no estado do dispositivo. O exemplo a seguir demonstra como definir a localização e a orientação da barra de ferramentas de tinta com base nas preferências de escrita para destros ou canhotos especificadas em Configurações > Dispositivos > Caneta e Windows Ink > Caneta > Escolha com qual mão você escreve.
Configuração de mão dominante
Você pode consultar essa configuração por meio da propriedade HandPreference de Windows.UI.ViewManagement e definir a HorizontalAlignment com base no valor retornado. Neste exemplo, localizamos a barra de ferramentas no lado esquerdo do aplicativo para uma pessoa canhota e no lado direito para uma pessoa destra.
Baixe este exemplo do exemplo de localização e orientação da barra de ferramentas do Ink (básico)
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;
}
Ajustar dinamicamente ao estado do usuário ou do dispositivo
Você também pode usar a associação para cuidar das atualizações da interface do usuário com base em alterações nas preferências do usuário, nas configurações do dispositivo ou nos estados do dispositivo. No exemplo a seguir, expandimos o exemplo anterior e mostramos como posicionar dinamicamente a barra de ferramentas de tinta com base na orientação do dispositivo usando associação, um objeto ViewMOdel e a interface INotifyPropertyChanged .
Baixe este exemplo de Exemplo dinâmico de posição e orientação da barra de ferramentas Ink
Primeiro, vamos adicionar nosso ViewModel.
Adicione uma nova pasta ao projeto e chame-a de ViewModels.
Adicione uma nova classe à pasta ViewModels (para este exemplo, chamamos de InkToolbarSnippetHostViewModel.cs).
Observação
Usamos o padrão Singleton , pois só precisamos de um objeto desse tipo para a vida útil do aplicativo
Adicione
using System.ComponentModelo namespace ao arquivo.Adicione uma variável de membro estático chamada instância e uma propriedade somente leitura estática chamada Instância. Torne o construtor privado para garantir que essa classe só possa ser acessada por meio da propriedade Instance.
Observação
Essa classe herda da interface INotifyPropertyChanged, que é usada para notificar clientes, tipicamente clientes vinculados, que um valor de propriedade foi alterado. Usaremos isso para lidar com as alterações na orientação do dispositivo (expandiremos esse código e explicaremos mais detalhadamente em uma etapa posterior).
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() { } }Adicione duas propriedades bool à classe InkToolbarSnippetHostViewModel: LeftHandedLayout (mesma funcionalidade do exemplo somente XAML anterior) e PortraitLayout (orientação do dispositivo).
Observação
A propriedade PortraitLayout é configurável e inclui a definição para o evento 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")); } }
Agora, vamos adicionar algumas classes de conversor ao nosso projeto. Cada classe contém um objeto Convert que retorna um valor de alinhamento ( HorizontalAlignment ou VerticalAlignment).
Adicione uma nova pasta ao projeto e chame-a de Conversores.
Adicione duas novas classes na pasta Conversores (neste exemplo, chamamos HorizontalAlignmentFromHandednessConverter.cs e VerticalAlignmentFromAppViewConverter.cs).
Adicionar
using Windows.UI.Xamleusing Windows.UI.Xaml.Datanamespaces a cada arquivo.Altere cada classe para
publice especifique que ela implementa a interface IValueConverter .Adicione os métodos Convert e ConvertBack a cada arquivo, conforme mostrado aqui (deixamos o método ConvertBack não implementado).
- HorizontalAlignmentFromHandednessConverter posiciona a barra de ferramentas de tinta no lado direito do aplicativo para usuários destros e no lado esquerdo do aplicativo para usuários canhotos.
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 posiciona a toolbar de tinta no centro do aplicativo na orientação retrato e na parte superior na orientação paisagem (embora a intenção seja melhorar a usabilidade, essa é apenas uma escolha arbitrária para fins de demonstração).
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(); } } }
Agora, abra o arquivo MainPage.xaml.cs.
- Adicione
using using locationandorientation.ViewModelsà lista de namespaces para associar nosso ViewModel. - Adicione
using Windows.UI.ViewManagementà lista de namespaces para habilitar a escuta de alterações na orientação do dispositivo. - Adicione o código WindowSizeChangedEventHandler .
- Defina o DataContext para a exibição como a instância singleton da classe 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; } } }- Adicione
Em seguida, abra o arquivo MainPage.xaml.
Adicione
xmlns:converters="using:locationandorientation.Converters"aoPageelemento para associação aos nossos conversores.<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">Adicione um
PageResourceselemento e especifique referências aos nossos conversores.<Page.Resources> <converters:HorizontalAlignmentFromHandednessConverter x:Key="HorizontalAlignmentConverter"/> <converters:VerticalAlignmentFromAppViewConverter x:Key="VerticalAlignmentConverter"/> </Page.Resources>Adicione os elementos InkCanvas e InkToolbar e vincule as propriedades VerticalAlignment e HorizontalAlignment do 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}" />
Retorne ao arquivo InkToolbarSnippetHostViewModel.cs para adicionar as propriedades bool
PortraitLayouteLeftHandedLayoutà classe, juntamente com o suporte para revinculação doPortraitLayoutquando o valor dessa propriedade for alterado.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
Agora você deve ter um aplicativo de escrita à tinta que se adapte à preferência de mão dominante do usuário e responda dinamicamente à orientação do dispositivo do usuário.
Especificar o botão selecionado
Barra de ferramentas do Windows Ink com botão lápis selecionado na inicialização
Por padrão, o primeiro botão (ou mais à esquerda) é selecionado quando o aplicativo é iniciado e a barra de ferramentas é inicializada. Na barra de ferramentas padrão do Windows Ink, este é o botão de caneta esferográfica.
Como a estrutura define a ordem dos botões internos, o primeiro botão pode não ser a caneta ou a ferramenta que você deseja ativar por padrão.
Você pode substituir esse comportamento padrão e especificar o botão selecionado na barra de ferramentas.
Para este exemplo, inicializamos a barra de ferramentas padrão com o botão lápis selecionado e o lápis ativado (em vez da caneta esferográfica).
- Use a declaração XAML para InkCanvas e InkToolbar do exemplo anterior.
- No code-behind, configure um manipulador para o evento Loaded do objeto 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;
}
No manipulador do evento Loaded :
- Obtenha uma referência ao botão interno InkToolbarPencilButton.
Passar um objeto InkToolbarTool.Pencil no método GetToolButton retorna um objeto InkToolbarToolButton para o InkToolbarPencilButton.
- Defina ActiveTool como o objeto retornado na etapa anterior.
/// <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;
}
Especificar os botões internos
Botões específicos incluídos na inicialização
Conforme mencionado, a barra de ferramentas do Windows Ink inclui uma coleção de botões internos padrão. Esses botões são exibidos na seguinte ordem (da esquerda para a direita):
- InkToolbarBallpointPenButton
- InkToolbarPencilButton
- InkToolbarHighlighterButton
- InkToolbarEraserButton
- InkToolbarRulerButton
Para este exemplo, inicializamos a barra de ferramentas com apenas os botões de caneta esferográfica, lápis e borracha incorporados.
Você pode fazer isso usando XAML ou code-behind.
XAML
Modifique a declaração XAML para InkCanvas e InkToolbar do primeiro exemplo.
- Adicione um atributo InitialControls e defina seu valor como "Nenhum". Isso limpa a coleção padrão de botões embutidos.
- Adicione os botões específicos do InkToolbar exigidos pelo aplicativo. Aqui, adicionamos somente InkToolbarBallpointPenButton, InkToolbarPencilButton e InkToolbarEraserButton.
Observação
Os botões são adicionados à barra de ferramentas na ordem definida pela estrutura, não na ordem especificada aqui.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
<TextBlock x:Name="Header"
Text="Basic ink sample"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10,0,0,0" />
</StackPanel>
<Grid Grid.Row="1">
<Image Source="Assets\StoreLogo.png" />
<!-- Clear the default InkToolbar buttons by setting InitialControls to None. -->
<!-- Set the active tool to the pencil button. -->
<InkCanvas x:Name="inkCanvas" />
<InkToolbar x:Name="inkToolbar"
VerticalAlignment="Top"
TargetInkCanvas="{x:Bind inkCanvas}"
InitialControls="None">
<!--
Add only the ballpoint pen, pencil, and eraser.
Note that the buttons are added to the toolbar in the order
defined by the framework, not the order we specify here.
-->
<InkToolbarEraserButton />
<InkToolbarBallpointPenButton />
<InkToolbarPencilButton/>
</InkToolbar>
</Grid>
</Grid>
Code-behind
- Use a declaração XAML para InkCanvas e InkToolbar do primeiro exemplo.
<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>
- No code-behind, configure um handler para o evento Loading do objeto 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;
}
- Defina InitialControls como "None".
- Crie referências de objeto para os botões exigidos pelo aplicativo. Aqui, adicionamos apenas InkToolbarBallpointPenButton, InkToolbarPencilButton e InkToolbarEraserButton.
Observação
Os botões são adicionados à barra de ferramentas na ordem definida pela estrutura, não na ordem especificada aqui.
- Adicione os botões ao 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);
}
Botões personalizados e recursos de escrita à tinta
Você pode personalizar e estender a coleção de botões (e recursos de escrita à tinta associados) fornecidos por meio do InkToolbar.
O InkToolbar consiste em dois grupos distintos de tipos de botões:
- Um grupo de botões de “ferramenta” que contém os botões internos de desenhar, apagar e realçar. Ferramentas e canetas personalizadas são adicionadas aqui.
Nota A seleção de recursos é mutuamente exclusiva.
- Um grupo de botões de “alternância” que contém o botão de régua interno. Alternâncias personalizadas são adicionadas aqui.
Nota Os recursos não são mutuamente exclusivos e podem ser usados simultaneamente com outras ferramentas ativas.
Dependendo do aplicativo e da funcionalidade de escrita à tinta necessária, você pode adicionar qualquer um dos seguintes botões (vinculados aos recursos de escrita à tinta personalizados) ao InkToolbar:
- Caneta personalizada: uma caneta para a qual o aplicativo host define a paleta de cores de tinta e as propriedades da ponta da caneta, como forma, rotação e tamanho.
- Ferramenta personalizada: uma ferramenta sem caneta que o aplicativo host define.
- Alternância personalizada: define o estado de um recurso que o aplicativo define como ativado ou desativado. Quando ativado, o recurso funciona em conjunto com a ferramenta ativa.
Nota Não é possível alterar a ordem de exibição dos botões internos. A ordem de exibição padrão é: caneta esferográfica, lápis, marca-texto, borracha e régua. As canetas personalizadas são acrescentadas à última caneta padrão, os botões de ferramenta personalizados são adicionados entre o último botão de caneta e o botão de borracha e os botões de alternância personalizados são adicionados após o botão da régua. (Os botões personalizados são adicionados na ordem em que são especificados.)
Caneta personalizada
Você pode criar uma caneta personalizada (ativada por meio de um botão de caneta personalizada) em que você define as propriedades de paleta de cores de tinta e ponta de caneta, como forma, rotação e tamanho.
Botão de caneta caligráfica personalizado
Para este exemplo, definimos uma caneta personalizada com uma ponta ampla que permite traços básicos de tinta caligráfica. Também personalizamos a coleção de pincéis na paleta exibida no submenu do botão.
Code-behind
Primeiro, definimos nossa caneta personalizada e especificamos os atributos de desenho no code-behind. Fazemos referência a essa caneta personalizada de XAML posteriormente.
- Clique com o botão direito do mouse no projeto no Gerenciador de Soluções e selecione Adicionar –> Novo item.
- No Visual C# –> Código, adicione um novo arquivo de classe e chame-o de CalligraphicPen.cs.
- Em Calligraphic.cs, substitua o bloco de uso padrão pelo seguinte:
using System.Numerics;
using Windows.UI;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
- Especifique que a classe CalligraphicPen é derivada de InkToolbarCustomPen.
class CalligraphicPen : InkToolbarCustomPen
{
}
- Sobrescreva CreateInkDrawingAttributesCore para especificar seu próprio pincel e tamanho de traço.
class CalligraphicPen : InkToolbarCustomPen
{
protected override InkDrawingAttributes
CreateInkDrawingAttributesCore(Brush brush, double strokeWidth)
{
}
}
- Crie um objeto InkDrawingAttributes e defina a forma da ponta da caneta, a rotação da ponta, o tamanho do traço e a cor da tinta.
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
Em seguida, adicionamos à MainPage.xaml as referências necessárias à caneta personalizada.
- Declaramos um dicionário de recursos de página local que cria uma referência à caneta personalizada (
CalligraphicPen) definida em CalligraphicPen.cs e uma coleção de pincel com suporte pela caneta personalizada (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>
- Em seguida, adicionamos uma InkToolbar com um elemento filho InkToolbarCustomPenButton.
O botão de caneta personalizada inclui as duas referências de recurso estático declaradas nos recursos da página: CalligraphicPen e CalligraphicPenPalette.
Também especificamos o intervalo para o controle deslizante do tamanho do traço (MinStrokeWidth, MaxStrokeWidth e SelectedStrokeWidth), o pincel selecionado (SelectedBrushIndex) e o ícone do botão de caneta personalizado (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>
Alternância personalizada
Você pode criar uma alternância personalizada (ativada por meio de um botão de alternância personalizado) para definir o estado de um recurso definido pelo aplicativo como ativado ou desativado. Quando ativado, o recurso funciona em conjunto com a ferramenta ativa.
Neste exemplo, definimos um botão de alternância personalizado que permite a escrita à tinta com entrada por toque (por padrão, a escrita por toque não está habilitada).
Observação
Se você precisar dar suporte à escrita à tinta com toque, recomendamos habilitá-la usando um CustomToggleButton, com o ícone e a dica de ferramenta especificados neste exemplo.
Normalmente, a entrada por toque é usada para manipulação direta de um objeto ou da interface do usuário do aplicativo. Para demonstrar as diferenças de comportamento quando a escrita por toque está habilitada, colocamos o InkCanvas dentro de um contêiner ScrollViewer e definimos as dimensões do ScrollViewer como menores que o InkCanvas.
Quando o aplicativo é iniciado, há suporte apenas para o uso de caneta e o toque é usado para mover ou ampliar a superfície de anotação. Quando a escrita por toque está habilitada, a superfície de escrita à tinta não pode ser movida ou aproximada por meio da entrada de toque.
Observação
Veja controles de tinta para as diretrizes de UX tanto de InkCanvas quanto de InkToolbar. As seguintes recomendações são relevantes para este exemplo:
- O InkToolbar e a escrita à tinta em geral oferecem a melhor experiência através de uma caneta ativa. No entanto, poderá haver suporte para escrita à tinta com mouse e touch, se seu aplicativo exigir.
- Se houver suporte à escrita à tinta com entrada por toque, recomendamos usar o ícone "ED5F" da família de fontes "Segoe MLD2 Assets" para o botão de alternância, com uma dica de texto "Escrita por toque".
XAML
- Primeiro, declaramos um elemento InkToolbarCustomToggleButton (toggleButton) com um listener de evento de clique que especifica o manipulador de eventos (Toggle_Custom).
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
x:Name="HeaderPanel"
Orientation="Horizontal">
<TextBlock x:Name="Header"
Text="Basic ink sample"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10" />
</StackPanel>
<ScrollViewer Grid.Row="1"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<Grid HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<InkToolbar Grid.Row="0"
Margin="10"
x:Name="inkToolbar"
VerticalAlignment="Top"
TargetInkCanvas="{x:Bind inkCanvas}">
<InkToolbarCustomToggleButton
x:Name="toggleButton"
Click="CustomToggle_Click"
ToolTipService.ToolTip="Touch Writing">
<SymbolIcon Symbol="{x:Bind TouchWritingIcon}"/>
</InkToolbarCustomToggleButton>
</InkToolbar>
<ScrollViewer Grid.Row="1"
Height="500"
Width="500"
x:Name="scrollViewer"
ZoomMode="Enabled"
MinZoomFactor=".1"
VerticalScrollMode="Enabled"
VerticalScrollBarVisibility="Auto"
HorizontalScrollMode="Enabled"
HorizontalScrollBarVisibility="Auto">
<Grid x:Name="outputGrid"
Height="1000"
Width="1000"
Background="{ThemeResource SystemControlBackgroundChromeWhiteBrush}">
<InkCanvas x:Name="inkCanvas"/>
</Grid>
</ScrollViewer>
</Grid>
</ScrollViewer>
</Grid>
Code-behind
No snippet anterior, declaramos um ouvinte e manipulador de eventos click (Toggle_Custom) no botão de alternância personalizado para escrita por toque (toggleButton). Esse manipulador simplesmente alterna o suporte para CoreInputDeviceTypes.Touch por meio da propriedade InputDeviceTypes do InkPresenter.
Também especificamos um ícone para o botão usando o elemento SymbolIcon e a extensão de marcação {x:Bind} que o associa a um campo definido no arquivo code-behind (TouchWritingIcon).
O snippet a seguir inclui o manipulador de eventos Click e a definição de 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;
}
}
}
}
Ferramenta personalizada
Você pode criar um botão de ferramenta personalizado para invocar uma ferramenta que não seja caneta definida pelo seu aplicativo.
Por padrão, um InkPresenter processa todas as entradas como um traço de tinta ou um traço de borracha. Isso inclui a entrada modificada por um recurso físico de hardware secundário, como um botão lateral da caneta, um botão direito do mouse ou semelhante. No entanto, o InkPresenter pode ser configurado para deixar uma entrada específica não processada, que pode ser passada para seu aplicativo para processamento personalizado.
Neste exemplo, definimos um botão de ferramenta personalizado que, quando selecionado, faz com que os traços subsequentes sejam processados e renderizados como um laço de seleção (linha tracejada) em vez de tinta. Todos os traços de tinta dentro dos limites da área de seleção são definidos como Selecionados.
Observação
Consulte os controles de tinta para as diretrizes de UX para InkCanvas e InkToolbar. A seguinte recomendação é relevante para este exemplo:
- Se estiver fornecendo seleção de traço, recomendamos usar o ícone "EF20" da fonte "Segoe MLD2 Assets" para o botão de ferramenta, com uma dica de ferramenta "Ferramenta de seleção".
XAML
Primeiro, declaramos um elemento InkToolbarCustomToolButton (customToolButton) com um ouvinte de evento Click que especifica o manipulador de eventos (customToolButton_Click) em que a seleção de traço está configurada. (Também adicionamos um conjunto de botões para copiar, cortar e colar a seleção de traçados.)
Também adicionamos um elemento Canvas para desenhar nosso traço de seleção. Usar uma camada separada para desenhar o traço de seleção garante que o InkCanvas e seu conteúdo permaneçam intocados.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
<TextBlock x:Name="Header"
Text="Basic ink sample"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10,0,0,0" />
</StackPanel>
<StackPanel x:Name="ToolPanel" Orientation="Horizontal" Grid.Row="1">
<InkToolbar x:Name="inkToolbar"
VerticalAlignment="Top"
TargetInkCanvas="{x:Bind inkCanvas}">
<InkToolbarCustomToolButton
x:Name="customToolButton"
Click="customToolButton_Click"
ToolTipService.ToolTip="Selection tool">
<SymbolIcon Symbol="{x:Bind SelectIcon}"/>
</InkToolbarCustomToolButton>
</InkToolbar>
<Button x:Name="cutButton"
Content="Cut"
Click="cutButton_Click"
Width="100"
Margin="5,0,0,0"/>
<Button x:Name="copyButton"
Content="Copy"
Click="copyButton_Click"
Width="100"
Margin="5,0,0,0"/>
<Button x:Name="pasteButton"
Content="Paste"
Click="pasteButton_Click"
Width="100"
Margin="5,0,0,0"/>
</StackPanel>
<Grid Grid.Row="2" x:Name="outputGrid"
Background="{ThemeResource SystemControlBackgroundChromeWhiteBrush}"
Height="Auto">
<!-- Canvas for displaying selection UI. -->
<Canvas x:Name="selectionCanvas"/>
<!-- Canvas for displaying ink. -->
<InkCanvas x:Name="inkCanvas" />
</Grid>
</Grid>
Code-behind
Em seguida, manipulamos o evento Click para o InkToolbarCustomToolButton no arquivo code-behind MainPage.xaml.cs.
Esse manipulador configura o InkPresenter para passar a entrada não processada para o aplicativo.
Para obter uma etapa mais detalhada deste código: consulte a entrada de passagem para a seção de processamento avançado de interações com Caneta e Windows Ink em aplicativos do Windows.
Também especificamos um ícone para o botão usando o elemento SymbolIcon e a extensão de marcação {x:Bind} que o associa a um campo definido no arquivo code-behind (SelectIcon).
O snippet a seguir inclui o manipulador de eventos Click e a definição de 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);
}
}
}
}
Renderização de tinta personalizada
Por padrão, a entrada de tinta é processada em um thread de plano de fundo de baixa latência e renderizada "molhada" à medida que é desenhada. Quando o traço é concluído (com a caneta ou dedo levantado, ou o botão do mouse liberado), ele é processado na thread específica da interface do usuário e então exibido como "seco" na camada InkCanvas (situada acima do conteúdo do aplicativo e substituindo a tinta molhada).
A plataforma de tinta permite substituir esse comportamento e personalizar completamente a experiência de escrita à tinta por meio da secagem personalizada da entrada de tinta.
Para obter mais informações sobre a secagem personalizada, consulte interações com Caneta e Windows Ink em aplicativos do Windows.
Observação
Secagem personalizada e o InkToolbar
Se o aplicativo substituir o comportamento padrão de renderização de tinta do InkPresenter com uma implementação de secagem personalizada, os traços de tinta renderizados não estarão mais disponíveis para o InkToolbar e os comandos de apagamento internos do InkToolbar não funcionarão conforme o esperado. Para fornecer funcionalidade de apagamento, você deve lidar com todos os eventos de ponteiro, executar o teste de clique em cada traço e substituir o comando interno "Apagar toda a tinta".
Artigos relacionados
Exemplos de tópico
- Exemplo de localização e orientação da barra de ferramentas de tinta (básico)
- Exemplo de localização e orientação da barra de ferramentas de tinta (dinâmico)
Outros exemplos
Windows developer