Remarque
L’accès à cette page requiert une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page requiert une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article explique comment importer des médias à partir d’un appareil, notamment la recherche de sources multimédias disponibles, l’importation de fichiers tels que des vidéos, des photos et des fichiers sidecar, et la suppression des fichiers importés à partir de l’appareil source.
Note
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 depuis le dépôt Git des exemples d’applications Windows universelles pour voir le code dans son contexte ou l’utiliser comme point de départ pour votre propre application.
Créer une interface utilisateur d’importation de média simple
L’exemple de cet article utilise une interface utilisateur minimale pour activer les scénarios d’importation multimédia principaux. Pour savoir comment créer une interface utilisateur plus robuste pour une application d’importation multimédia, consultez l’exemple MediaImport. Le code XAML suivant crée un StackPanel contenant les contrôles suivants :
- Bouton permettant de lancer la recherche de sources à partir desquelles le média peut être importé.
- ComboBox permettant d’afficher et de sélectionner les sources d’importation de médias détectées.
- Contrôle ListView à afficher et sélectionner parmi les éléments multimédias de la source d’importation sélectionnée.
- Bouton permettant de lancer l’importation d’éléments multimédias à partir de la source sélectionnée.
- Bouton permettant de lancer la suppression des éléments importés à partir de la source sélectionnée.
- Bouton pour annuler une opération d’importation de média asynchrone.
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Padding="24" Spacing="16">
<TextBlock
Text="Import media from device"
Style="{StaticResource SubtitleTextBlockStyle}" />
<TextBlock
Text="Choose a connected device source, review the files that can be imported, and then import or delete imported files."
TextWrapping="WrapWholeWords" />
<StackPanel Spacing="8">
<TextBlock
Text="Source"
Style="{StaticResource BodyStrongTextBlockStyle}" />
<Button
x:Name="findSourcesButton"
Content="Find sources"
Click="findSourcesButton_Click" />
<ComboBox
x:Name="sourcesComboBox"
PlaceholderText="Select a device source"
SelectionChanged="sourcesComboBox_SelectionChanged" />
</StackPanel>
<StackPanel Spacing="8">
<TextBlock
Text="Importable items"
Style="{StaticResource BodyStrongTextBlockStyle}" />
<ListView
x:Name="fileListView"
MinHeight="320"
MaxHeight="480"
SelectionMode="None"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListView.ItemTemplate>
<DataTemplate>
<Grid ColumnSpacing="12" Padding="0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox
Grid.Column="0"
VerticalAlignment="Center"
IsChecked="{Binding ImportableItem.IsSelected, Mode=TwoWay}" />
<Image
Grid.Column="1"
Width="120"
Height="120"
Source="{Binding Thumbnail}"
Stretch="UniformToFill" />
<TextBlock
Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding ImportableItem.Name}"
TextWrapping="WrapWholeWords" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<StackPanel Spacing="8">
<TextBlock
Text="Import actions"
Style="{StaticResource BodyStrongTextBlockStyle}" />
<StackPanel Orientation="Horizontal" Spacing="12">
<Button
x:Name="importButton"
Content="Import"
Click="importButton_Click" />
<Button
x:Name="deleteButton"
Content="Delete imported"
Click="deleteButton_Click" />
<Button
x:Name="cancelButton"
Content="Cancel"
Click="cancelButton_Click" />
</StackPanel>
<ProgressBar
x:Name="progressBar"
Minimum="0"
Maximum="1"
ShowError="False"
ShowPaused="False" />
</StackPanel>
<TextBlock
x:Name="statusTextBlock"
Style="{StaticResource CaptionTextBlockStyle}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="WrapWholeWords" />
</StackPanel>
</ScrollViewer>
Configurer l’annulation de tâche pour les opérations d’importation multimédia
Étant donné que les opérations d’importation multimédia peuvent prendre beaucoup de temps, elles sont effectuées de manière asynchrone à l’aide de IAsyncOperationWithProgress. Déclarez une variable membre de classe de type CancellationTokenSource qui sera utilisée pour annuler une opération en cours si l’utilisateur clique sur le bouton Annuler.
CancellationTokenSource? cts;
Implémentez un gestionnaire pour le bouton Annuler. Les exemples présentés plus loin dans cet article initialisent l’cancellationTokenSource lorsqu’une opération commence et la définissent sur Null une fois l’opération terminée. Dans le gestionnaire de boutons d’annulation, vérifiez si le jeton est null et, si ce n’est pas le cas, appelez Annuler pour annuler l’opération.
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
if (cts is not null)
{
cts.Cancel();
SetStatus("Operation canceled by the Cancel button.");
}
}
Classes utilitaires de liaison de données
Dans un scénario d’importation multimédia classique, vous affichez à l’utilisateur une liste d’éléments multimédias disponibles à importer. Il peut y avoir un grand nombre de fichiers multimédias à choisir et, généralement, vous souhaitez afficher une miniature pour chaque élément multimédia. Pour cette raison, cet exemple utilise trois classes d’assistance pour charger de manière incrémentielle des entrées dans le contrôle ListView lorsque l’utilisateur fait défiler la liste vers le bas.
- Classe IncrementalLoadingBase : implémente la classe IList, ISupportIncrementalLoading et INotifyCollectionChanged pour fournir le comportement de chargement incrémentiel de base.
- Classe GeneratorIncrementalLoadingClass : fournit une implémentation de la classe de base de chargement incrémentielle.
- ImportableItemWrapper classe - Wrapper léger autour de la classe PhotoImportItem pour ajouter une propriété BitmapImage pouvant être liée 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’assistance à votre projet, déclarez une variable membre de classe de type GeneratorIncrementalLoadingClass qui sera utilisée plus loin dans cet exemple.
GeneratorIncrementalLoadingClass<ImportableItemWrapper>? itemsToImport = null;
Rechercher les sources disponibles à partir desquelles le média peut être importé
Dans le gestionnaire de clics pour le bouton rechercher des sources, appelez la méthode statique PhotoImportManager.FindAllSourcesAsync pour démarrer le système à la recherche d’appareils à partir desquels le média peut être importé. Après avoir attendu la fin de l’opération, parcourez chaque objet PhotoImportSource de la liste renvoyée et ajoutez une entrée à la ComboBox, en définissant la propriété Tag sur l’objet source lui-même afin de pouvoir récupérer facilement cet objet lorsque l’utilisateur effectue une sélection.
private async void findSourcesButton_Click(object sender, RoutedEventArgs e)
{
var sources = await PhotoImportManager.FindAllSourcesAsync();
sourcesComboBox.Items.Clear();
foreach (PhotoImportSource source in sources)
{
ComboBoxItem item = new();
item.Content = source.DisplayName;
item.Tag = source;
sourcesComboBox.Items.Add(item);
}
SetStatus($"Found {sources.Count} import source(s).");
}
Déclarez une variable membre de classe pour stocker la source d’importation sélectionnée de l’utilisateur.
PhotoImportSource? importSource;
Dans le gestionnaire SelectionChanged pour la zone comboBox source d’importation, définissez la variable membre de classe sur la source sélectionnée, puis appelez la méthode d’assistance FindItems qui sera affichée plus loin dans cet article.
private void sourcesComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sourcesComboBox.SelectedItem is ComboBoxItem item && item.Tag is PhotoImportSource selectedSource)
{
importSource = selectedSource;
FindItems();
}
}
Rechercher des éléments à importer
Ajoutez des variables membres 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 try, créez une session d’importation en appelant CreateImportSession sur l’objet PhotoImportSource sélectionné par l’utilisateur. Créez un objet Progress pour fournir un rappel pour afficher la progression de l’opération de recherche. Ensuite, appelez FindItemsAsync pour démarrer l’opération de recherche. Fournissez une valeur PhotoImportContentTypeFilter pour spécifier si des photos, des vidéos ou les deux doivent être retournées. Fournissez une valeur PhotoImportItemSelectionMode pour spécifier si tous, aucun ou uniquement les nouveaux éléments multimédias sont retourné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 ListView.
FindItemsAsync retourne un IAsyncOperationWithProgress. La méthode d’extension AsTask est utilisée pour créer une tâche qui peut être attendue, peut être annulée avec le jeton d’annulation et qui signale la progression à l’aide de l’objet Progress fourni.
Ensuite, la classe d’assistance de liaison de données, GeneratorIncrementalLoadingClass est initialisée. FindItemsAsync, une fois l’attente terminée, renvoie un objet PhotoImportFindItemsResult. Cet objet contient des informations d’état sur l’opération de recherche, notamment la réussite 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 GeneratorIncrementalLoadingClass prend en tant qu’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 l’importableItemWrapper qui encapsule PhotoImportItem et inclut une miniature pour chaque élément. Une fois la classe de chargement incrémentielle initialisée, définissez-la sur la propriété ItemsSource du contrôle ListView dans l’interface utilisateur. À présent, les éléments multimédias trouvés sont chargés de manière incrémentielle et affichés dans la liste.
Enfin, définissez le jeton d’annulation sur Null, car l’opération est terminée.
private async void FindItems()
{
if (importSource is null)
{
SetStatus("Select an import source first.");
return;
}
cts = new CancellationTokenSource();
try
{
importSession = importSource.CreateImportSession();
var progress = new Progress<uint>((result) =>
{
SetStatus($"Found {result} file(s)...");
});
var currentItemsResult =
await importSession.FindItemsAsync(PhotoImportContentTypeFilter.ImagesAndVideos, PhotoImportItemSelectionMode.SelectAll)
.AsTask(cts.Token, progress);
itemsResult = currentItemsResult;
itemsToImport = new GeneratorIncrementalLoadingClass<ImportableItemWrapper>(currentItemsResult.TotalCount,
(int index) =>
{
return new ImportableItemWrapper(currentItemsResult.FoundItems[index]);
});
fileListView.ItemsSource = itemsToImport;
var findResultProperties = new StringBuilder();
findResultProperties.AppendLine($"Photos\t\t\t : {currentItemsResult.PhotosCount} \t\t Selected Photos\t\t: {currentItemsResult.SelectedPhotosCount}");
findResultProperties.AppendLine($"Videos\t\t\t : {currentItemsResult.VideosCount} \t\t Selected Videos\t\t: {currentItemsResult.SelectedVideosCount}");
findResultProperties.AppendLine($"SideCars\t\t : {currentItemsResult.SidecarsCount} \t\t Selected Sidecars\t: {currentItemsResult.SelectedSidecarsCount}");
findResultProperties.AppendLine($"Siblings\t\t\t : {currentItemsResult.SiblingsCount} \t\t Selected Sibilings\t: {currentItemsResult.SelectedSiblingsCount}");
findResultProperties.AppendLine($"Total Items Items\t : {currentItemsResult.TotalCount} \t\t Selected TotalCount \t: {currentItemsResult.SelectedTotalCount}");
System.Diagnostics.Debug.WriteLine(findResultProperties.ToString());
if (currentItemsResult.HasSucceeded)
{
SetStatus("FindItemsAsync succeeded.");
}
else
{
SetStatus("FindItemsAsync did not succeed or was not completed.");
}
}
catch (Exception ex)
{
SetStatus("Photo import find items operation failed. " + ex.Message);
}
cts = null;
}
Importer des éléments multimédias
Avant d’implémenter l’opération d’importation, déclarez un objet PhotoImportImportItemsResult pour stocker les résultats de l’opération d’importation. Cette opération sera utilisée ultérieurement pour supprimer les éléments multimédias qui ont été importés avec succès à partir de la source.
private PhotoImportImportItemsResult? importedResult;
Avant de démarrer l’opération d’importation multimédia, initialisez la variable CancellationTokenSource et définissez la valeur du contrôle ProgressBar sur 0.
S’il n’existe aucun élément sélectionné dans le contrôle ListView , il n’y a rien à importer. Sinon, initialisez un objet Progress pour fournir un rappel de progression qui met à jour la valeur du contrôle de barre de progression. Enregistrez un gestionnaire pour l’événement ItemImported de 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, génère le nom de chaque fichier importé dans la console de débogage.
Appelez ImportItemsAsync pour commencer l’opération d’importation. Tout comme avec l’opération de recherche, la méthode d’extension AsTask est utilisée pour convertir l’opération retournée en tâche qui peut être attendue, signale la progression et 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 PhotoImportItemsResult retourné par ImportItemsAsync. Cet exemple génère les informations d’état dans la console de débogage, puis enfin, définit le jeton d’annulation sur Null.
private async void importButton_Click(object sender, RoutedEventArgs e)
{
if (itemsResult is null)
{
SetStatus("Find importable items before importing.");
return;
}
cts = new CancellationTokenSource();
progressBar.Value = 0;
try
{
if (itemsResult.SelectedTotalCount <= 0)
{
SetStatus("Nothing Selected for Import.");
}
else
{
var progress = new Progress<PhotoImportProgress>((result) =>
{
progressBar.Value = result.ImportProgress;
});
itemsResult.ItemImported += (s, a) =>
{
DispatcherQueue.TryEnqueue(() =>
{
System.Diagnostics.Debug.WriteLine($"Imported: {a.ImportedItem.Name}");
});
};
importedResult = await itemsResult.ImportItemsAsync().AsTask(cts.Token, progress);
if (importedResult is not null)
{
StringBuilder importedSummary = new();
importedSummary.AppendLine($"Photos Imported \t: {importedResult.PhotosCount} ");
importedSummary.AppendLine($"Videos Imported \t: {importedResult.VideosCount} ");
importedSummary.AppendLine($"SideCars Imported \t: {importedResult.SidecarsCount} ");
importedSummary.AppendLine($"Siblings Imported \t: {importedResult.SiblingsCount} ");
importedSummary.AppendLine($"Total Items Imported \t: {importedResult.TotalCount} ");
importedSummary.AppendLine($"Total Bytes Imported \t: {importedResult.TotalSizeInBytes} ");
System.Diagnostics.Debug.WriteLine(importedSummary.ToString());
}
if (importedResult is null || !importedResult.HasSucceeded)
{
SetStatus("ImportItemsAsync did not succeed or was not completed");
}
else
{
SetStatus("Import completed.");
}
}
}
catch (Exception ex)
{
SetStatus("Files could not be imported. Exception: " + ex);
}
cts = null;
}
Supprimer les éléments importés
Pour supprimer les éléments importés avec succès à partir 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. Vérifiez que le PhotoImportImportItemsResult retourné par ImportItemsAsync n’est pas null. Si ce n’est pas le cas, créez à nouveau un objet Progress pour 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 tâche attendue avec des fonctionnalités de progression et d’annulation. Une fois l’attente terminée, l’objet PhotoImportDeleteImportedItemsFromSourceResult 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 is null)
{
SetStatus("Nothing was imported for deletion.");
}
else
{
var progress = new Progress<double>((result) =>
{
progressBar.Value = result;
});
PhotoImportDeleteImportedItemsFromSourceResult? deleteResult = await importedResult.DeleteImportedItemsFromSourceAsync().AsTask(cts.Token, progress);
if (deleteResult is not null)
{
StringBuilder deletedResults = new();
deletedResults.AppendLine($"Total Photos Deleted:\t{deleteResult.PhotosCount} ");
deletedResults.AppendLine($"Total Videos Deleted:\t{deleteResult.VideosCount} ");
deletedResults.AppendLine($"Total Sidecars Deleted:\t{deleteResult.SidecarsCount} ");
deletedResults.AppendLine($"Total Sibilings Deleted:\t{deleteResult.SiblingsCount} ");
deletedResults.AppendLine($"Total Files Deleted:\t{deleteResult.TotalCount} ");
deletedResults.AppendLine($"Total Bytes Deleted:\t{deleteResult.TotalSizeInBytes} ");
System.Diagnostics.Debug.WriteLine(deletedResults.ToString());
}
if (deleteResult is null || !deleteResult.HasSucceeded)
{
SetStatus("Delete operation did not succeed or was not completed");
}
else
{
SetStatus("Delete operation completed.");
}
}
}
catch (Exception ex)
{
SetStatus("Files could not be deleted. Exception: " + ex);
}
cts = null;
}
Rubriques connexes
- PhotoImportManager
- exemple d’application MediaImport UWP
Windows developer