Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Existem dois controlos diferentes que facilitam o desenho a tinta nas aplicações Windows: InkCanvas e InkToolbar.
O controlo InkCanvas fornece funcionalidades básicas do Windows Ink. Usa-o para renderizar a entrada da caneta como traço de tinta (usando as definições padrão de cor e espessura) ou como traço de apagamento.
Para detalhes sobre a implementação do InkCanvas, veja Interações entre caneta e caneta nas aplicações Windows.
Como uma sobreposição completamente transparente, o InkCanvas não oferece qualquer interface de utilizador incorporada para definir propriedades de traços de tinta. Se quiser alterar a experiência de entintagem predefinida, permitir que os utilizadores definam propriedades de traço de tinta e suportar outras funcionalidades personalizadas de tinta, tem duas opções:
No code-behind, use o objeto subjacente InkPresenter associado ao InkCanvas.
As APIs do InkPresenter suportam uma personalização extensa da experiência de escrita digital. Para mais detalhes, consulte Interações de caneta e stylus nas aplicações Windows.
Associe um InkToolbar ao InkCanvas. Por defeito, o InkToolbar oferece uma coleção personalizável e extensível de botões para ativar funcionalidades relacionadas com a tinta, como tamanho do traço, cor da tinta e ponta da caneta.
Discutimos o InkToolbar neste tópico.
APIs importantes: classe InkCanvas, classe InkToolbar, classe InkPresenter, Windows.UI.Input.Inking
InkToolbar por defeito
Por defeito, o InkToolbar inclui botões para desenhar, apagar, destacar e mostrar um estêncil (régua ou transportador). Dependendo da funcionalidade, outras configurações e comandos, como a cor da tinta, a espessura do traçado, apagar toda a tinta, são fornecidos num menu suspenso.
Barra de ferramentas padrão do Windows Ink
Para adicionar uma InkToolbar padrão a uma aplicação de desenho, basta colocá-la na mesma página do InkCanvas e associar os dois controlos.
- No MainPage.xaml, declare um objeto contentor (neste exemplo, usamos um controlo Grid) para a superfície de tinta.
- Declare um objeto InkCanvas como filho do contentor. (O tamanho do InkCanvas é herdado do recipiente.)
- Declara um InkToolbar e usa o atributo TargetInkCanvas para o ligar ao InkCanvas.
Observação
Certifique-se de que o InkToolbar é declarado após o InkCanvas. Caso contrário, a sobreposição do 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 secção, abordamos alguns cenários básicos de personalização da barra de ferramentas do Windows Ink.
Especificar localização e orientação
Quando adiciona uma barra de ferramentas de tinta à sua aplicação, pode aceitar a localização e orientação padrão da barra de ferramentas ou defini-las conforme exigido pela sua aplicação ou utilizador.
XAML
Especifique explicitamente a localização e orientação da barra de ferramentas através das suas propriedades VerticalAlignment, HorizontalAlignment e Orientation .
| Predefinido | Explícito |
|---|---|
|
|
| Localização e orientação padrão da barra de ferramentas Windows Ink | Localização explícita e orientação da barra de ferramentas Windows Ink |
Aqui está o código para definir explicitamente a localização e orientação da barra de ferramentas ink em XAML.
<InkToolbar x:Name="inkToolbar"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Orientation="Vertical"
TargetInkCanvas="{x:Bind inkCanvas}" />
Inicializar com base nas preferências do utilizador ou no estado do dispositivo
Em alguns casos, pode querer definir a localização e orientação da barra de ferramentas de tinta com base na preferência do utilizador ou no estado do dispositivo. O exemplo seguinte demonstra como definir a localização e orientação da barra de ferramentas de tinta com base nas preferências de escrita à esquerda ou à direita especificadas através de Definições > Dispositivos > Caneta & Windows Tinta >> Caneta Escolha com que mão escrever.
Configuração da mão dominante
Pode consultar esta definição através da propriedade HandPreference do Windows.UI.ViewManagement e definir o HorizontalAlignment com base no valor devolvido. Neste exemplo, localizamos a barra de ferramentas do lado esquerdo da aplicação para uma pessoa canhota e do lado direito para uma pessoa destra.
Descarregue este exemplo da amostra de localização e orientação da barra de ferramentas 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;
}
Ajuste dinamicamente ao estado do utilizador ou dispositivo
Também pode usar binding para acompanhar atualizações da interface com base em alterações nas preferências do utilizador, definições do dispositivo ou estados do dispositivo. No exemplo seguinte, expandimos o exemplo anterior e mostramos como posicionar dinamicamente a barra de ferramentas de tinta com base na orientação do dispositivo usando binding, um objeto ViewMOdel e a interface INotifyPropertyChanged .
Descarregue este exemplo a partir da amostra dinâmica de localização e orientação da barra de ferramentas Ink
Primeiro, vamos adicionar o nosso ViewModel.
Adicione uma nova pasta ao seu projeto e chame-lhe ViewModels.
Adicione uma nova classe à pasta ViewModels (neste exemplo, chamámos-lhe InkToolbarSnippetHostViewModel.cs).
Observação
Usámos o padrão Singleton , pois só precisamos de um objeto deste tipo durante toda a vida útil da aplicação
Adicione
using System.ComponentModelespaço de nomes ao ficheiro.Adicione uma variável membro estática chamada instância, e uma propriedade estática de apenas leitura chamada Instância. Torne o construtor privado para garantir que esta classe só pode ser acedida através da propriedade Instance.
Observação
Esta classe herda da interface INotifyPropertyChanged , que é usada para notificar clientes, normalmente clientes vinculativos, de que o valor de uma propriedade mudou. Vamos usar isto para lidar com alterações na orientação do dispositivo (vamos expandir este código e explicar mais detalhadamente numa 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() { } }Adicionar duas propriedades bool à classe InkToolbarSnippetHostViewModel: LeftHandedLayout (mesma funcionalidade do exemplo anterior apenas XAML) e PortraitLayout (orientação do dispositivo).
Observação
A propriedade PortraitLayout é configurável e inclui a definição do 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 conversores ao nosso projeto. Cada classe contém um objeto Convert que devolve um valor de alinhamento ( seja HorizontalAlignment ou VerticalAlignment).
Adicione uma nova pasta ao seu projeto e chame-lhe Conversores.
Adicione duas novas classes à pasta Converters (neste exemplo, chamamos-lhes HorizontalAlignmentFromHandednessConverter.cs e VerticalAlignmentFromAppViewConverter.cs).
Adicionar
using Windows.UI.Xamleusing Windows.UI.Xaml.Datanamespaces a cada ficheiro.Altere cada classe para
publice especifique que implementa a interface IValueConverter.Adicione os métodos Convert e ConvertBack a cada ficheiro, como mostrado aqui (deixamos o método ConvertBack por implementar).
- HorizontalAlignmentFromHandednessConverter posiciona a barra de ferramentas de tinta para o lado direito da aplicação para utilizadores destros e para o lado esquerdo para utilizadores 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 barra de ferramentas de tinta no centro da aplicação para orientação retrato e no topo da aplicação para orientação horizontal (embora tenha como objetivo melhorar a usabilidade, esta é 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, abre o ficheiro MainPage.xaml.cs.
- Adicione
using using locationandorientation.ViewModelsà lista de namespaces para associar o nosso ViewModel. - Adicione
using Windows.UI.ViewManagementà lista de namespaces para permitir a escuta de alterações na orientação do dispositivo. - Adicione o código WindowSizeChangedEventHandler .
- Defina o DataContext da vista para 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
De seguida, abre o ficheiro MainPage.xam.
Adiciona
xmlns:converters="using:locationandorientation.Converters"aoPageelemento para ligaçã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 associe as propriedades VerticalAlignment e HorizontalAlignment da 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
PortraitLayouteLeftHandedLayoutbool à classeInkToolbarSnippetHostViewModele inclua suporte para reatribuição dePortraitLayoutquando o valor desta propriedade mudar.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 deves ter uma aplicação de tinta que se adapta tanto à preferência dominante da mão do utilizador como responde dinamicamente à orientação do dispositivo do utilizador.
Especifique o botão selecionado
Barra de ferramentas Windows Ink com botão lápis selecionado na inicialização
Por defeito, o primeiro (ou mais à esquerda) botão é selecionado quando a aplicação é lançada e a barra de ferramentas é inicializada. Na barra de ferramentas padrão do Windows Ink, este é o botão da caneta esferográfica.
Como a estrutura define a ordem dos botões integrados, o primeiro botão pode não ser a caneta ou ferramenta que deseja ativar por padrão.
Pode sobrescrever este comportamento predefinido e especificar o botão selecionado na barra de ferramentas.
Neste 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 o InkCanvas e InkToolbar do exemplo anterior.
- No code-behind, configura um handler 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 handler do evento Loaded :
- Obtenha uma referência para o InkToolbarPencilButton incorporado.
Passar um objeto InkToolbarTool.Pencil no método GetToolButton devolve um objeto InkToolbarToolButton para o InkToolbarPencilButton.
- Defina o ActiveTool para o objeto devolvido no passo 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 incorporados
Botões específicos incluídos na inicialização
Como referido, a barra de ferramentas Windows Ink inclui uma coleção de botões predefinidos e incorporados. Estes botões são apresentados pela seguinte ordem (da esquerda para a direita):
- InkToolbarBolaPenBotão
- InkToolbarPencilButton
- InkToolbarMarcadorButton
- BotãoApagadorDaBarraDeFerramentasDeTinta
- InkToolbarRulerButton
Neste exemplo, inicializamos a barra de ferramentas apenas com os botões integrados da caneta, lápis e borracha.
Podes fazer isto usando XAML ou code-behind.
XAML
Modificar a declaração XAML para o InkCanvas e InkToolbar a partir do primeiro exemplo.
- Adicione um atributo InitialControls e defina o seu valor para "Nenhum". Isto elimina a coleção padrão de botões incorporados.
- Adicione os botões específicos do InkToolbar exigidos pela sua aplicação. Aqui, adicionamos InkToolbarBallpointPenButton, InkToolbarPencilButton e InkToolbarEraserButton apenas.
Observação
Os botões são adicionados à barra de ferramentas pela ordem definida pelo framework, não pela ordem aqui especificada.
<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>
Código por trás
- Use a declaração XAML para o InkCanvas e o 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 Carregamento 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 InicialControls com o valor "Nenhum".
- Crie referências de objetos para os botões exigidos pela sua aplicação. Aqui, adicionamos InkToolbarBallpointPenButton, InkToolbarPencilButton e InkToolbarEraserButton apenas.
Observação
Os botões são adicionados à barra de ferramentas pela ordem definida pelo framework, não pela ordem aqui especificada.
- Adicione os botões à 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 funcionalidades de escrita digital
Pode personalizar e expandir a coleção de botões (e as funcionalidades associadas de tinta) que são disponibilizadas através da InkToolbar.
O InkToolbar consiste em dois grupos distintos de tipos de botão:
- Um grupo de botões de "ferramenta" que contém os botões integrados de desenho, apagamento e realce. Canetas e ferramentas personalizadas são adicionadas aqui.
Nota A seleção de funcionalidades é mutuamente exclusiva.
- Um grupo de botões de comutação contendo o botão de régua integrado. Botões personalizados são adicionados aqui.
Nota As funcionalidades não são mutuamente exclusivas e podem ser usadas em simultâneo com outras ferramentas ativas.
Dependendo do seu aplicativo e da funcionalidade de tinta necessária, você pode adicionar qualquer um dos seguintes botões (vinculados aos seus recursos de tinta personalizados) à InkToolbar:
- Caneta personalizada – uma caneta para a qual a paleta de cores da tinta e as propriedades da ponta da caneta, como forma, rotação e tamanho, são definidas pelo aplicativo host.
- Ferramenta personalizada – uma ferramenta sem caneta, definida pelo aplicativo host.
- Alternância personalizada – Define o estado de um recurso definido pelo aplicativo como ativado ou desativado. Quando ativado, o recurso funciona em conjunto com a ferramenta ativa.
Nota Não pode alterar a ordem de exibição dos botões incorporados. A ordem de exibição padrão é: caneta esferográfica, lápis, marcador, 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 da caneta e o botão da 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
Podes criar uma caneta personalizada (ativada através de um botão de caneta personalizada) onde defines a paleta de cores da tinta e as propriedades da ponta da caneta, como forma, rotação e tamanho.
Botão de caneta caligráfica personalizada
Neste exemplo, definimos uma caneta personalizada com uma ponta larga que permite traços básicos de tinta caligráfica. Também personalizamos a coleção de pincéis na paleta exibida no botão de abertura.
Código por trás
Primeiro, definimos a nossa caneta personalizada e especificamos os atributos de desenho no code-behind. Referenciamos esta caneta personalizada do XAML mais tarde.
- Clique com o botão direito no projeto no Explorador de Soluções e selecione Adicionar -> Novo item.
- Em Visual C# -> Código, adicione um novo ficheiro de Classe e chame-o CalligraphicPen.cs.
- Em Calligraphic.cs, substitua o padrão usando bloco 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 deriva de InkToolbarCustomPen.
class CalligraphicPen : InkToolbarCustomPen
{
}
- Substituir CreateInkDrawingAttributesCore para especificar o teu 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
De seguida, adicionamos as referências necessárias à caneta personalizada no MainPage.xaml.
- Declaramos um dicionário de recursos de página local que cria uma referência para a caneta personalizada (
CalligraphicPen) definida em CalligraphicPen.cs, e uma coleção de pincéis suportada 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>
- Depois adicionamos um InkToolbar com um elemento filho chamado InkToolbarCustomPenButton .
O botão da caneta personalizada inclui as duas referências estáticas de recursos declaradas nos recursos da página: CalligraphicPen e CalligraphicPenPalette.
Também especificamos o intervalo para o slider do tamanho do traço (MinStrokeWidth, MaxStrokeWidth e SelectedStrokeWidth), o pincel selecionado (SelectedBrushIndex) e o ícone do botão personalizado da caneta (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>
Alavanca personalizada
Podes criar um interruptor personalizado (ativado através de um botão de alternância personalizado) para definir o estado de uma funcionalidade definida pela aplicação como ligado ou desligado. Quando ativado, o recurso funciona em conjunto com a ferramenta ativa.
Neste exemplo, definimos um botão personalizado que permite a tinta com toque (por predefinição, a tinta por toque não está ativada).
Observação
Se precisar de suportar a tinta com toque, recomendamos que o ative usando um CustomToggleButton, com o ícone e o tooltip especificados neste exemplo.
Normalmente, a entrada tátil é usada para manipulação direta de um objeto ou da interface da aplicação. Para demonstrar as diferenças de comportamento quando a tinta por toque está ativada, colocamos o InkCanvas dentro de um contentor ScrollViewer e definimos as dimensões do ScrollViewer para serem menores do que as do InkCanvas.
Quando a aplicação é iniciada, apenas a tinta com caneta é suportada e o toque é usado para fazer panorâmica ou ampliação da superfície de desenho. Quando a função de tinta por toque está ativada, a superfície de tinta não pode ser deslocada ou ampliada através de entrada tátil.
Observação
Consulte os controlos de tinta para as diretrizes de UX tanto do InkCanvas como do InkToolbar. As seguintes recomendações são relevantes para este exemplo:
- A InkToolbar, e a tinta em geral, são melhor experienciadas através de uma caneta ativa. No entanto, a escrita com o rato e o toque pode ser suportada, se requerido pela sua aplicação.
- Se suportar a escrita com tinta com o input tátil, recomendamos usar o ícone "ED5F" da fonte "Segoe MLD2 Assets" para o botão de alternância, com uma dica de ferramenta "Escrita tátil".
XAML
- Primeiro, declaramos um elemento InkToolbarCustomToggleButton (toggleButton) com uma escuta de eventos Click 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>
Código por trás
No excerto anterior, declarámos um ouvinte de eventos Click e um handler (Toggle_Custom) no botão personalizado para a digitalização por toque (toggleButton). Este manipulador simplesmente alterna o suporte para CoreInputDeviceTypes.Touch através da propriedade InputDeviceTypes do InkPresenter.
Também especificámos um ícone para o botão usando o elemento SymbolIcon e a extensão de marcação {x:Bind} que o liga a um campo definido no ficheiro code-behind (TouchWritingIcon).
O excerto seguinte inclui tanto o handler de eventos Click como 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
Pode criar um botão de ferramenta personalizado para invocar uma ferramenta que não seja caneta, definida pela sua aplicação.
Por defeito, um InkPresenter processa todo o input como um traço de tinta ou um traço de apagador. Isto inclui entrada modificada por uma funcionalidade secundária de hardware, como um botão de caneta para canetas, um botão direito do rato ou algo semelhante. No entanto, o InkPresenter pode ser configurado para deixar entradas específicas por processar, que depois podem ser encaminhadas para a sua aplicação para processamento personalizado.
Neste exemplo, definimos um botão de ferramenta personalizado que, quando selecionado, faz com que os traços seguintes 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
Veja os controlos de Tinta para as diretrizes de UX tanto do InkCanvas como do InkToolbar. A recomendação seguinte é relevante para este exemplo:
- Se a seleção de traços for fornecida, recomendamos usar o ícone "EF20" da fonte "Segoe MLD2 Assets" para o botão da 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 gestor de eventos (customToolButton_Click) onde a seleção de traços está configurada. (Também adicionámos um conjunto de botões para copiar, cortar e colar a seleção de traços.)
Também adicionamos um elemento Canvas para desenhar o nosso traço de seleção. Usar uma camada separada para desenhar o traço de seleção garante que o InkCanvas e o seu conteúdo permanecem 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>
Código por trás
Depois, tratamos do evento Click para o InkToolbarCustomToolButton no ficheiro code-behind MainPage.xaml.cs.
Este handler configura o InkPresenter para passar entrada não processada para a aplicação.
Para uma análise mais detalhada deste código, consulte a secção "Pass-through input para processamento avançado" nas interações com caneta e Windows Ink nas aplicações Windows.
Também especificámos um ícone para o botão usando o elemento SymbolIcon e a extensão de marcação {x:Bind} que o liga a um campo definido no ficheiro code-behind (SelectIcon).
O snippet seguinte inclui tanto o handler de eventos Click como 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 personalizada de tinta
Por defeito, a entrada de tinta é processada numa thread de fundo de baixa latência e renderizada "húmida" à medida que é desenhada. Quando o traço é concluído (com a caneta ou o dedo levantados, ou o botão do rato libertado), o traço é processado na thread da interface do utilizador e renderizado como "seco" para a camada InkCanvas (acima do conteúdo da aplicação e substituindo a tinta húmida).
A plataforma de tinta permite-lhe sobrepor este comportamento e personalizar completamente a experiência de entintagem ao secar a entrada de tinta à medida.
Para mais informações sobre secagem personalizada, veja Interações com canetas e Windows Ink nas aplicações Windows.
Observação
Secagem personalizada e o InkToolbar
Se a sua aplicação sobrescrever o comportamento padrão de renderização de tinta do InkPresenter com uma implementação personalizada de secagem, os traços de tinta renderizados deixam de estar disponíveis para a InkToolbar e os comandos de apagamento incorporados da InkToolbar não funcionam como esperado. Para fornecer funcionalidade de apagamento, deve gerir todos os eventos do apontador, realizar testes de acerto em cada traço e sobrescrever o comando incorporado "Apagar toda a tinta".
Artigos relacionados
Exemplos de tópicos
- Exemplo de posicionamento e orientação da barra de tinta (exemplo básico)
- Amostra de localização e orientação da barra de ferramentas de tinta (dinâmica)
Outras amostras
Windows developer