Stocker et récupérer les données de traits Windows Ink

Les applications Windows qui prennent en charge Windows Ink peuvent sérialiser et désérialiser des traits d’encre dans un fichier ISF (Ink Serialized Format). Le fichier ISF est une image GIF avec des métadonnées supplémentaires pour toutes les propriétés et comportements de trait d’encre. Les applications qui ne sont pas compatibles avec l’entrée manuscrite peuvent afficher l’image GIF statique, y compris la transparence de l’arrière-plan de canal alpha.

Remarque

ISF est la représentation persistante la plus compacte de l’encre. Il peut être incorporé dans un format de document binaire, tel qu’un fichier GIF, ou placé directement dans le Presse-papiers.

La spécification ISF (Ink Serialized Format) peut être téléchargée à partir du Centre de téléchargement Microsoft.

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

Enregistrer des traits d’encre dans un fichier

Ici, nous montrons comment enregistrer des traits d’encre dessinés sur un contrôle InkCanvas.

Téléchargez cet exemple à partir de l’enregistrement et du chargement des traits d’encre à partir d’un fichier ISF (Ink Serialized Format)

  1. Tout d’abord, nous avons configuré l’interface utilisateur.

    L’interface utilisateur inclut les boutons « Enregistrer », « Charger » et « Effacer » et 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. Ensuite, nous définissons quelques comportements de base en matière de saisie à l’encre.

    InkPresenter est configuré pour interpréter les données d’entrée du stylet et de la souris en tant que traits d’encre (InputDeviceTypes), et les écouteurs pour les événements de clic sur les boutons sont déclarés.

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. Enfin, nous enregistrons l’entrée manuscrite dans le gestionnaire d’événements Click du bouton Enregistrer .

    Un FileSavePicker permet à l’utilisateur de sélectionner à la fois le fichier et l’emplacement où les données manuscrites sont enregistrées.

    Une fois qu’un fichier est sélectionné, nous ouvrez un flux IRandomAccessStream défini sur ReadWrite.

    Nous appelons ensuite SaveAsync pour sérialiser les traits d’encre gérés par InkStrokeContainer vers le flux.

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

Remarque

GIF est le seul format de fichier pris en charge pour l’enregistrement des données manuscrites. Toutefois, la méthode LoadAsync (illustrée dans la section suivante) prend en charge des formats supplémentaires pour la compatibilité descendante.

Charger des traits d’encre à partir d’un fichier

Ici, nous montrons comment charger des traits d’encre à partir d’un fichier et les afficher sur un contrôle InkCanvas.

Téléchargez cet exemple à partir de l’enregistrement et du chargement des traits d’encre à partir d’un fichier ISF (Ink Serialized Format)

  1. Tout d’abord, nous avons configuré l’interface utilisateur.

    L’interface utilisateur inclut les boutons « Enregistrer », « Charger » et « Effacer » et 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. Ensuite, nous définissons quelques comportements de base en matière de saisie à l’encre.

    InkPresenter est configuré pour interpréter les données d’entrée du stylet et de la souris en tant que traits d’encre (InputDeviceTypes), et les écouteurs pour les événements de clic sur les boutons sont déclarés.

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. Enfin, nous chargeons l’entrée manuscrite dans le gestionnaire d’événements Click du bouton Charger .

    Un FileOpenPicker permet à l’utilisateur de sélectionner à la fois le fichier et l’emplacement à partir duquel récupérer les données manuscrites enregistrées.

    Une fois qu’un fichier est sélectionné, nous allons ouvrir un flux IRandomAccessStream défini sur Lecture.

    Nous appelons ensuite LoadAsync pour lire, désérialiser et charger les traits d’encre enregistrés dans InkStrokeContainer. Le chargement des traits dans InkStrokeContainer entraîne le rendu immédiat d’InkPresenter sur InkCanvas.

    Remarque

    Tous les traits existants dans InkStrokeContainer sont effacés avant le chargement des nouveaux traits.

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

Remarque

GIF est le seul format de fichier pris en charge pour l’enregistrement des données manuscrites. Toutefois, la méthode LoadAsync prend en charge les formats suivants pour la compatibilité descendante.

