Partager via


Importer des fichiers multimédias à partir d’un appareil

Cet article décrit comment importer des fichiers multimédias à partir d’un appareil, notamment en recherchant les sources multimédias disponibles, en important des fichiers tels que des vidéos, des photos et des fichiers sidecar, et en supprimant les fichiers importés de l’appareil source.

Remarque

Le code de cet article a été adapté à partir de l’exemple d’application MediaImport UWP . Vous pouvez cloner ou télécharger cet exemple à partir du référentiel Git des exemples d’applications Windows universelles pour voir le code dans son contexte ou pour l’utiliser comme point de départ pour votre propre application.

Créez une interface utilisateur simple pour l’importation de médias

L’exemple présenté dans cet article utilise une interface utilisateur minimale pour mettre en œuvre les principaux scénarios d’importation de médias. Pour savoir comment créer une interface utilisateur plus robuste pour une application d’importation de médias, consultez l’exemple MediaImport. Le XAML suivant crée un panneau de pile avec les contrôles suivants :

  • Un bouton pour lancer la recherche des sources à partir desquelles les médias peuvent être importés.
  • Une ComboBox pour lister et sélectionner les sources d’importation de médias trouvées.
  • Un contrôle ListView pour afficher et sélectionner les éléments multimédias de la source d’importation sélectionnée.
  • Un bouton pour lancer l’importation d’éléments multimédias à partir de la source sélectionnée.
  • Un bouton pour lancer la suppression des éléments qui ont été importés à partir de la source sélectionnée.
  • Un bouton pour annuler une opération d’importation asynchrone de médias.
<StackPanel Orientation="Vertical">
    <Button x:Name="findSourcesButton" Click="findSourcesButton_Click" Content="Find sources"/>
    <ComboBox x:Name="sourcesComboBox" SelectionChanged="sourcesComboBox_SelectionChanged"/>
    <ListView x:Name="fileListView" 
                    HorizontalAlignment="Left" Margin="182,260,0,171" 
                    Width="715" 
                    SelectionMode="None" 
                    BorderBrush="#FF858585"   
                    BorderThickness="1" 
                    ScrollViewer.VerticalScrollBarVisibility="Visible">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="0.05*"/>
                        <ColumnDefinition Width="0.20*"/>
                        <ColumnDefinition Width="0.75*"/>
                    </Grid.ColumnDefinitions>
                    <CheckBox Grid.Column="0" IsChecked="{Binding ImportableItem.IsSelected, Mode=TwoWay}" />
                    <!-- Click="CheckBox_Click"/>-->
                    <Image Grid.Column="1" Source="{Binding Thumbnail}" Width="120" Height="120" Stretch="Uniform"/>
                    <TextBlock Grid.Column="2" Text="{Binding ImportableItem.Name}" VerticalAlignment="Center" Margin="10,0"/>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    
    <Button x:Name="importButton" Click="importButton_Click" Content="Import"/>
    <Button x:Name="deleteButton" Click="deleteButton_Click" Content="Delete"/>
    <Button x:Name="cancelButton" Click="cancelButton_Click" Content="Cancel"/>
    <ProgressBar x:Name="progressBar" SmallChange="0.01" LargeChange="0.1" Maximum="1"/>
    
</StackPanel>

Configurez votre fichier code-behind

Ajoutez des directives d’utilisation pour inclure les espaces de noms utilisés dans cet exemple qui ne sont pas déjà inclus dans le modèle de projet par défaut.

using Windows.Media.Import;
using System.Threading;
using Windows.UI.Core;
using System.Text;

Configurez l’annulation des tâches pour les opérations d’importation de médias

Les opérations d’importation de médias pouvant prendre beaucoup de temps, elles sont exécutées de manière asynchrone à l’aide de IAsyncOperationWithProgress. Déclarez une variable membre de la classe de type CancellationTokenSource qui sera utilisée pour annuler une opération en cours si l’utilisateur clique sur le bouton d’annulation.

