Partager via


Stocker et récupérer des données de trait 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 du canal alpha en arrière-plan.

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 le 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. Nous définissons ensuite des comportements d’entrée manuscrite de base.

    Le InkPresenter est configuré pour interpréter les données d’entrée à partir 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'encre dans le gestionnaire d'événements de clic 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 ouvrons un flux IRandomAccessStream réglé sur ReadWrite.

    Nous appelons ensuite SaveAsync pour sérialiser les traits d’encre gérés par le InkStrokeContainer au 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 le 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. Nous définissons ensuite des comportements d’entrée manuscrite de base.

    Le InkPresenter est configuré pour interpréter les données d’entrée à partir 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'encre dans le gestionnaire d'événements clic 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 ouvrons 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 l'InkStrokeContainer entraîne le rendu immédiat de l'InkPresenter à l'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 Descriptif
Format de sérialisation d'encre Spécifie l'encre qui est conservée à l'aide de l'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'encre qui est conservée en encodant l'ISF comme un flux base64. Ce format est fourni afin que l'encre puisse être encodée directement dans un fichier XML ou HTML.
Un Gif Spécifie l'encre qui est conservée à l'aide d'un fichier GIF contenant l'ISF comme métadonnées intégrées au fichier. Cela permet à l'encre d'être consultée dans les applications qui ne sont pas compatibles avec l'encre et de conserver sa pleine fidélité lorsqu'elle revient à une application compatible avec l'encre. 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 l'encre qui est conservée à l’aide d’un GIF protégé codé en base64. Ce format est fourni lorsque l'encre doit être encodée directement dans un fichier XML ou HTML pour être convertie ultérieurement 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 intégrées InkStrokeContainer couper et copier nécessitent la sélection d’un ou de 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 de passage pour un traitement avancé dans interactions avec le stylet.

Téléchargez cet exemple depuis Enregistrez et chargez des traits d’encre depuis le 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. Nous définissons ensuite des comportements d’entrée manuscrite de base.

    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). 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 de passage pour un traitement avancé dans interactions avec le 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 l'ajout de la prise en charge de la sélection des traits, nous implémentons la fonctionnalité presse-papiers dans les gestionnaires d'événements clic des boutons Couper, Copieret Coller.

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

    Nous appelons ensuite DeleteSelected pour supprimer les traits du canvas d'encre.

    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 copier, nous appelons simplement CopySelectedToClipboard sur la InkStrokeContainer de l'InkPresenter.

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

Pour coller, nous appelons CanPasteFromClipboard afin de nous assurer que le contenu du Presse-papiers peut être collé au canevas d'encre.

Si c'est le cas, nous appelons PasteFromClipboard pour insérer les traits d'encre du Presse-papiers dans le InkStrokeContainer du InkPresenter, qui restitue ensuite les traits sur la toile de dessin à l'encre.

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