Format Description
InkSerializedFormat Spécifie les entrées manuscrites persistantes à l’aide d’ISF. Il s’agit de la représentation persistante la plus compacte de l’encre. Il peut être incorporé dans un format de document binaire ou placé directement dans le Presse-papiers.
Base64InkSerializedFormat Spécifie l’entrée manuscrite persistante en encodant l’ISF en tant que flux base64. Ce format est fourni afin que l’entrée manuscrite puisse être encodée directement dans un fichier XML ou HTML.
Gif Spécifie les entrées manuscrites persistantes à l’aide d’un fichier GIF qui contient ISF en tant que métadonnées incorporées dans le fichier. Cela permet à l’entrée manuscrite d’être consultée dans les applications qui ne sont pas compatibles avec l’entrée manuscrite et de maintenir sa fidélité à l’encre complète lorsqu’elle revient à une application compatible avec l’entrée manuscrite. Ce format est idéal pour transporter du contenu manuscrit dans un fichier HTML et pour le rendre utilisable par les applications manuscrites et non manuscrites.
Base64Gif Spécifie les entrées manuscrites persistantes à l’aide d’un GIF codé en base64. Ce format est fourni lorsque l’entrée manuscrite doit être encodée directement dans un fichier XML ou HTML pour une conversion ultérieure en image. L’utilisation possible de ceci est dans un format XML généré pour contenir toutes les informations manuscrites et utilisée pour générer du code HTML via des transformations XSLT (Extensible Stylesheet Language Transformations).

Copier et coller des traits d’encre avec le Presse-papiers

Ici, nous montrons comment utiliser le Presse-papiers pour transférer des traits d’encre entre les applications.

Pour prendre en charge les fonctionnalités du Presse-papiers, les commandes de découpage et de copie InkStrokeContainer intégrées nécessitent une ou plusieurs traits d’encre.

Pour cet exemple, nous allons activer la sélection des traits lorsque l’entrée est modifiée avec un bouton de canon de stylet (ou bouton droit de la souris). Pour obtenir un exemple complet d’implémentation de la sélection des traits, consultez l’entrée directe pour un traitement avancé dans les interactions stylet et stylet.

Téléchargez cet exemple à partir des traits d’encre d’enregistrement et de chargement à partir du Presse-papiers

  1. Tout d’abord, nous avons configuré l’interface utilisateur.

    L’interface utilisateur inclut les boutons « Couper », « Copier », « Coller » et « Effacer », ainsi que inkCanvas et un canevas de sélection.

<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. Ensuite, nous définissons quelques comportements de base en matière de saisie à l’encre.

    InkPresenter est configuré de manière à interpréter les données d’entrée provenant du stylo et de la souris comme des traits d’encre (InputDeviceTypes). Les écouteurs pour les événements de clic sur les boutons, ainsi que les événements de pointeur et de trait pour la fonctionnalité de sélection sont également déclarés ici.

    Pour obtenir un exemple complet d’implémentation de la sélection des traits, consultez l’entrée directe pour un traitement avancé dans les interactions stylet et stylet.

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. Enfin, après avoir ajouté la prise en charge de la sélection des traits, nous implémentons la fonctionnalité presse-papiers dans les gestionnaires d’événements Click des boutons Couper, Copier et Coller .

    Pour couper, nous appelons d’abord CopySelectedToClipboard sur InkStrokeContainer de InkPresenter.

    Nous appelons ensuite DeleteSelected pour supprimer les traits du canevas manuscrit.

    Enfin, nous supprimons tous les traits de sélection du canevas de sélection.

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;
        }
    }

Pour la copie, nous appelons simplement CopySelectedToClipboard sur InkStrokeContainer de InkPresenter.

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

Pour coller, nous appelons CanPasteFromClipboard pour vous assurer que le contenu du Presse-papiers peut être collé au canevas manuscrit.

Dans ce cas, nous appelons PasteFromClipboard pour insérer les traits d’encre du Presse-papiers dans le InkStrokeContainer de InkPresenter, qui restitue ensuite les traits dans la zone de dessin manuscrite.

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

Exemples de rubriques

Autres exemples