CancellationTokenSource cts;

Implémentez un gestionnaire pour le bouton d’annulation. Les exemples présentés plus loin dans cet article initialiseront la variable CancellationTokenSource au début de l’opération et lui donneront la valeur null à la fin de l’opération. Dans le gestionnaire du bouton d’annulation, vérifiez que le jeton est nul et, si ce n’est pas le cas, appelez Cancel pour annuler l’opération.

private void cancelButton_Click(object sender, RoutedEventArgs e)
{
    if (cts != null)
    {
        cts.Cancel();
        System.Diagnostics.Debug.WriteLine("Operation canceled by the Cancel button.");
    }
}

Classes d’aide à la liaison de données

Dans un scénario typique d’importation de médias, vous montrez à l’utilisateur une liste d’éléments multimédias disponibles à importer, il peut y avoir un grand nombre de fichiers multimédias à choisir et, typiquement, vous voulez afficher une miniature pour chaque élément multimédia. C’est pourquoi cet exemple utilise trois classes d’aide pour charger de manière incrémentale les entrées dans le contrôle ListView au fur et à mesure que l’utilisateur fait défiler la liste vers le bas.

  • Classe IncrementalLoadingBase - Implémente les classes IList, ISupportIncrementalLoading et INotifyCollectionChanged pour fournir le comportement de base du chargement incrémental.
  • Classe GeneratorIncrementalLoadingClass - Fournit une implémentation de la classe de base de chargement incrémental.
  • Classe ImportableItemWrapper - Un wrapper fin autour de la classe PhotoImportItem pour ajouter une propriété BitmapImage liante pour l’image miniature de chaque élément importé.

Ces classes sont fournies dans l’exemple MediaImport et peuvent être ajoutées à votre projet sans modification. Après avoir ajouté les classes d’aide à votre projet, déclarez une variable membre de classe de type GeneratorIncrementalLoadingClass qui sera utilisée ultérieurement dans cet exemple.

GeneratorIncrementalLoadingClass<ImportableItemWrapper> itemsToImport = null;

Recherche des sources disponibles à partir desquelles des médias peuvent être importés

Dans le gestionnaire de clics pour le bouton trouver des sources, appelez la méthode statique PhotoImportManager.FindAllSourcesAsync pour lancer le système à la recherche d’appareils à partir desquels des médias peuvent être importés. Après avoir attendu la fin de l’opération, parcourez en boucle chaque objet PhotoImportSource de la liste retournée et ajoutez une entrée à la ComboBox, en attribuant la propriété Tag à l’objet source lui-même afin qu’il puisse être facilement récupéré lorsque l’utilisateur effectue une sélection.

private async void findSourcesButton_Click(object sender, RoutedEventArgs e)
{
    var sources = await PhotoImportManager.FindAllSourcesAsync();
    foreach (PhotoImportSource source in sources)
    {
        ComboBoxItem item = new ComboBoxItem();
        item.Content = source.DisplayName;
        item.Tag = source;
        sourcesComboBox.Items.Add(item);
    }
}

Déclarez une variable membre de la classe pour stocker la source d’importation sélectionnée par l’utilisateur.

PhotoImportSource importSource;

Dans le gestionnaire SelectionChanged de la ComboBox de la source d’importation, attribuez à la variable membre de la classe la valeur de la source sélectionnée, puis appelez la méthode d’aide FindItems qui sera présentée plus loin dans cet article.

private void sourcesComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    this.importSource = (PhotoImportSource)((ComboBoxItem)sourcesComboBox.SelectedItem).Tag;
    FindItems();
}

Recherche des éléments à importer

Ajoutez des variables de membre de classe de type PhotoImportSession et PhotoImportFindItemsResult qui seront utilisées dans les étapes suivantes.

PhotoImportSession importSession;
PhotoImportFindItemsResult itemsResult;

