Partilhar via


Importar mídia de um dispositivo

Este artigo descreve como importar mídia de um dispositivo, incluindo a pesquisa de fontes de mídia disponíveis, a importação de arquivos como fotos e vídeos arquivos acessórios e a exclusão dos arquivos importados do dispositivo de origem.

Observação

O código neste artigo foi adaptado do exemplo de aplicativo MediaImport UWP. Você pode clonar ou baixar esse exemplo do repositório Git de exemplos de aplicativos universais do Windows para ver o código no contexto ou usá-lo como ponto de partida para seu próprio aplicativo.

Criar uma interface do usuário de importação de mídia simples

O exemplo neste artigo usa uma interface do usuário mínima para habilitar os cenários de importação de mídia principal. Para ver como criar uma interface do usuário mais robusta para um aplicativo de importação de mídia, consulte o exemplo MediaImport. O XAML a seguir cria um painel de pilha com os seguintes controles:

  • Um Button para iniciar a pesquisa de fontes das quais a mídia pode ser importada.
  • Um ComboBox para listar e selecionar entre as fontes de importação de mídia encontradas.
  • Um controle ListView para exibir e selecionar itens de mídia da fonte de importação selecionada.
  • Um Button para iniciar a importação de itens de mídia da fonte selecionada.
  • Um Button para iniciar a exclusão dos itens que foram importados da fonte selecionada.
  • Um Button para cancelar uma operação de importação de mídia assíncrona.
<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>

Definir o arquivo code-behind

Adicione diretivas using para incluir os namespaces usados por este exemplo que ainda não estão incluídos no modelo de projeto padrão.

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

Configurar o cancelamento de tarefas para operações de importação de mídia

Como as operações de importação de mídia podem levar muito tempo, elas são executadas de forma assíncrona usando IAsyncOperationWithProgress. Declare uma variável de membro de classe do tipo CancellationTokenSource a ser usada para cancelar uma operação em andamento se o usuário clicar no botão cancelar.

CancellationTokenSource cts;

Implemente um manipulador para o botão de cancelar. Os exemplos mostrados posteriormente neste artigo inicializarão o CancellationTokenSource quando uma operação começar e defini-lo como null quando a operação for concluída. No manipulador do botão de cancelar, verifique se o token é nulo e, se não, chame Cancel para cancelar a operação.

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

Classes auxiliares de vinculação de dados

Em um cenário típico de importação de mídia em que você mostra ao usuário uma lista de itens de mídia disponíveis para importar, pode haver um grande número de arquivos de mídia para escolher e, normalmente, você deseja mostrar uma miniatura para cada item de mídia. Por esse motivo, este exemplo usa três classes auxiliares para carregar entradas incrementalmente no controle ListView à medida que o usuário rola a tela para baixo pela lista.

  • Classe IncrementalLoadingBase — Implementa o IList, ISupportIncrementalLoading e INotifyCollectionChanged para fornecer o comportamento de carregamento incremental base.
  • Classe GeneratorIncrementalLoadingClass – Fornece uma implementação da classe base de carregamento incremental.
  • Classe ImportableItemWrapper — Um wrapper fino em torno da classe PhotoImportItem para adicionar uma propriedade BitmapImage para a imagem em miniatura de cada item importado.

Essas classes são fornecidas no MediaImport sample e podem ser adicionadas ao seu projeto sem modificações. Depois de adicionar as classes auxiliares ao seu projeto, declare uma variável de membro de classe do tipo GeneratorIncrementalLoadingClass, que será usada posteriormente neste exemplo.

GeneratorIncrementalLoadingClass<ImportableItemWrapper> itemsToImport = null;

Localizar fontes disponíveis das quais a mídia pode ser importada

No manipulador de cliques para o botão de localizar fontes, chame o método estático PhotoImportManager.FindAllSourcesAsync para iniciar o sistema procurando dispositivos dos quais a mídia pode ser importada. Depois de aguardar a conclusão da operação, faça um loop por cada objeto PhotoImportSource na lista retornada e adicione uma entrada à ComboBox, definindo a propriedade propriedade como o próprio objeto de origem para que ele possa ser facilmente recuperado quando o usuário fizer uma seleção.

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

Declare uma variável de membro de classe para armazenar a origem de importação selecionada do usuário.

PhotoImportSource importSource;

No manipulador SelectionChanged para a fonte de importação ComboBox, defina a variável de membro da classe para a fonte selecionada e, em seguida, chame o método auxiliar FindItems, que será mostrado posteriormente neste artigo.

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

Localizar itens para importar

Adicione variáveis de membro de classe do tipo PhotoImportSession e PhotoImportFindItemsResult, que serão usadas nas etapas a seguir.

