Compartilhar via


Armazenar e recuperar dados de traço do Windows Ink

Os aplicativos do Windows que dão suporte ao Windows Ink podem serializar e desserializar traços de tinta para um arquivo ISF (Formato Serializado de Tinta). O arquivo ISF é uma imagem GIF com metadados adicionais para todas as propriedades e comportamentos de traço de tinta. Os aplicativos que não são habilitados para tinta podem exibir a imagem GIF estática, incluindo a transparência do plano de fundo do canal alfa.

Observação

ISF é a representação persistente mais compacta de tinta. Ele pode ser incorporado em um formato de documento binário, como um arquivo GIF, ou colocado diretamente na área de transferência.

A especificação ISF (Ink Serialized Format) pode ser feita por download no Centro de Download da Microsoft.

APIs importantes: InkCanvas, Windows.UI.Input.Inking

Salvar traços de tinta em um arquivo

Aqui, demonstramos como salvar traços de tinta desenhados em um controle InkCanvas.

Baixe este exemplo em Salvar e carregar traços de tinta de um arquivo ISF (Formato Serializado de Tinta)

  1. Primeiro, configuramos a interface do usuário.

    A interface do usuário inclui os botões "Salvar", "Carregar" e "Limpar" e o InkCanvas.

<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 store sample" 
                       Style="{ThemeResource HeaderTextBlockStyle}" 
                       Margin="10,0,0,0" />
            <Button x:Name="btnSave" 
                    Content="Save" 
                    Margin="50,0,10,0"/>
            <Button x:Name="btnLoad" 
                    Content="Load" 
                    Margin="50,0,10,0"/>
            <Button x:Name="btnClear" 
                    Content="Clear" 
                    Margin="50,0,10,0"/>
        </StackPanel>
        <Grid Grid.Row="1">
            <InkCanvas x:Name="inkCanvas" />
        </Grid>
    </Grid>
  1. Em seguida, definimos alguns comportamentos básicos de entrada de tinta.

    O InkPresenter está configurado para interpretar dados de entrada da caneta e do mouse como traços de tinta (InputDeviceTypes) e os ouvintes dos eventos de clique nos botões são declarados.

public MainPage()
    {
        this.InitializeComponent();

        // Set supported inking device types.
        inkCanvas.InkPresenter.InputDeviceTypes =
            Windows.UI.Core.CoreInputDeviceTypes.Mouse |
            Windows.UI.Core.CoreInputDeviceTypes.Pen;

        // Listen for button click to initiate save.
        btnSave.Click += btnSave_Click;
        // Listen for button click to initiate load.
        btnLoad.Click += btnLoad_Click;
        // Listen for button click to clear ink canvas.
        btnClear.Click += btnClear_Click;
    }
  1. Por fim, salvamos a tinta no manipulador de eventos de clique do botão Salvar .

    Um FileSavePicker permite que o usuário selecione o arquivo e o local onde os dados de tinta são salvos.

    Depois que um arquivo é selecionado, abrimos um fluxo IRandomAccessStream definido como ReadWrite.

    Em seguida, chamamos SaveAsync para serializar os traços de tinta gerenciados pelo InkStrokeContainer para o fluxo.

// Save ink data to a file.
    private async void btnSave_Click(object sender, RoutedEventArgs e)
    {
        // Get all strokes on the InkCanvas.
        IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();

        // Strokes present on ink canvas.
        if (currentStrokes.Count > 0)
        {
            // Let users choose their ink file using a file picker.
            // Initialize the picker.
            Windows.Storage.Pickers.FileSavePicker savePicker = 
                new Windows.Storage.Pickers.FileSavePicker();
            savePicker.SuggestedStartLocation = 
                Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
            savePicker.FileTypeChoices.Add(
                "GIF with embedded ISF", 
                new List<string>() { ".gif" });
            savePicker.DefaultFileExtension = ".gif";
            savePicker.SuggestedFileName = "InkSample";

            // Show the file picker.
            Windows.Storage.StorageFile file = 
                await savePicker.PickSaveFileAsync();
            // When chosen, picker returns a reference to the selected file.
            if (file != null)
            {
                // Prevent updates to the file until updates are 
                // finalized with call to CompleteUpdatesAsync.
                Windows.Storage.CachedFileManager.DeferUpdates(file);
                // Open a file stream for writing.
                IRandomAccessStream stream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
                // Write the ink strokes to the output stream.
                using (IOutputStream outputStream = stream.GetOutputStreamAt(0))
                {
                    await inkCanvas.InkPresenter.StrokeContainer.SaveAsync(outputStream);
                    await outputStream.FlushAsync();
                }
                stream.Dispose();

                // Finalize write so other apps can update file.
                Windows.Storage.Provider.FileUpdateStatus status =
                    await Windows.Storage.CachedFileManager.CompleteUpdatesAsync(file);

                if (status == Windows.Storage.Provider.FileUpdateStatus.Complete)
                {
                    // File saved.
                }
                else
                {
                    // File couldn't be saved.
                }
            }
            // User selects Cancel and picker returns null.
            else
            {
                // Operation cancelled.
            }
        }
    }