Dans la méthode FindItems, initialisez la variable CancellationTokenSource afin qu’elle puisse être utilisée pour annuler l’opération de recherche si nécessaire. Dans un bloc d’essai, créez une nouvelle session d’importation en appelant CreateImportSession sur l’objet PhotoImportSource sélectionné par l’utilisateur. Créez un nouvel objet Progress pour fournir un rappel permettant d’afficher la progression de l’opération de recherche. Appelez ensuite FindItemsAsync pour lancer l’opération de recherche. Fournissez une valeur PhotoImportContentTypeFilter pour indiquer si les photos, les vidéos ou les deux doivent être renvoyées. Indiquez une valeur PhotoImportItemSelectionMode pour préciser si tous les éléments multimédias, aucun ou seulement les nouveaux éléments multimédias sont renvoyés avec leur propriété IsSelected définie sur true. Cette propriété est liée à une case à cocher pour chaque élément multimédia dans notre modèle d’élément ListBox.

FindItemsAsync renvoie une IAsyncOperationWithProgress. La méthode d’extension AsTask est utilisée pour créer une tâche qui peut être attendue, qui peut être annulée avec le jeton d’annulation et qui rend compte de la progression en utilisant l’objet Progress fourni.

Ensuite, la classe d’aide à la liaison de données, GeneratorIncrementalLoadingClass, est initialisée. FindItemsAsync, lorsqu’il revient après avoir été attendu, renvoie un objet PhotoImportFindItemsResult. Cet objet contient des informations sur l’état de l’opération de recherche, notamment le succès de l’opération et le nombre de différents types d’éléments multimédias trouvés. La propriété FoundItems contient une liste d’objets PhotoImportItem représentant les éléments multimédias trouvés. Le constructeur de la classe GeneratorIncrementalLoadingClass prend comme arguments le nombre total d’éléments qui seront chargés de manière incrémentielle et une fonction qui génère de nouveaux éléments à charger selon les besoins. Dans ce cas, l’expression lambda fournie crée une nouvelle instance de ImportableItemWrapper qui enveloppe PhotoImportItem et inclut une miniature pour chaque élément. Une fois que la classe de chargement incrémentiel a été initialisée, définissez-la sur la propriété ItemsSource du contrôle ListView dans l’interface utilisateur. Désormais, les éléments multimédias trouvés seront chargés de manière incrémentielle et affichés dans la liste.

Ensuite, les informations sur l’état de l’opération de recherche sont affichées. Une application classique affiche ces informations à l’utilisateur dans l’interface utilisateur, mais cet exemple se contente d’afficher les informations dans la console de débogage. Enfin, définissez le jeton d’annulation à null car l’opération est terminée.

