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.
Surface Pen (disponível para compra na Microsoft Store).
Visão geral
Otimize a sua aplicação Windows para introdução com caneta, proporcionando tanto a funcionalidade padrão do dispositivo com ponteiro como a melhor experiência Windows Ink para os seus utilizadores.
Observação
Este tema centra-se na plataforma Windows Ink. Para o tratamento geral de entradas de ponteiros (semelhante a rato, toque e touchpad), consulte Manusear entrada de ponteiros.
Usar tinta na sua aplicação Windows
Use o Windows Pen and Ink para criar aplicações empresariais mais envolventes
A plataforma Windows Ink, juntamente com um dispositivo caneta, oferece uma forma natural de criar notas digitais manuscritas, desenhos e anotações. A plataforma suporta a captura de entrada do digitalizador como dados de tinta, geração de dados de tinta, gestão de dados de tinta, renderização dos dados de tinta como traços de tinta no dispositivo de saída e conversão de tinta em texto através do reconhecimento de escrita manual.
Para além de captar a posição básica e o movimento da caneta enquanto o utilizador escreve ou desenha, a sua aplicação também pode acompanhar e recolher as diferentes quantidades de pressão usadas ao longo de um traço. Esta informação, juntamente com definições para a forma, tamanho e rotação da ponta da caneta, cor da tinta e propósito (tinta simples, apagamento, realce e seleção), permite-lhe proporcionar experiências de utilizador que se assemelham muito à escrita ou ao desenho em papel com caneta, lápis ou pincel.
Observação
A sua aplicação também pode suportar entrada de tinta de outros dispositivos baseados em ponteiro, incluindo digitalizadores táteis e dispositivos de rato.
A plataforma de tinta é muito flexível. Foi concebido para suportar vários níveis de funcionalidade, dependendo das suas necessidades.
Para as diretrizes de UX do Windows Ink, veja Controles para Escrita Digital.
Componentes da plataforma Windows Ink
| Componente | Description |
|---|---|
| InkCanvas | Um controlo de plataforma de interface XAML que, por defeito, recebe e exibe toda a entrada de uma caneta como um traço de tinta ou um traço de apagamento. Para mais informações sobre como usar o InkCanvas, consulte Reconhecer traços do Windows Ink como texto e Armazenar e recuperar os dados dos traços do Windows Ink. |
| InkPresenter | Um objeto code-behind, instanciado junto com um controlador InkCanvas (exposto através da propriedade InkCanvas.InkPresenter). Este objeto fornece toda a funcionalidade de tintagem padrão exposta pelo InkCanvas, juntamente com um conjunto abrangente de APIs para personalização e adaptação adicionais. Para mais informações sobre como usar o InkPresenter, consulte Reconhecer traços do Windows Ink como texto e Armazenar e recuperar os dados dos traços do Windows Ink. |
| InkToolbar | Um controlo de plataforma de interface XAML, contendo uma coleção personalizável e extensível de botões que ativam funcionalidades relacionadas com tinta num InkCanvas associado. Para mais informações sobre como usar a InkToolbar, consulte Adicionar uma InkToolbar a uma aplicação Windows com suporte para caneta. |
| IInkD2DRenderer | Permite a renderização dos traços de tinta no contexto de dispositivo Direct2D de uma aplicação Universal Windows, em vez do controlo InkCanvas padrão. Isto permite a personalização total da experiência de tinta. Para mais informações, consulte a amostra de tinta complexa. |
Noções básicas de pintura no InkCanvas
Para adicionar funcionalidade básica de desenho, basta colocar um controlo InkCanvas da plataforma UWP na página apropriada da sua aplicação.
Por defeito, o InkCanvas suporta apenas entrada de tinta a partir de uma caneta. A entrada é renderizada como um traço de tinta usando as definições padrão de cor e espessura (uma caneta esferográfica preta com 2 pixels de espessura), ou tratada como uma borracha de traço (quando a entrada é feita de uma ponta de borracha ou a ponta da caneta modificada com um botão de apagamento).
Observação
Se não houver ponta ou botão de borracha presente, o InkCanvas pode ser configurado para processar a entrada da ponta da caneta como um traço de apagamento.
Neste exemplo, um InkCanvas sobrepõe uma imagem de fundo.
Observação
Um InkCanvas tem propriedades padrão de altura e largura de zero, a menos que seja filho de um elemento que dimensione automaticamente os seus filhos, como os controlos StackPanel ou Grid.
<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" />
</Grid>
</Grid>
Esta série de imagens mostra como a entrada da caneta é renderizada por este controlo InkCanvas.
|
|
|
|---|---|---|
| O InkCanvas em branco com imagem de fundo. | O InkCanvas com pinceladas de tinta. | O InkCanvas com um traço apagado (repare como o apagamento funciona num traço inteiro, não numa parte). |
A funcionalidade de tinta-tinta suportada pelo controlo InkCanvas é fornecida por um objeto code-behind chamado InkPresenter.
Para a tinta-tinta básica, não tens de te preocupar com o InkPresenter. No entanto, para personalizar e configurar o comportamento de entintagem no InkCanvas, deve aceder ao respetivo objeto InkPresenter .
Personalização básica com o InkPresenter
Um objeto InkPresenter é instanciado com cada controlo InkCanvas.
Observação
O InkPresenter não pode ser instanciado diretamente. Em vez disso, é acedido através da propriedade InkPresenter do InkCanvas.
Para além de fornecer todos os comportamentos de tinta padrão do seu controlo correspondente no InkCanvas , o InkPresenter disponibiliza um conjunto abrangente de APIs para personalização adicional dos traços e gestão mais detalhada da entrada da caneta (padrão e modificada). Isto inclui propriedades de traços, tipos de dispositivos de entrada suportados e se a entrada é processada pelo objeto ou passada para a aplicação para processamento.
Observação
A entrada de tinta padrão (seja da ponta da caneta ou da ponta/botão da borracha) não é modificada por um recurso secundário de hardware, como um botão de caneta, botão direito do rato ou mecanismo semelhante.
Por defeito, a tinta é suportada apenas para entrada com caneta. Aqui, configuramos o InkPresenter para interpretar dados de entrada tanto da caneta como do rato como traços de tinta. Também definimos alguns atributos iniciais de traços de tinta usados para renderizar traços no InkCanvas.
Para ativar a escrita digital com rato e toque, defina a propriedade InputDeviceTypes do InkPresenter para a combinação dos valores CoreInputDeviceTypes que pretende.
public MainPage()
{
this.InitializeComponent();
// Set supported inking device types.
inkCanvas.InkPresenter.InputDeviceTypes =
Windows.UI.Core.CoreInputDeviceTypes.Mouse |
Windows.UI.Core.CoreInputDeviceTypes.Pen;
// Set initial ink stroke attributes.
InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
drawingAttributes.Color = Windows.UI.Colors.Black;
drawingAttributes.IgnorePressure = false;
drawingAttributes.FitToCurve = true;
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
}
Os atributos dos traços de tinta podem ser definidos dinamicamente para acomodar as preferências do utilizador ou os requisitos da aplicação.
Aqui, deixamos o utilizador escolher de uma lista de cores de tinta.
<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 customization sample"
VerticalAlignment="Center"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10,0,0,0" />
<TextBlock Text="Color:"
Style="{StaticResource SubheaderTextBlockStyle}"
VerticalAlignment="Center"
Margin="50,0,10,0"/>
<ComboBox x:Name="PenColor"
VerticalAlignment="Center"
SelectedIndex="0"
SelectionChanged="OnPenColorChanged">
<ComboBoxItem Content="Black"/>
<ComboBoxItem Content="Red"/>
</ComboBox>
</StackPanel>
<Grid Grid.Row="1">
<Image Source="Assets\StoreLogo.png" />
<InkCanvas x:Name="inkCanvas" />
</Grid>
</Grid>
Depois tratamos das alterações à cor selecionada e atualizamos os atributos do traço de tinta em conformidade.
// Update ink stroke color for new strokes.
private void OnPenColorChanged(object sender, SelectionChangedEventArgs e)
{
if (inkCanvas != null)
{
InkDrawingAttributes drawingAttributes =
inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();
string value = ((ComboBoxItem)PenColor.SelectedItem).Content.ToString();
switch (value)
{
case "Black":
drawingAttributes.Color = Windows.UI.Colors.Black;
break;
case "Red":
drawingAttributes.Color = Windows.UI.Colors.Red;
break;
default:
drawingAttributes.Color = Windows.UI.Colors.Black;
break;
};
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
}
}
Estas imagens mostram como a entrada da caneta é processada e personalizada pelo InkPresenter.
O InkCanvas com traços de tinta preta padrão.
O InkCanvas com traços de tinta vermelha selecionados pelo utilizador.
Para fornecer funcionalidades além da tinta e do apagamento, como a seleção de traços, a sua aplicação deve identificar uma entrada específica para o InkPresenter ser passada sem processamento para gestão pela sua aplicação.
Entrada pass-through para processamento avançado
Por defeito, o InkPresenter processa toda a entrada como um traço de tinta ou um traço de apagamento, incluindo entradas modificadas por uma funcionalidade secundária de hardware, como um botão de caneta, um botão direito do rato ou similar. No entanto, os utilizadores normalmente esperam alguma funcionalidade adicional ou comportamento modificado com estas possibilidades secundárias.
Em alguns casos, poderá também ser necessário expor funcionalidades adicionais para canetas sem vantagens secundárias (funcionalidades normalmente não associadas à ponta da caneta), outros tipos de dispositivos de entrada, ou algum tipo de comportamento modificado baseado numa seleção do utilizador na interface da sua aplicação.
Para suportar isto, o InkPresenter pode ser configurado para deixar entradas específicas por processar. Esta entrada não processada é depois encaminhada para a sua aplicação para processamento.
Exemplo - Utilize entrada não processada para implementar a seleção de traços
A plataforma Windows Ink não oferece suporte incorporado para ações que requerem entrada modificada, como a seleção de traços. Para suportar funcionalidades como esta, deve fornecer uma solução personalizada nas suas aplicações.
O seguinte exemplo de código (todo o código está nos ficheiros MainPage.xaml e MainPage.xaml.cs) demonstra como ativar a seleção de traços quando a entrada é alterada com um botão de barril da caneta (ou botão direito do rato).
Primeiro, configurámos a interface no MainPage.xaml.
Aqui, adicionamos uma tela (abaixo do InkCanvas) para desenhar o traço de seleção. Usar uma camada separada para desenhar o traço de seleção deixa o InkCanvas e o seu conteúdo intocados.
<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="Advanced ink customization sample" VerticalAlignment="Center" Style="{ThemeResource HeaderTextBlockStyle}" Margin="10,0,0,0" /> </StackPanel> <Grid Grid.Row="1"> <!-- Canvas for displaying selection UI. --> <Canvas x:Name="selectionCanvas"/> <!-- Inking area --> <InkCanvas x:Name="inkCanvas"/> </Grid> </Grid>Em MainPage.xaml.cs, declaramos algumas variáveis globais para manter referências a aspetos da interface de seleção. Especificamente, o traçado do laço de seleção e o retângulo delimitador que destaca os traços selecionados.
// Stroke selection tool. private Polyline lasso; // Stroke selection area. private Rect boundingRect;De seguida, configuramos o InkPresenter para interpretar dados de entrada tanto da caneta como do rato como traços de tinta, e definimos alguns atributos iniciais de traço de tinta usados para renderizar traços no InkCanvas.
Mais importante ainda, usamos a propriedade InputProcessingConfiguration do InkPresenter para indicar que qualquer entrada modificada deve ser processada pela aplicação. A entrada modificada é especificada atribuindo InputProcessingConfiguration.RightDragAction um valor de InkInputRightDragAction.LeaveUnprocessed. Quando este valor é definido, o InkPresenter passa para a classe InkUnprocessedInput , um conjunto de eventos de ponteiro para você tratar.
Assinalamos ouvintes para os eventos não processados PointerPressed, PointerMoved e PointerReleased, passados pelo InkPresenter. Toda a funcionalidade de seleção está implementada nos handlers para estes eventos.
Por fim, atribuímos ouvintes para os eventos StrokeStarted e StrokesErased do InkPresenter. Usamos os handlers para estes eventos para limpar a interface de seleção se um novo traço for iniciado ou um traço existente for apagado.
public MainPage() { this.InitializeComponent(); // Set supported inking device types. inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse | Windows.UI.Core.CoreInputDeviceTypes.Pen; // Set initial ink stroke attributes. InkDrawingAttributes drawingAttributes = new InkDrawingAttributes(); drawingAttributes.Color = Windows.UI.Colors.Black; drawingAttributes.IgnorePressure = false; drawingAttributes.FitToCurve = true; inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes); // 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; // Listen for new ink or erase strokes to clean up selection UI. inkCanvas.InkPresenter.StrokeInput.StrokeStarted += StrokeInput_StrokeStarted; inkCanvas.InkPresenter.StrokesErased += InkPresenter_StrokesErased; }Depois, definimos os handlers para os eventos não processados PointerPressed, PointerMoved e PointerReleased passados pelo InkPresenter.
Toda a funcionalidade de seleção está implementada nestes manipuladores, incluindo o traço do laço e o retângulo delimitador.
// 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(); }Para concluir o manipulador de eventos PointerReleased, limpamos a camada de seleção de todo o conteúdo (o traço do laço) e depois desenhamos um único retângulo de contorno em torno dos traços de tinta abrangidos pela área do laço.
// 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); } }Por fim, definimos os handlers para os eventos StrokeStarted e StrokesErased InkPresenter.
Ambos apenas chamam a mesma função de limpeza para a eliminação da seleção atual sempre que é detetado um novo traço.
// 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(); }Aqui está a função para remover toda a interface de seleção do quadro de seleção quando um novo traço é iniciado ou um traço existente é apagado.
// Clean up selection UI. private void ClearSelection() { var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes(); foreach (var stroke in strokes) { stroke.Selected = false; } ClearDrawnBoundingRect(); } private void ClearDrawnBoundingRect() { if (selectionCanvas.Children.Any()) { selectionCanvas.Children.Clear(); boundingRect = Rect.Empty; } }
Renderização personalizada de tinta
Por defeito, a entrada de tinta é processada numa thread de fundo de baixa latência e renderizada em progresso, ou "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).
Podes substituir este comportamento padrão e controlar completamente a experiência de escrita ao "secar de forma personalizada" os traços de tinta fresca. Embora o comportamento padrão seja tipicamente suficiente para a maioria das aplicações, existem alguns casos em que pode ser necessária uma secagem personalizada, incluindo:
- Gestão mais eficiente de coleções grandes ou complexas de traços de tinta
- Suporte de panorâmica e zoom mais eficiente em telas de tinta grandes
- Intercalando tinta e outros objetos, como formas ou texto, mantendo a ordem z
- Secar e converter tinta de forma síncrona numa forma DirectX (por exemplo, uma linha reta ou forma rasterizada e integrada no conteúdo da aplicação em vez de como uma camada separada do InkCanvas ).
A secagem personalizada requer um objeto IInkD2DRenderer para gerir a entrada de tinta e renderizá-la no contexto do dispositivo Direct2D da tua aplicação Universal Windows, em vez do controlo padrão InkCanvas.
Ao chamar ActivateCustomDrying (antes de o InkCanvas ser carregado), uma aplicação cria um objeto InkSynchronizer para personalizar como um traço de tinta é renderizado seco para um SurfaceImageSource ou VirtualSurfaceImageSource.
Tanto o SurfaceImageSource como o VirtualSurfaceImageSource fornecem uma superfície DirectX partilhada para a sua aplicação desenhar e compor no conteúdo da sua aplicação, embora o VSIS forneça uma superfície virtual maior do que o ecrã para panorâmicas e zoom de desempenho eficiente. Como as atualizações visuais destas superfícies são sincronizadas com o thread da interface XAML, quando a tinta é renderizada em qualquer uma delas, a tinta húmida pode ser removida do InkCanvas simultaneamente.
Também podes personalizar a tinta seca para um SwapChainPanel, mas a sincronização com o thread da interface não é garantida e pode haver um atraso entre o momento em que a tinta é renderizada no teu SwapChainPanel e a remoção da tinta do InkCanvas.
Para um exemplo completo desta funcionalidade, veja a amostra de tinta Complexa.
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".
Outros artigos nesta secção
| Tópico | Description |
|---|---|
| Reconhecer traços de tinta | Converta traços de tinta em texto usando reconhecimento de escrita manual, ou em formas usando reconhecimento personalizado. |
| Armazenar e recuperar traços de tinta | Armazene os dados dos traços de tinta num ficheiro Graphics Interchange Format (GIF) usando metadados Ink Serialized Format (ISF) incorporados. |
| Adicionar um InkToolbar a uma aplicação de tinta para Windows | Adicione uma InkToolbar padrão a uma aplicação de tinta-tinta de uma aplicação Windows, adicione um botão de caneta personalizada à InkToolbar e associe o botão da caneta personalizada a uma definição de caneta personalizada. |
Artigos relacionados
- Comece: Suporte a tinta digital na sua aplicação para Windows
- Manipular entrada de ponteiro
- Identificar dispositivos de entrada
- Especificação do Ink Serialized Format (ISF).
APIs
Samples
- Tutorial de Iniciação: Suporte à tinta digital na sua aplicação Windows
- Amostra de tinta simples (C#/C++)
- Amostra complexa de tinta (C++)
- Amostra de tinta (JavaScript)
- Amostra de livro de colorir
- Amostra de notas familiares
- Amostra de entrada básica
- Amostra de entrada de baixa latência
- Exemplo de modo de interação do usuário
- Exemplo de visuais do Focus
Arquivar amostras
Windows developer