Condividi tramite


Importare elementi multimediali da un dispositivo

In questo articolo viene descritto come importare file multimediali da un dispositivo, inclusa la ricerca di origini multimediali disponibili, l'importazione di file come video, foto e file collaterali e l'eliminazione dei file importati dal dispositivo di origine.

Nota

Il codice in questo articolo è stato adattato dall'esempio di app UWP MediaImport. Si può clonare o scaricare questo esempio dal repository Git degli esempi di app di Windows universale per visualizzare il codice nel contesto o usarlo come punto di partenza per l'app.

Creare un'interfaccia utente di importazione multimediale semplice

L'esempio in questo articolo usa un'interfaccia utente minima per abilitare gli scenari di importazione dei supporti di base. Per informazioni su come creare un'interfaccia utente più affidabile per un'app di importazione multimediale, vedere l'esempio MediaImport. Il codice XAML seguente crea un pannello stack con i controlli seguenti:

  • Un pulsante per avviare la ricerca di origini da cui è possibile importare i supporti.
  • Una Casella combinata da elencare e selezionare tra le origini di importazione multimediale trovate.
  • Un controllo ListView da visualizzare e selezionare dagli elementi multimediali dall'origine di importazione selezionata.
  • Un pulsante per avviare l'importazione di elementi multimediali dall'origine selezionata.
  • Un pulsante per avviare l'eliminazione degli elementi importati dall'origine selezionata.
  • Un pulsante per annullare un'operazione asincrona di importazione multimediale.
<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>

Definire il file del codice che sta dietro

Aggiungere direttive using per includere gli spazi dei nomi usati da questo esempio che non sono già inclusi nel modello di progetto predefinito.

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

Configurare l'annullamento delle attività per le operazioni di importazione multimediale

Poiché le operazioni di importazione multimediale possono richiedere molto tempo, vengono eseguite in modo asincrono usando IAsyncOperationWithProgress. Dichiarare una variabile membro della classe di tipo CancellationTokenSource che verrà utilizzata per annullare un'operazione in corso se l'utente fa clic sul pulsante Annulla.

CancellationTokenSource cts;

Implementare un handler per il pulsante annulla. Gli esempi illustrati più avanti in questo articolo inizializzeranno CancellationTokenSource all'avvio di un'operazione e lo impostano su Null al termine dell'operazione. Nel gestore del pulsante annulla verificare se il token è Null e, in caso contrario, chiamare Cancel per annullare l'operazione.

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

Classi helper di data binding

In uno scenario tipico di importazione multimediale che mostra all'utente un elenco di elementi multimediali disponibili da importare, può essere presente un numero elevato di file multimediali tra cui scegliere e, in genere, si vuole visualizzare un'anteprima per ogni elemento multimediale. Per questo motivo, questo esempio usa tre classi helper per caricare in modo incrementale le voci nel controllo ListView mentre l'utente scorre verso il basso nell'elenco.

  • Classe IncrementalLoadingBase: Implementa IList, ISupportIncrementalLoading e INotifyCollectionChanged per fornire il comportamento di caricamento incrementale di base.
  • Classe GeneratorIncrementalLoadingClass: fornisce un'implementazione della classe base di caricamento incrementale.
  • Classe ImportableItemWrapper: wrapper sottile intorno alla classe PhotoImportItem per aggiungere una proprietà BitmapImage associabile per l'immagine di anteprima per ogni elemento importato.

Queste classi vengono fornite nell'esempio MediaImport e possono essere aggiunte al progetto senza modifiche. Dopo aver aggiunto le classi helper al progetto, dichiarare una variabile membro della classe di tipo GeneratorIncrementalLoadingClass che verrà usata più avanti in questo esempio.

GeneratorIncrementalLoadingClass<ImportableItemWrapper> itemsToImport = null;

Trovare le origini disponibili da cui è possibile importare i supporti

Nel gestore clic per il pulsante Trova origini chiamare il metodo statico PhotoImportManager.FindAllSourcesAsync per avviare la ricerca di dispositivi da cui è possibile importare i supporti. Dopo aver atteso il completamento dell'operazione, scorrere ogni oggetto PhotoImportSource nell'elenco restituito e aggiungere una voce a ComboBox, impostando la proprietà Tag sull'oggetto di origine stesso in modo che possa essere facilmente recuperata quando l'utente effettua una selezione.

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

Dichiarare una variabile membro della classe per archiviare l'origine di importazione selezionata dell'utente.

PhotoImportSource importSource;

Nel gestore SelectionChanged per il controllo di origine di importazione ComboBox impostare la variabile membro della classe sull'origine selezionata e quindi chiamare il metodo helper FindItems che verrà illustrato più avanti in questo articolo.

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

Trovare elementi da importare

Aggiungere variabili membro di classe di tipo PhotoImportSession and PhotoImportFindItemsResult che verranno usate nei passaggi seguenti.

PhotoImportSession importSession;
PhotoImportFindItemsResult itemsResult;