private async void FindItems()
{
    this.cts = new CancellationTokenSource();

    try
    {
        this.importSession = this.importSource.CreateImportSession();

        // Progress handler for FindItemsAsync
        var progress = new Progress<uint>((result) =>
        {
            System.Diagnostics.Debug.WriteLine(String.Format("Found {0} Files", result.ToString()));
        });

        this.itemsResult =
            await this.importSession.FindItemsAsync(PhotoImportContentTypeFilter.ImagesAndVideos, PhotoImportItemSelectionMode.SelectAll)
            .AsTask(this.cts.Token, progress);

        // GeneratorIncrementalLoadingClass is used to incrementally load items in the Listview view including thumbnails
        this.itemsToImport = new GeneratorIncrementalLoadingClass<ImportableItemWrapper>(this.itemsResult.TotalCount,
        (int index) =>
        {
            return new ImportableItemWrapper(this.itemsResult.FoundItems[index]);
        });

        // Set the items source for the ListView control
        this.fileListView.ItemsSource = this.itemsToImport;

        // Log the find results
        if (this.itemsResult != null)
        {
            var findResultProperties = new System.Text.StringBuilder();
            findResultProperties.AppendLine(String.Format("Photos\t\t\t :  {0} \t\t Selected Photos\t\t:  {1}", itemsResult.PhotosCount, itemsResult.SelectedPhotosCount));
            findResultProperties.AppendLine(String.Format("Videos\t\t\t :  {0} \t\t Selected Videos\t\t:  {1}", itemsResult.VideosCount, itemsResult.SelectedVideosCount));
            findResultProperties.AppendLine(String.Format("SideCars\t\t :  {0} \t\t Selected Sidecars\t:  {1}", itemsResult.SidecarsCount, itemsResult.SelectedSidecarsCount));
            findResultProperties.AppendLine(String.Format("Siblings\t\t\t :  {0} \t\t Selected Sibilings\t:  {1} ", itemsResult.SiblingsCount, itemsResult.SelectedSiblingsCount));
            findResultProperties.AppendLine(String.Format("Total Items Items\t :  {0} \t\t Selected TotalCount \t:  {1}", itemsResult.TotalCount, itemsResult.SelectedTotalCount));
            System.Diagnostics.Debug.WriteLine(findResultProperties.ToString());
        }

        if (this.itemsResult.HasSucceeded)
        {
            // Update UI to indicate success
            System.Diagnostics.Debug.WriteLine("FindItemsAsync succeeded.");
        }
        else
        {
            // Update UI to indicate that the operation did not complete
            System.Diagnostics.Debug.WriteLine("FindItemsAsync did not succeed or was not completed.");
        }
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine("Photo import find items operation failed. " + ex.Message);
    }


    this.cts = null;
}

Importer des éléments multimédias

Avant de mettre en œuvre l’opération d’importation, déclarez un objet PhotoImportImportItemsResult pour stocker les résultats de l’opération d’importation. Cet objet sera utilisé ultérieurement pour supprimer les éléments multimédias qui ont été importés avec succès depuis la source.

private PhotoImportImportItemsResult importedResult;

Avant de lancer l’opération d’importation de médias, initialisez la variable CancellationTokenSource et définissez la valeur du contrôle ProgressBar à 0.

S’il n’y a pas d’éléments sélectionnés dans le contrôle ListView, il n’y a rien à importer. Dans le cas contraire, initialisez un objet Progress pour fournir un rappel de progression qui met à jour la valeur du contrôle de la barre de progression. Enregistrez un gestionnaire pour l’événement ItemImported du résultat PhotoImportFindItemsResult renvoyé par l’opération de recherche. Cet événement est déclenché chaque fois qu’un élément est importé et, dans cet exemple, il affiche le nom de chaque fichier importé dans la console de débogage.

Appelez ImportItemsAsync pour lancer l’opération d’importation. Comme pour l’opération de recherche, la méthode d’extension AsTask est utilisée pour convertir l’opération renvoyée en une tâche qui peut être attendue, qui fait état de la progression et qui peut être annulée.

Une fois l’opération d’importation terminée, l’état de l’opération peut être obtenu à partir de l’objet PhotoImportImportItemsResult renvoyé par ImportItemsAsync. Cet exemple affiche les informations sur l’état de l’opération dans la console de débogage et, enfin, attribue la valeur null au jeton d’annulation.

