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

In Mobile Development by Christian HissibiniLeave a Comment

Cet article vous décrit la procédure d’importation de fichiers multimédias à partir d’un appareil, notamment la recherche de sources de médias disponibles, l’importation de photos et de fichiers sidecar et la suppression des fichiers importés de l’appareil source.

 Notes

Le code figurant dans cet article a été adapté de l’exemple d’application MediaImport UWP. Vous pouvez dupliquer ou télécharger cet exemple à partir du référentiel Git d’exemples d’applications Windows universelles afin d’afficher le code en contexte ou de l’utiliser en tant que point de départ pour votre propre application.

Créer une interface simple d’importation de médias

L’exemple de cet article utilise une interface utilisateur épurée prenant en charge les scénarios principaux d’importation de médias.Pour savoir comment créer une interface utilisateur plus robuste adaptée à une application d’importation de médias, consultez l’exemple MediaImport. Le code XAML suivant crée un panneau d’empilement avec les contrôles suivants:

  • Un objet Button permettant de lancer la recherche des sources à partir desquelles les médias peuvent être importés.
  • Un objet ComboBox permettant de répertorier et d’effectuer des sélections à partir des sources d’importation de fichiers multimédias identifiées.
  • Un objet ListView permettant d’afficher et de sélectionner les éléments multimédias à partir de la source d’importation sélectionnée.
  • Un élément Button permettant de lancer l’importation des éléments multimédias à partir de la source sélectionnée.
  • Un élément Button permettant de lancer la suppression des éléments importés de la source sélectionnée.
  • Un élément Button permettant d’annuler une opération d’importation asynchrone d’éléments multimédias.

XMLCopier

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

Configurer votre fichier code-behind

Ajoutez des directives using afin d’inclure les espaces de noms utilisés par cet exemple qui ne sont pas encore inclus dans le modèle de projet par défaut.C#Copier

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

Configurer l’annulation des tâches associées aux opérations d’importation des fichiers multimédias

Étant donné que les opérations d’importation multimédia peuvent prendre un certain temps, elles sont exécutées de manière asynchrone à l’aide de IAsyncOperationWithProgress. Déclarez une variable de membre de classe de type CancellationTokenSourceà utiliser pour annuler une opération en cours de progression si l’utilisateur clique sur le bouton d’annulation.C#Copier

CancellationTokenSource cts;

Implémentez un gestionnaire pour le bouton d’annulation. L’exemple figurant plus loin dans cet article est dédié à l’initialisation de l’instance CancellationTokenSource au démarrage d’une opération. La valeur de cette dernière est définie sur null à l’issue. Dans le gestionnaire du bouton d’annulation, vérifiez si le jeton est défini sur null et si ce n’est pas le cas, appelez Cancel afin d’annuler l’opération.C#Copier

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’assistance de liaison de données

Dans un scénario d’importation standard d’éléments multimédias, vous présentez à l’utilisateur une liste des éléments multimédias disponibles à l’importation. Le nombre de fichiers pouvant s’avérer conséquent, vous pouvez avoir intérêt à présenter une miniature de chaque élément multimédia. Pour cette raison, cet exemple utilise troisclasses d’assistance permettant de charger de manière incrémentielle des entrées dans le contrôle ListView, à mesure que l’utilisateur fait défiler la liste.

  • Classe IncrementalLoadingBase: implémente les objets IListISupportIncrementalLoading et INotifyCollectionChanged afin de communiquer le comportement de base de chargement incrémentiel.
  • Classe GeneratorIncrementalLoadingClass: fournit une implémentation de la classe de base de chargement incrémentiel.
  • Classe ImportableItemWrapper: un wrapper fin autour de la classe PhotoImportItem afin d’ajouter une propriété BitmapImagepouvant être liée pour chacune des images miniatures associées aux éléments importés.

Ces classes sont fournies dans l’exemple MediaImport; ils peuvent être ajoutées à votre projet sans modifications. Une fois les classes d’assistance ajoutées à votre projet, déclarez une variable de membre de classe de type GeneratorIncrementalLoadingClass, qui sera utilisé plus loin dans cet exemple.C#Copier

GeneratorIncrementalLoadingClass<ImportableItemWrapper> itemsToImport = null;

Rechercher les sources disponibles à partir desquelles importer les fichiers multimédias

Dans le gestionnaire de clics du bouton de recherche des sources, appelez la méthode statique PhotoImportManager.FindAllSourcesAsync afin de démarrer sur le système la recherche des appareils à partir desquels importer des fichiers multimédias. Après avoir attendu l’exécution de l’opération, parcourez chaque objet PhotoImportSource de la liste renvoyée et ajoutez une entrée à l’instance ComboBox, en définissant la propriété Tag sur l’objet source, afin qu’il puisse être facilement récupéré lorsque l’utilisateur effectue une sélection.C#Copier

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 de membre de classe dédiée au stockage de la source d’importation sélectionnée de l’utilisateur.C#Copier

PhotoImportSource importSource;

Dans le gestionnaire SelectionChanged de la source d’importation ComboBox, définissez la variable de membre de classe sur la source sélectionnée, puis appelez la méthode d’assistance FindItems, décrite plus loin dans cet article.C#Copier

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

Rechercher des éléments à importer

