Compartir a través de


Almacenar y recuperar datos de trazos de lápiz 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 están habilitadas para entrada de lápiz pueden ver 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 entrada de lápiz. Se puede incrustar dentro de un formato de documento binario, como un archivo GIF, o colocarse directamente en el Portapapeles.

La especificación de Ink Serialized Format (ISF) se puede descargar desde el Centro de descarga de Microsoft.

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

Guardar trazos de lápiz en un archivo

Aquí se muestra cómo guardar trazos de lápiz 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 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.

    InkPresenter está configurado para interpretar los datos de entrada del lápiz y el mouse como trazos de lápiz (InputDeviceTypes) y los agentes de escucha para los eventos de clic en los botones se declaran.

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 entrada de lápiz en el controlador de eventos click del botón Guardar .

    Un Objeto FileSavePicker permite al usuario seleccionar tanto el archivo como la ubicación donde se guardan los datos de entrada de lápiz.

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

    A continuación, llamamos a SaveAsync para serializar los trazos de lápiz administrados por 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í se muestra cómo cargar trazos de lápiz 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 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.

    InkPresenter está configurado para interpretar los datos de entrada del lápiz y el mouse como trazos de lápiz (InputDeviceTypes) y los agentes de escucha para los eventos de clic en los botones se declaran.

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 entrada de lápiz en el controlador de eventos click del botón Cargar .

    Un Objeto FileOpenPicker permite al usuario seleccionar el archivo y la ubicación desde donde recuperar los datos de entrada de lápiz guardados.

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

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

    Nota:

    Todos los trazos existentes en 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 entrada de lápiz que se conserva mediante ISF. Esta es la representación más compacta y persistente de la entrada de lápiz. Se puede incrustar dentro de un formato de documento binario o colocarse directamente en el Portapapeles.
Base64InkSerializedFormat Especifica la entrada de lápiz que se conserva 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 entrada de lápiz que se conserva mediante un archivo GIF que contiene ISF como metadatos incrustados en el archivo. Esto permite que la entrada de lápiz se vea en aplicaciones que no están habilitadas para entrada de lápiz y mantienen su fidelidad de entrada de lápiz completa cuando vuelve a una aplicación habilitada para entrada de lápiz. Este formato es ideal al transportar contenido de entrada de lápiz dentro de un archivo HTML y para que sea utilizable por las aplicaciones de entrada de lápiz y no de lápiz.
Base64Gif Especifica la entrada de lápiz que se conserva mediante un GIF 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 entrada de lápiz y se usa 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 de cortar y copiar InkStrokeContainer requieren que se seleccionen uno o varios trazos de lápiz.

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 a través para el procesamiento avanzado en interacciones de lápiz y lápiz.

Descargue este ejemplo de Guardar y cargar trazos de lápiz 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.

    InkPresenter está configurado para interpretar los datos de entrada de lápiz y mouse como trazos de lápiz (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 a través para el procesamiento avanzado en interacciones de lápiz y lápiz.

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, Copiar y Pegar .

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

    A continuación, llamamos a DeleteSelected para quitar los trazos del lienzo de entrada de lápiz.

    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 inkStrokeContainer de 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 entrada de lápiz.

Si es así, llamamos a PasteFromClipboard para insertar los trazos de lápiz del Portapapeles en el inkStrokeContainer de InkPresenter, que luego representa los trazos en el lienzo de lápiz.

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