Observação

GIF é o único formato de arquivo suportado para salvar dados de tinta. No entanto, o método LoadAsync (demonstrado na próxima seção) dá suporte a formatos adicionais para compatibilidade com versões anteriores.

Carregar traços de tinta de um arquivo

Aqui, demonstramos como carregar traços de tinta de um arquivo e renderizá-los em um controle InkCanvas .

Baixe este exemplo em Salvar e carregar traços de tinta de um arquivo ISF (Formato Serializado de Tinta)

  1. Primeiro, configuramos a interface do usuário.

    A interface do usuário inclui os botões "Salvar", "Carregar" e "Limpar" e o InkCanvas.

<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 store sample" 
                       Style="{ThemeResource HeaderTextBlockStyle}" 
                       Margin="10,0,0,0" />
            <Button x:Name="btnSave" 
                    Content="Save" 
                    Margin="50,0,10,0"/>
            <Button x:Name="btnLoad" 
                    Content="Load" 
                    Margin="50,0,10,0"/>
            <Button x:Name="btnClear" 
                    Content="Clear" 
                    Margin="50,0,10,0"/>
        </StackPanel>
        <Grid Grid.Row="1">
            <InkCanvas x:Name="inkCanvas" />
        </Grid>
    </Grid>
  1. Em seguida, definimos alguns comportamentos básicos de entrada de tinta.

    O InkPresenter está configurado para interpretar dados de entrada da caneta e do mouse como traços de tinta (InputDeviceTypes) e os ouvintes dos eventos de clique nos botões são declarados.

public MainPage()
    {
        this.InitializeComponent();

        // Set supported inking device types.
        inkCanvas.InkPresenter.InputDeviceTypes =
            Windows.UI.Core.CoreInputDeviceTypes.Mouse |
            Windows.UI.Core.CoreInputDeviceTypes.Pen;

        // Listen for button click to initiate save.
        btnSave.Click += btnSave_Click;
        // Listen for button click to initiate load.
        btnLoad.Click += btnLoad_Click;
        // Listen for button click to clear ink canvas.
        btnClear.Click += btnClear_Click;
    }
  1. Por fim, carregamos a tinta no manipulador de eventos de clique do botão Carregar .

    Um FileOpenPicker permite que o usuário selecione o arquivo e o local de onde recuperar os dados de tinta salvos.

    Depois que um arquivo é selecionado, abrimos um fluxo IRandomAccessStream definido como Read.

    Em seguida, chamamos LoadAsync para ler, desserializar e carregar os traços de tinta salvos no InkStrokeContainer. Carregar os traços no InkStrokeContainer faz com que o InkPresenter os renderize imediatamente no InkCanvas.

    Observação

    Todos os traços existentes no InkStrokeContainer são limpos antes que novos traços sejam carregados.

// Load ink data from a file.
private async void btnLoad_Click(object sender, RoutedEventArgs e)
{
    // Let users choose their ink file using a file picker.
    // Initialize the picker.
    Windows.Storage.Pickers.FileOpenPicker openPicker =
        new Windows.Storage.Pickers.FileOpenPicker();
    openPicker.SuggestedStartLocation =
        Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
    openPicker.FileTypeFilter.Add(".gif");
    // Show the file picker.
    Windows.Storage.StorageFile file = await openPicker.PickSingleFileAsync();
    // User selects a file and picker returns a reference to the selected file.
    if (file != null)
    {
        // Open a file stream for reading.
        IRandomAccessStream stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
        // Read from file.
        using (var inputStream = stream.GetInputStreamAt(0))
        {
            await inkCanvas.InkPresenter.StrokeContainer.LoadAsync(inputStream);
        }
        stream.Dispose();
    }
    // User selects Cancel and picker returns null.
    else
    {
        // Operation cancelled.
    }
}

Observação

GIF é o único formato de arquivo suportado para salvar dados de tinta. No entanto, o método LoadAsync dá suporte aos seguintes formatos para compatibilidade com versões anteriores.

Formatar Descrição
InkSerializedFormat Especifica a tinta que é mantida usando ISF. Esta é a representação persistente mais compacta da tinta. Ele pode ser incorporado em um formato de documento binário ou colocado diretamente na área de transferência.
Base64InkSerializedFormat Especifica a tinta que é persistida codificando o ISF como um fluxo base64. Esse formato é fornecido para que a tinta possa ser codificada diretamente em um arquivo XML ou HTML.
Gif Especifica a tinta que é mantida usando um arquivo GIF que contém ISF como metadados inseridos no arquivo. Isso permite que a tinta seja exibida em aplicativos que não são habilitados para tinta e mantenha sua fidelidade total de tinta quando retornar a um aplicativo habilitado para tinta. Esse formato é ideal para transportar conteúdo de tinta dentro de um arquivo HTML e para torná-lo utilizável por aplicativos de tinta e não tinta.
Base64Gif Especifica a tinta que é mantida usando um GIF fortificado codificado em base64. Esse formato é fornecido quando a tinta deve ser codificada diretamente em um arquivo XML ou HTML para posterior conversão em uma imagem. Um possível uso disso é em um formato XML gerado para conter todas as informações de tinta e usado para gerar HTML por meio de XSLT (Extensible Stylesheet Language Transformations).