private async void importButton_Click(object sender, RoutedEventArgs e)
{
    cts = new CancellationTokenSource();
    progressBar.Value = 0;

    try
    {
        if (itemsResult.SelectedTotalCount <= 0)
        {
            System.Diagnostics.Debug.WriteLine("Nothing Selected for Import.");
        }
        else
        {
            var progress = new Progress<PhotoImportProgress>((result) =>
            {
                progressBar.Value = result.ImportProgress;
            });

            this.itemsResult.ItemImported += async (s, a) =>
            {
                await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    System.Diagnostics.Debug.WriteLine(String.Format("Imported: {0}", a.ImportedItem.Name));
                });
            };

            // import items from the our list of selected items
            this.importedResult = await this.itemsResult.ImportItemsAsync().AsTask(cts.Token, progress);

            if (importedResult != null)
            {
                StringBuilder importedSummary = new StringBuilder();
                importedSummary.AppendLine(String.Format("Photos Imported   \t:  {0} ", importedResult.PhotosCount));
                importedSummary.AppendLine(String.Format("Videos Imported    \t:  {0} ", importedResult.VideosCount));
                importedSummary.AppendLine(String.Format("SideCars Imported   \t:  {0} ", importedResult.SidecarsCount));
                importedSummary.AppendLine(String.Format("Siblings Imported   \t:  {0} ", importedResult.SiblingsCount));
                importedSummary.AppendLine(String.Format("Total Items Imported \t:  {0} ", importedResult.TotalCount));
                importedSummary.AppendLine(String.Format("Total Bytes Imported \t:  {0} ", importedResult.TotalSizeInBytes));

                System.Diagnostics.Debug.WriteLine(importedSummary.ToString());
            }

            if (!this.importedResult.HasSucceeded)
            {
                System.Diagnostics.Debug.WriteLine("ImportItemsAsync did not succeed or was not completed");
            }
        }
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine("Files could not be imported. " + "Exception: " + ex.ToString());
    }

    cts = null;
}

Suppression des éléments importés

Pour supprimer les éléments importés avec succès de la source à partir de laquelle ils ont été importés, initialisez d’abord le jeton d’annulation afin que l’opération de suppression puisse être annulée et définissez la valeur de la barre de progression sur 0. Assurez-vous que le résultat PhotoImportImportItemsResult renvoyé par ImportItemsAsync n’est pas nul. Si ce n’est pas le cas, créez à nouveau un objet Progress afin de fournir un rappel de progression pour l’opération de suppression. Appelez DeleteImportedItemsFromSourceAsync pour commencer à supprimer les éléments importés. Utilisez AsTask pour convertir le résultat en une tâche en attente avec des capacités de progression et d’annulation. Après l’attente, l’objet PhotoImportDeleteImportedItemsFromSourceResult renvoyé peut être utilisé pour obtenir et afficher des informations sur l’état de l’opération de suppression.


private async void deleteButton_Click(object sender, RoutedEventArgs e)
{
    cts = new CancellationTokenSource();
    progressBar.Value = 0;

    try
    {
        if (importedResult == null)
        {
            System.Diagnostics.Debug.WriteLine("Nothing was imported for deletion.");
        }
        else
        {
            var progress = new Progress<double>((result) =>
            {
                this.progressBar.Value = result;
            });

            PhotoImportDeleteImportedItemsFromSourceResult deleteResult = await this.importedResult.DeleteImportedItemsFromSourceAsync().AsTask(cts.Token, progress);

            if (deleteResult != null)
            {
                StringBuilder deletedResults = new StringBuilder();
                deletedResults.AppendLine(String.Format("Total Photos Deleted:\t{0} ", deleteResult.PhotosCount));
                deletedResults.AppendLine(String.Format("Total Videos Deleted:\t{0} ", deleteResult.VideosCount));
                deletedResults.AppendLine(String.Format("Total Sidecars Deleted:\t{0} ", deleteResult.SidecarsCount));
                deletedResults.AppendLine(String.Format("Total Sibilings Deleted:\t{0} ", deleteResult.SiblingsCount));
                deletedResults.AppendLine(String.Format("Total Files Deleted:\t{0} ", deleteResult.TotalCount));
                deletedResults.AppendLine(String.Format("Total Bytes Deleted:\t{0} ", deleteResult.TotalSizeInBytes));
                System.Diagnostics.Debug.WriteLine(deletedResults.ToString());
            }

            if (!deleteResult.HasSucceeded)
            {
                System.Diagnostics.Debug.WriteLine("Delete operation did not succeed or was not completed");
            }
        }

    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine("Files could not be Deleted." + "Exception: " + ex.ToString());
    }

    // set the CancellationTokenSource to null when the work is complete.
    cts = null;


}