Compartir a través de


Almacena y recupera los datos de trazos de Windows Ink

Las aplicaciones de Windows que admiten Windows Ink pueden serializar y deserializar trazos de lápiz en un archivo de formato serializado de lápiz (ISF). El archivo ISF es una imagen GIF con metadatos adicionales para todas las propiedades y comportamientos de trazos de lápiz. Las aplicaciones que no son compatibles con lápiz pueden mostrar la imagen GIF estática, incluida la transparencia de fondo del canal alfa.

Nota:

ISF es la representación persistente más compacta de la tinta. Se puede incrustar dentro de un formato de documento binario, como un archivo GIF, o colocarse directamente en el Portapapeles.

La especificación de Formato Serializado de Tinta (ISF) se puede descargar del Centro de Descargas de Microsoft.

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

Guardar trazos de lápiz en un archivo

Aquí mostramos cómo guardar trazos de tinta dibujados en un control InkCanvas .

Descargue este ejemplo de Guardar y cargar trazos de lápiz desde un archivo de formato serializado de lápiz (ISF)

  1. En primer lugar, configuramos la interfaz de usuario.

    La interfaz de usuario incluye los botones "Guardar", "Cargar" y "Borrar", y el 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. A continuación, establecemos algunos comportamientos básicos de entrada de lápiz.

    El InkPresenter está configurado para interpretar los datos de entrada tanto del lápiz como del ratón como trazos de tinta (InputDeviceTypes), y se declaran los agentes de escucha para los eventos de clic de los botones.

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 último, guardamos la tinta en el controlador de evento de clic del botón Guardar.

    Un FileSavePicker permite al usuario seleccionar tanto el archivo como la ubicación donde se guardan los datos de tinta.

    Una vez seleccionado un archivo, se abre una secuencia de IRandomAccessStream establecida en readWrite.

    A continuación, llamamos a SaveAsync para serializar los trazos de lápiz administrados por el InkStrokeContainer en la secuencia.

// 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.
            }
        }
    }

Nota:

GIF es el único formato de archivo admitido para guardar datos de entrada de lápiz. Sin embargo, el método LoadAsync (que se muestra en la sección siguiente) admite formatos adicionales para la compatibilidad con versiones anteriores.

Cargar trazos de lápiz desde un archivo

Aquí demostramos cómo cargar trazos de tinta desde un archivo y representarlos en un control InkCanvas.

Descargue este ejemplo de Guardar y cargar trazos de lápiz desde un archivo de formato serializado de lápiz (ISF)

  1. En primer lugar, configuramos la interfaz de usuario.

    La interfaz de usuario incluye los botones "Guardar", "Cargar" y "Borrar", y el 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. A continuación, establecemos algunos comportamientos básicos de entrada de lápiz.

    El InkPresenter está configurado para interpretar los datos de entrada tanto del lápiz como del ratón como trazos de tinta (InputDeviceTypes), y se declaran los agentes de escucha para los eventos de clic de los botones.

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 último, cargamos la tinta en el controlador de eventos click del botón Cargar.

    Un FileOpenPicker permite al usuario seleccionar tanto el archivo como la ubicación desde donde recuperar los datos de tinta guardados.

    Una vez seleccionado un archivo, abrimos una secuencia IRandomAccessStream configurada para leer .

    A continuación, llamamos a LoadAsync para leer, deserializar y cargar los trazos de lápiz guardados en el InkStrokeContainer. Cargar los trazos en el InkStrokeContainer hace que el InkPresenter los represente inmediatamente en el InkCanvas .

    Nota:

    Todos los trazos existentes en el InkStrokeContainer se borran antes de cargar los nuevos trazos.

// 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.
    }
}

Nota:

GIF es el único formato de archivo admitido para guardar datos de entrada de lápiz. Sin embargo, el método LoadAsync admite los siguientes formatos para la compatibilidad con versiones anteriores.

Formato Descripción
InkSerializedFormat Especifica la tinta que se conserva mediante ISF. Esta es la representación más compacta y persistente de la tinta. Se puede incrustar dentro de un formato de documento binario o colocarse directamente en el Portapapeles.
Base64InkSerializedFormat Especifica la tinta que se guarda codificando el ISF como una secuencia base64. Este formato se proporciona para que la entrada de lápiz se pueda codificar directamente en un archivo XML o HTML.
GIF Especifica la tinta que se persiste mediante un archivo GIF que contiene ISF como metadatos incrustados. Esto permite que el trazo de tinta se vea en aplicaciones que no están habilitadas para trazos de tinta y mantiene su plena fidelidad de tinta al regresar a una aplicación habilitada para trazos de tinta. Este formato es ideal para transportar contenido de tinta dentro de un archivo HTML y para que sea utilizable por aplicaciones de tinta y no de tinta.
Base64Gif Especifica tinta que es persistida mediante un GIF fortalecido codificado en base64. Este formato se proporciona cuando la entrada de lápiz se va a codificar directamente en un archivo XML o HTML para la conversión posterior en una imagen. Un posible uso de esto es en un formato XML generado para contener toda la información de tinta y utilizado para generar HTML a través de Transformaciones de Lenguaje de Hoja de Estilos Extensibles (XSLT).

Copiar y pegar trazos de lápiz con el Portapapeles

Aquí se muestra cómo usar el Portapapeles para transferir trazos de lápiz entre aplicaciones.

Para admitir la funcionalidad del Portapapeles, los comandos integrados InkStrokeContainer de cortar y copiar requieren que se seleccionen uno o varios trazos de tinta.

En este ejemplo, se habilita la selección de trazos cuando se modifica la entrada con un botón de barril de lápiz (o botón derecho del mouse). Para obtener un ejemplo completo de cómo implementar la selección de trazos, consulte Entrada de paso directo para el procesamiento avanzado en interacciones de bolígrafo y lápiz óptico.

Descargar este ejemplo de Guardar y cargar trazos de tinta desde el portapapeles

  1. En primer lugar, configuramos la interfaz de usuario.

    La interfaz de usuario incluye botones "Cortar", "Copiar", "Pegar" y "Borrar", junto con InkCanvas y un lienzo de selección.

<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. A continuación, establecemos algunos comportamientos básicos de entrada de lápiz.

    El InkPresenter está configurado para interpretar los datos de entrada del lápiz y el mouse como trazos de tinta (InputDeviceTypes). Los agentes de escucha de los eventos de clic en los botones, así como los eventos de puntero y trazo para la funcionalidad de selección también se declaran aquí.

    Para obtener un ejemplo completo de cómo implementar la selección de trazos, consulte Entrada de paso directo para el procesamiento avanzado en interacciones de bolígrafo y lápiz óptico.

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 último, después de agregar compatibilidad con la selección de trazos, implementamos la funcionalidad del portapapeles en los controladores de eventos click de los botones Cortar, Copiary Pegar.

    Para cortar, primero llamamos a CopySelectedToClipboard en el InkStrokeContainer del InkPresenter .

    A continuación, llamamos a DeleteSelected para quitar los trazos del lienzo de tinta.

    Por último, eliminamos todos los trazos de selección del lienzo de selección.

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, simplemente llamamos a CopySelectedToClipboard en el InkStrokeContainer del InkPresenter.

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

Para pegar, llamamos a CanPasteFromClipboard para asegurarnos de que el contenido del Portapapeles se pueda pegar en el lienzo de lápiz.

Si es así, llamamos a PasteFromClipboard para insertar los trazos de lápiz del Portapapeles en el InkStrokeContainer del InkPresenter, que luego representa los trazos en el lienzo 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.
        }
    }

Ejemplos de temas

otros ejemplos