Copiar e colar traços de tinta com a área de transferência

Aqui, demonstramos como usar a área de transferência para transferir traços de tinta entre aplicativos.

Para dar suporte à funcionalidade da área de transferência, os comandos internos de recorte e cópia InkStrokeContainer exigem que um ou mais traços de tinta sejam selecionados.

Para este exemplo, habilitamos a seleção de traços quando a entrada é modificada com um botão de barril de caneta (ou botão direito do mouse). Para obter um exemplo completo de como implementar a seleção de traço, consulte Entrada de passagem para processamento avançado em Interações de caneta e caneta.

Baixe este exemplo em Salvar e carregar traços de tinta da área de transferência

  1. Primeiro, configuramos a interface do usuário.

    A interface do usuário inclui os botões "Recortar", "Copiar", "Colar" e "Limpar", juntamente com o InkCanvas e uma tela de seleção.

<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="tbHeader" 
                       Text="Basic ink store sample" 
                       Style="{ThemeResource HeaderTextBlockStyle}" 
                       Margin="10,0,0,0" />
            <Button x:Name="btnCut" 
                    Content="Cut" 
                    Margin="20,0,10,0"/>
            <Button x:Name="btnCopy" 
                    Content="Copy" 
                    Margin="20,0,10,0"/>
            <Button x:Name="btnPaste" 
                    Content="Paste" 
                    Margin="20,0,10,0"/>
            <Button x:Name="btnClear" 
                    Content="Clear" 
                    Margin="20,0,10,0"/>
        </StackPanel>
        <Grid x:Name="gridCanvas" Grid.Row="1">
            <!-- Canvas for displaying selection UI. -->
            <Canvas x:Name="selectionCanvas"/>
            <!-- Inking area -->
            <InkCanvas x:Name="inkCanvas"/>
        </Grid>
    </Grid>
  1. Em seguida, definimos alguns comportamentos básicos de entrada de tinta.

    InkPresenter está configurado para interpretar dados de entrada de caneta e mouse como traços de tinta (InputDeviceTypes). Os ouvintes dos eventos de clique nos botões, bem como os eventos de ponteiro e traço para a funcionalidade de seleção, também são declarados aqui.

    Para obter um exemplo completo de como implementar a seleção de traço, consulte Entrada de passagem para processamento avançado em Interações de caneta e caneta.

public MainPage()
    {
        this.InitializeComponent();

        // Set supported inking device types.
        inkCanvas.InkPresenter.InputDeviceTypes =
            Windows.UI.Core.CoreInputDeviceTypes.Mouse |
            Windows.UI.Core.CoreInputDeviceTypes.Pen;

        // Listen for button click to cut ink strokes.
        btnCut.Click += btnCut_Click;
        // Listen for button click to copy ink strokes.
        btnCopy.Click += btnCopy_Click;
        // Listen for button click to paste ink strokes.
        btnPaste.Click += btnPaste_Click;
        // Listen for button click to clear ink canvas.
        btnClear.Click += btnClear_Click;

        // 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;
    }
  1. Por fim, depois de adicionar suporte à seleção de traços, implementamos a funcionalidade da área de transferência nos manipuladores de eventos de clique dos botões Recortar, Copiar e Colar .

    Para corte, primeiro chamamos CopySelectedToClipboard no InkStrokeContainer do InkPresenter.

    Em seguida, chamamos DeleteSelected para remover os traços da tela de tinta.

    Por fim, excluímos todos os traços de seleção da tela de seleção.

private void btnCut_Click(object sender, RoutedEventArgs e)
    {
        inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
        inkCanvas.InkPresenter.StrokeContainer.DeleteSelected();
        ClearSelection();
    }
// 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;
        }
    }

Para cópia, basta chamar CopySelectedToClipboard no InkStrokeContainer do InkPresenter.

private void btnCopy_Click(object sender, RoutedEventArgs e)
    {
        inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
    }

Para colar, chamamos CanPasteFromClipboard para garantir que o conteúdo da área de transferência possa ser colado na tela de tinta.

Nesse caso, chamamos PasteFromClipboard para inserir os traços de tinta da área de transferência no InkStrokeContainer do InkPresenter, que renderiza os traços na tela de tinta.

private void btnPaste_Click(object sender, RoutedEventArgs e)
    {
        if (inkCanvas.InkPresenter.StrokeContainer.CanPasteFromClipboard())
        {
            inkCanvas.InkPresenter.StrokeContainer.PasteFromClipboard(
                new Point(0, 0));
        }
        else
        {
            // Cannot paste from clipboard.
        }
    }

Exemplos de tópicos

Outras amostras