Ajoutez des variables de membre de classe de type PhotoImportSession et PhotoImportFindItemsResult à utiliser dans les étapes suivantes.C#Copier

PhotoImportSession importSession;
PhotoImportFindItemsResult itemsResult;

Dans la méthode FindItems, initialisez la variable CancellationTokenSource afin de pouvoir l’utiliser pour annuler l’opération de recherche, si nécessaire. Dans un bloc try, 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 afin de fournir un rappel prenant en charge l’affichage de l’avancement de l’opération de recherche. Ensuite, appelez FindItemsAsync pour démarrer l’opération de recherche.Fournissez une valeur PhotoImportContentTypeFilter afin de spécifier si des photos ou des vidéos (ou les deux) sont à renvoyer.Fournissez une valeur PhotoImportItemSelectionMode afin de spécifier si l’ensemble des éléments multimédias, aucun d’entre eux ou uniquement les nouvelles instances sont renvoyés avec la propriété IsSelected définie sur true. Cette propriété est liée à une case à cocher pour chaque élément multimédia de notre modèle d’élément ListBox.

FindItemsAsync renvoie une valeur IAsyncOperationWithProgress. La méthode d’extension AsTask est utilisée pour créer une tâche pouvant être attendue ou pouvant être annulée avec le jeton d’annulation. Elle fait état de l’avancement à l’aide de l’objet Progressfourni.

En regard de la classe d’assistance de liaison des données, la classe GeneratorIncrementalLoadingClass est initialisée.FindItemsAsync, lorsqu’il revient d’avoir été attendu, renvoie un objet PhotoImportFindItemsResult. Cet objet comporte les informations de statut sur l’opération de recherche, notamment sur sa réussite et le nombre des différents types d’éléments multimédias identifiés. La propriété FoundItems contient une liste des objets PhotoImportItem représentant les éléments multimédias identifiés. Le constructeur GeneratorIncrementalLoadingClass prend en tant qu’arguments le nombre total d’éléments à charger de manière incrémentielle et une fonction qui génère de nouveaux éléments à charger au besoin. Dans ce cas, l’expression lambda fournie génère une nouvelle instance de l’objet ImportableItemWrapper, qui enveloppe PhotoImportItem et inclut une miniature pour chaque élément. Une fois que la classe de chargement incrémentielle a été initialisée, définissez-la sur la propriété ItemsSource du contrôle ListView de l’interface utilisateur. À présent, les éléments multimédias identifiés seront chargés de manière incrémentielle et chargés dans la liste.

Ensuite, les informations de statut de l’opération de recherche sont affichées. Une application standard affiche ces informations à l’utilisateur dans l’interface, mais cet exemple transmet simplement les informations à la console de débogage. Enfin, définissez le jeton d’annulation sur null, dans la mesure où l’opération est terminée.C#Copier

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 d’implémenter l’opération d’importation, déclarez un objet PhotoImportImportItemsResult dédié au stockage des résultats de l’opération d’importation. Il sera utilisé plus tard pour supprimer les éléments multimédias importés à partir de la source.C#Copier

private PhotoImportImportItemsResult importedResult;

Avant de démarrer l’opération d’importation d’éléments multimédias, initialisez la variable CancellationTokenSource en définissant la valeur du contrôle ProgressBar sur 0.

Si le contrôle ListView ne présente aucun élément sélectionné, l’importation ne peut pas avoir lieu. Sinon, initialisez un objet Progress afin de fournir un rappel d’avancement qui met à jour la valeur du contrôle de la barre d’avancement. Enregistrez un gestionnaire pour l’événement ItemImported de la classe PhotoImportFindItemsResult renvoyée par l’opération de recherche. Cet événement est déclenché chaque fois qu’un élément est importé et, dans cet exemple, génère le nom de chaque fichier importé sur la console de débogage.

Appelez ImportItemsAsync afin de démarrer l’opération d’importation. Comme c’est le cas avec l’opération de recherche, la méthode d’extension AsTask est utilisée pour convertir l’opération renvoyée en tâche qui peut être attendue, annulée et qui consigne l’avancement.

À l’issue de l’opération d’importation, le statut d’opération peut être obtenu à partir de l’objet PhotoImportImportItemsResultrenvoyé par ImportItemsAsync. Cet exemple affiche les informations de statut sur la console de débogage puis, en définitive, définit le jeton d’annulation sur null.C#Copier

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

Supprimer des éléments importés

Pour supprimer les éléments importés de la source à partir de laquelle ils ont été importés, initialisez le jeton d’annulation, de manière à ce que l’opération de suppression puisse être annulée et définissez la valeur de la barre d’avancement sur 0. Assurez-vous que l’instance PhotoImportImportItemsResult renvoyée de ImportItemsAsync n’est pas définie sur null. Si ce n’est pas le cas, créez de nouveau un objet Progress afin de fournir un rappel de progression associé à l’opération de suppression. Appelez la méthode DeleteImportedItemsFromSourceAsync afin de démarrer la suppression des éléments importés. Utilisez AsTask pour convertir le résultat en une tâche pouvant être attendue à l’aide de fonctionnalités de progression et d’annulation. Après l’attente, l’objet PhotoImportDeleteImportedItemsFromSourceResult renvoyé peut être utilisé pour récupérer et afficher les informations de statut sur l’opération de suppression.C#Copier


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;


}

Leave a Comment