Partilhar via


Armazenar e recuperar dados de traços do Windows Ink

As aplicações Windows que suportam o Windows Ink podem serializar e desserializar rastros de tinta para um ficheiro Ink Serialized Format (ISF). O ficheiro ISF é uma imagem GIF com metadados adicionais para todas as propriedades e comportamentos dos traços de tinta. As aplicações que não suportam tinta digital podem visualizar a imagem GIF estática, incluindo a transparência do fundo no canal alfa.

Observação

ISF é a representação persistente mais compacta da tinta. Pode ser incorporado num formato binário de documento, como um ficheiro GIF, ou colocado diretamente na Área de Transferências.

A Especificação Ink Serialized Format (ISF) pode ser descarregada no Microsoft Download Center.

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

Guardar traços de tinta num ficheiro

Aqui, demonstramos como guardar traços de tinta desenhados num controlo InkCanvas .

Faça o download deste exemplo a partir de Guardar e carregar traços de tinta de um ficheiro em formato ISF (Ink Serialized Format)

  1. Primeiro, configurámos a interface.

    A interface inclui os botões "Guardar", "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. Depois definimos alguns comportamentos básicos de entrada de tinta.

    O InkPresenter está configurado para interpretar dados de entrada tanto da caneta como do rato 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. Finalmente, guardamos a tinta no manipulador de eventos de clique do botão guardar.

    Um FileSavePicker permite ao utilizador selecionar tanto o ficheiro como o local onde os dados de tinta são guardados.

    Uma vez selecionado um ficheiro, abrimos um fluxo IRandomAccessStream configurado para ReadWrite.

    Depois chamamos o SaveAsync para serializar os traços de tinta geridos 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 ficheiro suportado para guardar dados de tinta. No entanto, o método LoadAsync (demonstrado na secção seguinte) suporta formatos adicionais para compatibilidade retroativa.

Carregar traços de tinta a partir de um ficheiro

Aqui, demonstramos como carregar traços de tinta a partir de um ficheiro e renderizá-los num controlo InkCanvas .

Descarregue este exemplo de Guardar e carregar traços de tinta de um ficheiro no formato Ink Serialized Format (ISF)

  1. Primeiro, configurámos a interface.

    A interface inclui os botões "Guardar", "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. Depois definimos alguns comportamentos básicos de entrada de tinta.

    O InkPresenter está configurado para interpretar dados de entrada tanto da caneta como do rato 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 handler de eventos de clique do botão Carregar .

    Um FileOpenPicker permite ao utilizador selecionar tanto o ficheiro como o local de onde recuperar os dados de tinta guardados.

    Assim que um ficheiro é selecionado, abrimos um fluxo IRandomAccessStream configurado para Read.

    Depois 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 de serem carregados novos traços.

// 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 ficheiro suportado para guardar dados de tinta. No entanto, o método LoadAsync suporta os seguintes formatos para compatibilidade retroativa.

Formato Description
InkSerializedFormat Especifica tinta que é preservada usando o formato ISF. Esta é a representação persistente mais compacta da tinta. Pode ser incorporado num formato de documento binário ou colocado diretamente na Área de Transferência.
Base64InkSerializedFormat Especifica tinta que é mantida codificando o ISF como um fluxo base64. Este formato é fornecido para que a tinta possa ser codificada diretamente num ficheiro XML ou HTML.
Gif Especifica a tinta que é preservada utilizando um ficheiro GIF que contém ISF como metadados incorporados no ficheiro. Isto permite que a tinta seja visualizada em aplicações que não são habilitadas para tinta e mantenha a sua fidelidade total quando regressa a uma aplicação habilitada para tinta. Este formato é ideal para transportar conteúdo de tinta dentro de um ficheiro HTML e para o tornar utilizável por aplicações de tinta e não-tinta.
Base64Gif Especifica tinta que é persistida através de um GIF fortificado codificado em base64. Este formato é fornecido quando a tinta vai ser codificada diretamente num ficheiro XML ou HTML para conversão posterior numa imagem. Uma possível utilização disto é num formato XML gerado para conter toda a informação de tinta e usado para gerar HTML através de Transformações Extensíveis de Linguagem de Folhas de Estilo (XSLT).

Copiar e colar traços de tinta com a prancheta

Aqui, demonstramos como usar a prancheta para transferir traços de tinta entre aplicações.

Para suportar a funcionalidade de prancheta, os comandos de corte e cópia incorporados do InkStrokeContainer exigem que um ou mais traços de tinta sejam selecionados.

Neste exemplo, ativamos a seleção de traços quando a entrada é modificada com um botão lateral da caneta (ou botão direito do rato). Para um exemplo completo de como implementar a seleção de traços, veja Entrada direta para processamento avançado em interações entre caneta e estilete.

Descarregue este exemplo de Guardar e carregar pinceladas de tinta a partir da prancheta

  1. Primeiro, configurámos a interface.

    A interface inclui os botões "Cortar", "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. Depois definimos alguns comportamentos básicos de entrada de tinta.

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

    Para um exemplo completo de como implementar a seleção de traços, veja Entrada direta para processamento avançado em interações entre caneta e stylus.

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. Finalmente, após adicionar suporte para seleção de traços, implementamos funcionalidade de prancheta nos manipuladores de eventos de clique dos botões Cortar, Copiar e Colar .

    Para cortar, primeiro chamamos CopySelectedToClipboard no InkStrokeContainer do InkPresenter.

    Depois chamamos o DeleteSelected para remover os traços da tela de tinta.

    Por fim, eliminamos 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 copiar, simplesmente chamamos 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 prancheta pode ser colado na tela de tinta.

Se assim for, chamamos o PasteFromClipboard para inserir os traços de tinta da prancheta no InkStrokeContainer do InkPresenter, que depois 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

Outros exemplos