PhotoImportSession importSession;
PhotoImportFindItemsResult itemsResult;

No método FindItems, inicialize a variável CancellationTokenSource para que ela possa ser usada para cancelar a operação de localização, se necessário. Em um bloco try, crie uma sessão de importação chamando CreateImportSession no objeto PhotoImportSource selecionado pelo usuário. Crie um objeto Progress para fornecer um retorno de chamada e exibir o progresso da operação de localização. Em seguida, chame FindItemsAsync para iniciar a operação de localização. Forneça um valor PhotoImportContentTypeFilter para especificar se fotos, vídeos ou ambos devem ser retornados. Forneça um valor PhotoImportItemSelectionMode para especificar se todos, nenhum ou apenas os novos itens de mídia serão retornados com sua propriedade IsSelected definida como true. Essa propriedade está vinculada a uma caixa de seleção para cada item de mídia em nosso modelo de item ListBox.

FindItemsAsync retorna um IAsyncOperationWithProgress. O método de extensão AsTask é usado para criar uma tarefa que pode ser aguardada, pode ser cancelada com o token de cancelamento e que relata o progresso usando o objeto Progress fornecido.

Em seguida, a classe auxiliar de vinculação de dados, GeneratorIncrementalLoadingClass, é inicializada. FindItemsAsync, quando retorna de ser aguardado, retorna um objeto PhotoImportFindItemsResult. Este objeto contém informações de status sobre a operação de localização, incluindo o sucesso da operação e a contagem de diferentes tipos de itens de mídia que foram encontrados. A propriedade FoundItems contém uma lista de objetos PhotoImportItem que representam os itens de mídia encontrados. O construtor GeneratorIncrementalLoadingClass usa como argumentos a contagem total de itens que serão carregados incrementalmente e uma função que gera novos itens a serem carregados conforme necessário. Nesse caso, a expressão lambda fornecida cria uma nova instância do ImportableItemWrapper, que encapsula PhotoImportItem e inclui uma miniatura para cada item. Depois que a classe de carregamento incremental tiver sido inicializada, defina-a como a propriedade ItemsSource do controle ListView na interface do usuário. Os itens de mídia encontrados serão carregados incrementalmente e exibidos na lista.

Em seguida, as informações de status da operação de localização são fornecidas. Um aplicativo típico exibirá essas informações para o usuário na interface do usuário, mas este exemplo simplesmente envia as informações para o console de depuração. Finalmente, defina o token de cancelamento como null porque a operação está concluída.

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

Importar itens de mídia

Antes de implementar a operação de importação, declare um objeto PhotoImportImportItemsResult para armazenar os resultados da operação de importação. Isso será usado posteriormente para excluir itens de mídia que foram importados com êxito da origem.

private PhotoImportImportItemsResult importedResult;

Antes de iniciar a operação de importação de mídia, inicialize a variável CancellationTokenSource e defina o valor do controle ProgressBar como 0.

Se não houver itens selecionados no controle ListView, não há nada para importar. Caso contrário, inicialize um objeto Progress para fornecer um retorno de chamada progress que atualize o valor do controle da barra de progresso. Registre um manipulador para o evento ItemImported do PhotoImportFindItemsResult retornado pela operação de localização. Esse evento será gerado sempre que um item for importado e, neste exemplo, enviará o nome de cada arquivo importado para o console de depuração.

Chame ImportItemsAsync para iniciar a operação de importação. Assim como na operação find, o método de extensão AsTask é usado para converter a operação retornada em uma tarefa que pode ser aguardada, relata o progresso e pode ser cancelada.

Depois que a operação de importação for concluída, o status da operação poderá ser obtido do objeto PhotoImportImportItemsResult retornado por ImportItemsAsync. Este exemplo gera as informações de status para o console de depuração e define o token de cancelamento como 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;
}

Excluir itens importados

Para excluir os itens importados com êxito da origem da qual foram importados, primeiro inicialize o token de cancelamento para que a operação de exclusão possa ser cancelada e defina o valor da barra de progresso como 0. Certifique-se de que o PhotoImportImportItemsResult retornado de ImportItemsAsync não é nulo. Caso contrário, crie novamente um objeto Progress para fornecer um retorno de chamada de progresso para a operação de exclusão. Chame DeleteImportedItemsFromSourceAsync para iniciar a exclusão dos itens importados. Use AsTask para converter o resultado em uma tarefa aguardada com recursos de progresso e cancelamento. Depois de aguardar, o objeto PhotoImportDeleteImportedItemsFromSourceResult retornado pode ser usado para obter e exibir informações de status sobre a operação de exclusão.


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;


}