Nel metodo FindItems inizializzare la variabile CancellationTokenSource in modo che possa essere usata per annullare l'operazione di ricerca, se necessario. All'interno di un blocco try, creare una nuova sessione di importazione chiamando CreateImportSession sull'oggetto PhotoImportSource selezionato dall'utente. Creare un nuovo oggetto Progress per fornire un callback per visualizzare lo stato di avanzamento dell'operazione di ricerca. Chiamare quindi FindItemsAsync per avviare l'operazione di ricerca. Specificare un valore PhotoImportContentTypeFilter per specificare se devono essere restituiti foto, video o entrambi. Specificare un valore PhotoImportItemSelectionMode per specificare se tutti, nessuno o solo i nuovi elementi multimediali vengono restituiti con la proprietà IsSelected impostata su true. Questa proprietà è associata a una casella di controllo per ogni elemento multimediale nel modello di elemento ListBox.

FindItemsAsync restituisce un oggetto IAsyncOperationWithProgress. Il metodo di estensione AsTask viene usato per creare un'attività che può essere attesa, può essere annullata con il token di annullamento e che segnala lo stato di avanzamento usando l'oggetto Progress fornito.

Successivamente viene inizializzata la classe helper data binding GeneratorIncrementalLoadingClass. FindItemsAsync, quando viene restituito dall'attesa, restituisce un oggetto PhotoImportFindItemsResult. Questo oggetto contiene informazioni sullo stato sull'operazione di ricerca, tra cui l'esito positivo dell'operazione e il conteggio di diversi tipi di elementi multimediali trovati. La proprietà FoundItems contiene un elenco di oggetti PhotoImportItem che rappresentano gli elementi multimediali trovati. Il costruttore GeneratorIncrementalLoadingClass accetta come argomenti il numero totale di elementi che verranno caricati in modo incrementale e una funzione che genera nuovi elementi da caricare in base alle esigenze. In questo caso, l'espressione lambda fornita crea una nuova istanza di ImportableItemWrapper che esegue il wrapping di PhotoImportItem e include un'anteprima per ogni elemento. Dopo aver inizializzato la classe di caricamento incrementale, impostarla sulla proprietà ItemsSource del controllo ListView nell'interfaccia utente. A questo punto, gli elementi multimediali trovati verranno caricati in modo incrementale e visualizzati nell'elenco.

Successivamente, vengono restituite le informazioni sullo stato dell'operazione di ricerca. Un'app tipica visualizzerà queste informazioni all'utente nell'interfaccia utente, ma questo esempio restituisce semplicemente le informazioni nella console di debug. Impostare infine il token di annullamento su Null perché l'operazione è stata completata.

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

Importare elementi multimediali

Prima di implementare l'operazione di importazione, dichiarare un oggetto PhotoImportImportItemsResult per archiviare i risultati dell'operazione di importazione. Questa operazione verrà usata in un secondo momento per eliminare gli elementi multimediali importati correttamente dall'origine.

private PhotoImportImportItemsResult importedResult;

Prima di avviare l'operazione di importazione multimediale inizializzare la variabile CancellationTokenSource e impostando il valore del controllo ProgressBar a 0.

Se non sono presenti elementi selezionati nel controllo ListView, non è necessario importare alcun elemento. In caso contrario, inizializzare un oggetto Progress per fornire un callback di stato che aggiorna il valore del controllo indicatore di stato. Registrare un gestore per l'evento ItemImported di PhotoImportFindItemsResult restituito dall'operazione di ricerca. Questo evento verrà generato ogni volta che un elemento viene importato e, in questo esempio, restituisce il nome di ogni file importato nella console di debug.

Chiamare ImportItemsAsync per avviare l'operazione di importazione. Analogamente all'operazione find, il metodo di estensione AsTask viene usato per convertire l'operazione restituita in un'attività che può essere attesa, segnala lo stato di avanzamento e può essere annullata.

Al termine dell'operazione di importazione, lo stato dell'operazione può essere ottenuto dall'oggetto PhotoImportImportItemsResult restituito da ImportItemsAsync. In questo esempio vengono restituite le informazioni sullo stato nella console di debug e infine il token di annullamento viene impostato su Null.

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

Eliminare elementi importati

Per eliminare gli elementi importati correttamente dall'origine da cui sono stati importati, inizializzare prima il token di annullamento in modo che l'operazione di eliminazione possa essere annullata e impostare il valore dell'indicatore di stato su 0. Assicurarsi che PhotoImportImportItemsResult restituito da ImportItemsAsync non sia null. In caso contrario, creare nuovamente un oggetto Progress per fornire un callback di stato per l'operazione di eliminazione. Chiamare DeleteImportedItemsFromSourceAsync per iniziare a eliminare gli elementi importati. Us AsTask per convertire il risultato in un'attività awaitable con funzionalità di avanzamento e annullamento. Dopo l'attesa, è possibile utilizzare l'oggetto PhotoImportDeleteImportedItemsFromSourceResult per ottenere e visualizzare informazioni sullo stato relative all'operazione di eliminazione.


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;


}