Parte 4: Habilitar selectores de archivos (aplicaciones de Windows en tiempo de ejecución con C++)

Parte 4: Habilitar selectores de archivos (aplicaciones de Windows en tiempo de ejecución con C++)

[ Este artículo está destinado a desarrolladores de Windows 8.x y Windows Phone 8.x que escriben aplicaciones de Windows Runtime. Si estás desarrollando para Windows 10, consulta la documentación más reciente ]

Esta parte final de la serie de tutoriales de C++ muestra cómo agregar un FileOpenPicker para que un usuario pueda obtener un archivo, enlazar controles de IU a datos e implementar una MostRecentlyUsedList para que el usuario pueda acceder a un archivo abierto anteriormente.

En el segundo tutorial de esta serie, Administrar el ciclo de vida y el estado de la aplicación, aprendiste sobre los datos de la aplicación y los datos de la sesión, y sobre cómo guardar estos datos en el almacenamiento ApplicationData. De manera predeterminada, tu aplicación puede acceder a algunas ubicaciones de archivo, como las ubicaciones de los datos de la aplicación, el directorio de instalación de la aplicación y los elementos que crea en la carpeta \Downloads\.

Por el contrario, los datos de usuario, como las imágenes, los vídeos y los archivos de documentos, son independientes de la aplicación y normalmente se almacenan en las carpetas de la biblioteca del usuario u otras ubicaciones del sistema de archivos. Para acceder a estas ubicaciones, tu aplicación debe declarar su capacidad de acceder a los datos mediante programación, o usar un selector de archivos para que el usuario pueda abrir el archivo manualmente. Aquí, usarás un selector de archivos para permitir el acceso a la Biblioteca de imágenes del usuario, así que no necesitas declarar ninguna capacidad de la aplicación. Para obtener más información sobre estas funcionalidades, consulta Declaraciones de funcionalidades de aplicación.

En este tutorial, agregas funcionalidad al diseño de la página de fotos que creaste en la Parte 3: Agregar navegación y vistas Primero, administras el evento de clic de botón "Get photo" para que abra un selector de archivos y permita al usuario seleccionar una imagen de la Biblioteca de imágenes. A continuación, vas a enlazar los controles de la interfaz de usuario a las propiedades del archivo para mostrar la información de la imagen. Por último, repasamos lo que aprendiste en el segundo tutorial sobre cómo guardar el estado de la aplicación. Vas a usar MostRecentlyUsedList para mantener el acceso a la imagen seleccionada por el usuario.

Antes de comenzar...

Paso 1: Usar un selector de archivos para obtener un archivo de imagen (Windows 8.1)

A través del selector de archivos, la aplicación puede acceder a archivos y carpetas en cualquier parte del sistema del usuario, según la selección del usuario. Cuando se llama al selector de archivos, el usuario puede explorar archivos (o carpetas) para acceder a ellos y guardarlos. Después de que el usuario selecciona archivos o carpetas, la aplicación recibe estas selecciones como objetos StorageFile y StorageFolder. A continuación, la aplicación puede usar estos objetos para realizar operaciones en los archivos y las carpetas seleccionados.

Lo primero que tienes que hacer es administrar el evento GetPhotoButton_Click para obtener una imagen que mostrar.

Empezamos con el código de Navegación, diseño y vistas (C++).

Dn263165.wedge(es-es,WIN.10).gifPara agregar un selector de archivos

  1. En Explorador de soluciones, abre PhotoPage.xaml en el proyecto de Windows.
  2. Selecciona el Button"Get photo" .
  3. En la ventana Propiedades (F4), selecciona el botón Eventos (Botón de eventos).
  4. Busca el evento Click en la lista de eventos. En el cuadro de texto del evento, escribe "GetPhotoButton_Click" como el nombre del método que controla el evento Click.
  5. Presiona Entrar. El método de controlador de eventos se crea en PhotoPage.xaml.cpp y se abre en el editor de código, de forma que puedes agregar código que se ejecutará cuando se produzca el evento.
  6. Agrega el código siguiente al método del controlador de eventos. El código abre un selector de archivos para permitir al usuario seleccionar una imagen de la biblioteca de imágenes. Cuando el usuario elige un archivo, se define como el origen de la imagen y el contexto de datos de la página.
    
    auto openPicker = ref new Windows::Storage::Pickers::FileOpenPicker();
    openPicker->SuggestedStartLocation = Windows::Storage::Pickers::PickerLocationId::PicturesLibrary;
    openPicker->ViewMode = Windows::Storage::Pickers::PickerViewMode::Thumbnail;
    
    // Filter to include a sample subset of file types.
    openPicker->FileTypeFilter->Clear();
    openPicker->FileTypeFilter->Append(".bmp");
    openPicker->FileTypeFilter->Append(".png");
    openPicker->FileTypeFilter->Append(".jpeg");
    openPicker->FileTypeFilter->Append(".jpg");
    
    // All this work will be done asynchronously on a background thread:
    
    // Wrap the async call inside a concurrency::task object
    create_task(openPicker->PickSingleFileAsync())
    
    // Accept the unwrapped return value of previous call as input param
    .then([this](Windows::Storage::StorageFile^ file)
    {
        // file is null if user cancels the file picker.
        if (file == nullptr)
        {
            // Stop work and clean up.
            cancel_current_task();
        }
    
        // For data binding text blocks to file properties
        this->DataContext = file;
    
        // Add picked file to MostRecentlyUsedList.
        mruToken = Windows::Storage::AccessCache::StorageApplicationPermissions::MostRecentlyUsedList->Add(file);
    
        // Return the IRandomAccessStream^ object
        return file->OpenAsync(Windows::Storage::FileAccessMode::Read);
    
    }).then([this](Windows::Storage::Streams::IRandomAccessStream^ fileStream)
    {
        // Set the stream as source of the bitmap
        Windows::UI::Xaml::Media::Imaging::BitmapImage^ bitmapImage =
            ref new Windows::UI::Xaml::Media::Imaging::BitmapImage();
        bitmapImage->SetSource(fileStream);
    
        // Set the bitmap as source of the Image control
        displayImage->Source = bitmapImage;
    });
    
    
    
  7. En la parte superior de PhotoPage.xaml.cpp, agrega esta instrucción using:
    
    
    using namespace concurrency;
    
    
    
  8. Presiona F5 para compilar y ejecutar la aplicación. Navega a la página de fotos y elige en el botón "Obtener foto" para ejecutar FileOpenPicker. En el selector de fotos, selecciona una foto y luego elige el botón Abrir. Aparece la foto en la página de fotos, pero el texto de información de la foto no está actualizado. Esto lo arreglaremos en el siguiente paso.

    Esta es la apariencia de la aplicación con una imagen seleccionada:

    Página de fotos con imagen

Antes de pasar al proyecto de Windows Phone, analizaremos más atentamente el código anterior. Tras crear e inicializar un objeto FileOpenPicker, llamamos a su método asincrónico FileOpenPicker::PickSingleFileAsync. El método se ejecutará en un subproceso en segundo plano, y no podemos proceder hasta que dicho método asincrónico se complete. Sin embargo, no podemos simplemente esperar a que finalice, porque no está permitido que las aplicaciones de la Tienda bloqueen el subproceso de interfaz de usuario. Para resolver este problema, utilizamos el método concurrency::create_task. Este método se define en ppltasks.h, que se incluye en el archivo de encabezado pch.h en todos los proyectos C++. El método create_task encapsula los métodos asincrónicos de Windows en tiempo de ejecución en un objeto concurrency::task, lo que te permite llamar al método y, después, esperar en el subproceso en segundo plano y utilizar task::then para especificar una o más continuaciones, lo que invocará una función proporcionada por el usuario cuando la llamada asincrónica finalice. Generalmente, como en este caso, la función se proporciona como una expresión lambda. Si la operación asincrónica original devuelve un valor —en este caso un objeto StorageFile—, dicho resultado se encapsula en un nuevo objeto de tarea y se pasa a la continuación como parámetro de entrada. Puedes encadenar cualquier número de continuaciones de esta manera. En pocas palabras, el uso de create_task con then es la manera de consumir API asincrónicas de Windows en tiempo de ejecución en C++.

En nuestro ejemplo específico, create_task invoca el método PickSingleFileAsync, que abre el cuadro de diálogo del selector de archivos y espera a que el usuario seleccione un archivo. Cuando el usuario selecciona el botón Aceptar, el método devuelve un objeto StorageFile que representa el archivo seleccionado. La continuación espera en el subproceso en segundo plano para recibir dicho archivo como entrada, y cuando este llega, procesa la secuencia de imágenes para crear una BitmapImage y, a continuación, establece la BitmapImage como Source del control de Image en la interfaz de usuario. Asimismo, establece el archivo como el DataContext de la página. Más adelante en este tutorial vincularemos el nombre del archivo y los elementos de bloque de texto de la ruta a propiedades en el StorageFile. Todo esto sucede en un subproceso en segundo plano.

Paso 1A: Usar un selector de archivos para obtener un archivo de imagen (Windows Phone 8.1)

Hay mucha diferencia entre la manera de utilizar un selector de archivos en Windows Phone 8.1 y en Windows 8.1. En Windows Phone, cuando se abre un selector de archivos la aplicación se desactiva, por lo que debes controlar el evento OnActivated que se desencadena cuando se cierra el selector. Dicho evento informa a la aplicación de que se está activando tras una operación del selector de archivos y pasa un controlador al archivo seleccionado. En este tutorial haremos lo mínimo necesario para que la aplicación funcione. Para ver un ejemplo más completo, consulta la muestra de selector de archivos.

Dn263165.wedge(es-es,WIN.10).gifAgregar los archivos de clase ContinuationManager (Windows Phone 8.1)

  1. En el proyecto de Windows Phone, presiona Ctrl+Mayús+A para agregar un elemento nuevo. Elige archivo .h, llámalo ContinuationManager.h y pega el siguiente código:

    
    #pragma once
    
    #include <windows.h>
    
    
    namespace WAA = Windows::ApplicationModel::Activation;
    
    namespace HelloWorld
    {
        /// <summary>
        /// ContinuationManager is used to detect if the most recent activation was due
        /// to a continuation such as the FileOpenPicker or WebAuthenticationBroker
        /// </summary>
        ref class ContinuationManager sealed
        {
        private:
            WAA::IContinuationActivatedEventArgs^ args;
            bool handled;
            Platform::Guid id;
    
        public:
            void Continue(WAA::IContinuationActivatedEventArgs^ args);
            void Continue(WAA::IContinuationActivatedEventArgs^ args, Windows::UI::Xaml::Controls::Frame^ rootFrame);
            void MarkAsStale();
            WAA::IContinuationActivatedEventArgs^ GetContinuationArgs(bool includeStaleArgs);
            property Platform::Guid Id
            {
                Platform::Guid get()
                {
                    return id;
                }
            }
            property WAA::IContinuationActivatedEventArgs^ ContinuationArgs
            {
                WAA::IContinuationActivatedEventArgs^ get()
                {
                    if (handled)
                        return nullptr;
                    MarkAsStale();
                    return args;
                }
            }
        };
    
        /// <summary>
        /// Implement this interface if your page invokes the file open picker
        /// API.
        /// </summary>
        public interface class IFileOpenPickerContinuable
        {
            /// <summary>
            /// This method is invoked when the file open picker returns picked
            /// files
            /// </summary>
            /// <param name="args">Activated event args object that contains returned files from file open picker</param>
            void ContinueFileOpenPicker(WAA::FileOpenPickerContinuationEventArgs^ args);
        };
    }
    
    
  2. Abre de nuevo el cuadro de diálogo Agregar elemento nuevo y elige archivo .cpp. Llámalo ContinuationManager.cpp y, a continuación, pega este código:

    
    #include "pch.h"
    #include "ContinuationManager.h"
    
    using namespace HelloWorld;
    
    using namespace Platform;
    using namespace Windows::ApplicationModel::Activation;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml;
    
    /// <summary>
    /// Sets the ContinuationArgs for this instance. Using default Frame of current Window
    /// Should be called by the main activation handling code in App.xaml.cs
    /// </summary>
    /// <param name="args">The activation args</param>
    void ContinuationManager::Continue(IContinuationActivatedEventArgs^ args)
    {
        Continue(args, dynamic_cast<Frame^>(Window::Current->Content));
    }
    
    /// <summary>
    /// Sets the ContinuationArgs for this instance. Should be called by the main activation
    /// handling code in App.xaml.cs
    /// </summary>
    /// <param name="args">The activation args</param>
    /// <param name="rootFrame">The frame control that contains the current page</param>
    void ContinuationManager::Continue(IContinuationActivatedEventArgs^ args, Frame^ rootFrame)
    {
        this->args = args;
        this->handled = false;
    
        GUID result;
        if (SUCCEEDED(CoCreateGuid(&result)))
        {
            this->id = Platform::Guid(result);
        }
    
    
        if (rootFrame == nullptr)
            return;
    
        switch (args->Kind)
        {
        case ActivationKind::PickFileContinuation:
        {
            auto fileOpenPickerPage = dynamic_cast<IFileOpenPickerContinuable^>(rootFrame->Content);
            if (fileOpenPickerPage != nullptr)
            {
                fileOpenPickerPage->ContinueFileOpenPicker(dynamic_cast<FileOpenPickerContinuationEventArgs^>(args));
            }
        }
            break;
            // See FilePickerSample for these cases
            // case ActivationKind::PickSaveFileContinuation:   
            // case ActivationKind::PickFolderContinuation:    
            // case ActivationKind::WebAuthenticationBrokerContinuation:
    
        }
    }
    
    /// <summary>
    /// Marks the contination data as 'stale', meaning that it is probably no longer of
    /// any use. Called when the app is suspended (to ensure future activations don't appear
    /// to be for the same continuation) and whenever the continuation data is retrieved 
    /// (so that it isn't retrieved on subsequent navigations)
    /// </summary>
    void ContinuationManager::MarkAsStale()
    {
        this->handled = true;
    }
    
    /// <summary>
    /// Retrieves the continuation args, if they have not already been retrieved, and 
    /// prevents further retrieval via this property (to avoid accidentla double-usage)
    /// </summary>
    IContinuationActivatedEventArgs^ ContinuationManager::GetContinuationArgs(bool includeStaleArgs)
    {
        if (!includeStaleArgs && handled)
            return nullptr;
        else
        {
            MarkAsStale();
            return args;
        }
    }
    
    

Dn263165.wedge(es-es,WIN.10).gifControlar el evento OnActivated (Windows Phone 8.1)

  1. Agrega esta instrucción #include a App.xaml.h:

    
    #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
    #include "ContinuationManager.h"
    #endif
    
    
  2. Agrega la siguiente función de miembro público en la clase App en App.xaml.h:

    
    #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
            virtual void OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs^ e) override;
    #endif
    
    
  3. Agrega miembros privados en la clase App para que las declaraciones privadas completas se parezcan a lo siguiente:

    
    private:
    #if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
    		Windows::UI::Xaml::Media::Animation::TransitionCollection^ _transitions;
    		Windows::Foundation::EventRegistrationToken _firstNavigatedToken;
      ContinuationManager^ continuationManager;
    		void RootFrame_FirstNavigated(Platform::Object^ sender, Windows::UI::Xaml::Navigation::NavigationEventArgs^ e);
    #endif
    
    		void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ e);
      void LaunchOrContinue(Windows::ApplicationModel::Activation::IActivatedEventArgs^ e);
      void OpenMainPage(Windows::UI::Xaml::Controls::Frame^ root, Windows::ApplicationModel::Activation::IActivatedEventArgs^ e);
    
    

    Ten en cuenta que hemos añadido un campo ContinuationManager, una función de miembro LaunchOrContinue y una función de miembro OpenMainPage.

  4. Agrega las implementaciones de los miembros nuevos en App.xaml.cpp:

    
    
    void App::LaunchOrContinue(Windows::ApplicationModel::Activation::IActivatedEventArgs^ e)
    {
    #if _DEBUG
        // Show graphics profiling information while debugging.
        if (IsDebuggerPresent())
        {
            // Display the current frame rate counters
            DebugSettings->EnableFrameRateCounter = true;
        }
    #endif
    
        auto rootFrame = dynamic_cast<Frame^>(Window::Current->Content);
    
        // Do not repeat app initialization when the Window already has content,
        // just ensure that the window is active
        if (rootFrame == nullptr)
        {
            // Create a Frame to act as the navigation context and associate it with
            // a SuspensionManager key
            rootFrame = ref new Frame();
            HelloWorld::Common::SuspensionManager::RegisterFrame(rootFrame, "appFrame");
    
            // Set the default language
            rootFrame->Language = Windows::Globalization::ApplicationLanguages::Languages->GetAt(0);
    
            if (e->PreviousExecutionState == ApplicationExecutionState::Terminated)
            {
                // TODO: Restore the saved session state only when appropriate, scheduling the
                // final launch steps after the restore is complete
                HelloWorld::Common::SuspensionManager::RestoreAsync();
            }
    
            if (rootFrame->Content == nullptr)
            {
                // If the navigation stack isn't restored, then go to the first page.
                OpenMainPage(rootFrame, e);
            }
        }
        else
        {
            if (rootFrame->Content == nullptr)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                OpenMainPage(rootFrame, e);
            }
        }
    
    #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
        if (e->Kind == ActivationKind::PickFileContinuation)
        {
            auto continuationEventArgs = dynamic_cast<IContinuationActivatedEventArgs^>(e);
            continuationManager = ref new ContinuationManager();
            if (continuationEventArgs != nullptr)
            {
                continuationManager->Continue(continuationEventArgs, rootFrame);
            }
        }
    #endif
    
        // Place the frame in the current Window
        Window::Current->Content = rootFrame;
    
        // Ensure the current window is active
        Window::Current->Activate();
    
    }
    
    void App::OpenMainPage(Frame^ root, Windows::ApplicationModel::Activation::IActivatedEventArgs^ e)
    {
        // If we are launching, then configure the new page by passing required 
        // information as a navigation parameter
        if (e->Kind == ActivationKind::Launch)
        {
            auto launchArgs = dynamic_cast<LaunchActivatedEventArgs^>(e);
            if (!root->Navigate(TypeName(MainPage::typeid), launchArgs->Arguments))
            {
                throw ref new FailureException("Failed to create initial page");
            }
        }
        else
        {
            root->Navigate(TypeName(MainPage::typeid));
        }
    }
    
    
  5. Sustituye el código de OnLaunched y OnActivated con esta llamada a LaunchOrContinue:

    
    LaunchOrContinue(e);
    
    

    El código que controla el evento Activated y el evento Launched es muy parecido. En ambos casos la aplicación debe comprobar si tiene un marco y si hay páginas en dicho marco. En caso de que así sea, tiene que restaurar el estado en dichas páginas. En caso contrario, simplemente va a la página predeterminada. Aquí hemos creado una función de miembro LaunchOrContinue para controlar ambos casos. Si la aplicación continúa tras la operación del selector de archivos, y si compilamos para Windows Phone 8.1, crearemos una clase ContinuationManager y la utilizaremos para realizar la tarea de restaurar el estado cuando vuelva el selector de archivos. La función de miembro OpenMainPagees simplemente una ayuda para reducir la duplicación de código.

Dn263165.wedge(es-es,WIN.10).gifModificar la clase PhotoPage (Windows Phone 8.1)

  1. Para controlar la continuación desde una activación del selector de archivos, la clase PhotoPage debe implementar la interfaz IFileOpenPickerContinuable que definimos en ContinuationManager.h:

    Primero agrega la instrucción #include y una instrucción using para el espacio de nombres del selector de archivos:

    
    #include "ContinuationManager.h"using namespace Windows::Storage::Pickers;
    
    

    A continuación, modifica la declaración de clase:

    
    public ref class PhotoPage sealed : IFileOpenPickerContinuable
    
    

    Agrega esta función de miembro público en Photopage.xaml.h:

    
    virtual void ContinueFileOpenPicker(Windows::ApplicationModel::Activation::FileOpenPickerContinuationEventArgs^ args);
    
    
  2. Agrega la implementación en Photopage.xaml.cpp:

    
    /// <summary>
    /// Handle the returned files from file picker
    /// This method is triggered by ContinuationManager based on ActivationKind
    /// </summary>
    /// <param name="args">File open picker continuation activation argment. It cantains the list of files user selected with file open picker </param>
    void PhotoPage::ContinueFileOpenPicker(FileOpenPickerContinuationEventArgs^ args)
    {
    
        Windows::Storage::StorageFile^ file;
    
        if (args->Files->Size > 0)
        {
            // Open a stream for the selected file.
             file = args->Files->GetAt(0);
        }
            // asynchronously return the IRandomAccessStream^ object
            create_task(file->OpenAsync(Windows::Storage::FileAccessMode::Read))
        .then([this, file](Windows::Storage::Streams::IRandomAccessStream^ fileStream)
        {
            // Set the image source to the selected bitmap.
            Windows::UI::Xaml::Media::Imaging::BitmapImage^ bitmapImage =
                ref new Windows::UI::Xaml::Media::Imaging::BitmapImage();
    
            bitmapImage->SetSource(fileStream);
            displayImage->Source = bitmapImage;
            this->DataContext = file;
        });
    
       
    }
    
    
  3. Establece el proyecto de Windows Phone como proyecto de inicio y presiona F5. Puedes utilizar el emulador para hacer una foto simulada y cargarla en tu aplicación.

Paso 2: Enlazar controles de la interfaz de usuario a datos del archivo

En este punto, la imagen aparece en la interfaz de usuario, pero las propiedades del archivo de imagen no se muestran aún en los bloques de texto que agregaste.

Podrías establecer la propiedad Text de cada TextBlock en el código, de la misma forma que estableciste la propiedad Image.Source. Pero para mostrar los datos, normalmente se usa el enlace de datos para conectar un origen de datos con la interfaz de usuario, ya sea estableciendo la propiedad Binding.Source o DataContext en el elemento de la interfaz de usuario. Cuando estableces un enlace y cambia el origen de datos, los elementos de la interfaz de usuario que están enlazados al origen de datos reflejan los cambios automáticamente.

Nota  De forma predeterminada, los enlaces son one-way, lo que significa que las actualizaciones en el origen de los datos se reflejan en la interfaz de usuario. Puedes especificar un enlace two-way de forma que los cambios que realice el usuario en un elemento de la interfaz de usuario se puedan ver reflejados en el origen de datos. Por ejemplo, si el usuario edita el valor de TextBox, el motor de enlace actualiza automáticamente el origen de datos subyacente para reflejar ese cambio.
 

En este tutorial, la propiedad DataContext permite configurar el enlace predeterminado de un elemento completo de la interfaz de usuario, incluidos todos los elementos secundarios. Estableces el StorageFile seleccionado en FileOpenPicker como el DataContext de la página de fotos. En el código anterior, recuerda que, después de seleccionar la imagen, usas esta línea para establecer DataContext:



this->DataContext = file;


Muestras el nombre del archivo enlazando la propiedad Text del TextBlock de título a la propiedad StorageFile.DisplayName del archivo seleccionado.

Como no has especificado un enlace Source, el motor de enlace de datos busca la propiedad DisplayName en el DataContext. Si el DataContext es null o no tiene una propiedad DisplayName, el enlace genera un error de forma silenciosa y no se muestra ningún texto en TextBlock.

Aquí enlazas los controles TextBlock a las propiedades StorageFile.

Dn263165.wedge(es-es,WIN.10).gifEnlazar controles a datos

  1. En el Explorador de soluciones, abre PhotoPage.xaml.
  2. Selecciona TextBlock para el nombre de foto que se encuentra debajo del botón "Get photo".
  3. En la ventana Propiedades, elige el botón Propiedades (Botón Propiedades) para mostrar la vista Propiedades.
  4. En Común, en la ventana Propiedades, elige el marcador de propiedad de la propiedad Text. Se abre el menú de propiedad.
    Nota  El marcador de propiedad es el símbolo de cuadro pequeño situado a la derecha de cada valor de propiedad. El marcador de propiedad Text de color negro indica que se ha establecido en un valor de cadena.
     
  5. En el menú de propiedad, selecciona Crear enlace de datos. Se abrirá el cuadro de diálogo Crear enlace de datos.
  6. En el cuadro de diálogo, en la lista desplegable Tipo de enlace, selecciona Contexto de datos.
  7. Escribe "DisplayName" en el cuadro de texto Path (Ruta de acceso), tal como se muestra aquí.

    Cuadro de diálogo de enlace de datos de Visual Studio.

    Nota  Observa que el mensaje del cuadro de diálogo Crear enlace de datos dice que no se ha establecido el contexto de datos. Eso es correcto porque estableces el contexto de datos en el código cuando ejecutas la aplicación y obtienes una imagen.
     
  8. Elige el botón Aceptar.

    He aquí el lenguaje de marcado de aplicaciones extensible (XAML) para el TextBlock después de agregar el enlace.

    
    <TextBlock Grid.Row="1" TextWrapping="Wrap" Text="{Binding DisplayName}" 
               Style="{StaticResource SubheaderTextBlockStyle}"/>
    
    
  9. Selecciona el TextBlock después de "Nombre de archivo:" TextBlock.
    1. Repite los pasos 3-5 para crear un enlace de datos para este TextBlock.
    2. Escribe "Name" en el cuadro de texto Ruta de acceso y selecciona Aceptar.
  10. Selecciona el TextBlock después de "Ruta de acceso:" TextBlock.
    1. Repite los pasos 3-5 para crear un enlace de datos para este TextBlock.
    2. Escribe "Path" en el cuadro de texto Ruta de acceso y selecciona Aceptar.

    Este es el XAML para el StackPanel de la información de la foto después de agregar los enlaces:

    
    
                    <StackPanel Margin="20,0,0,0">
                        <TextBlock TextWrapping="Wrap" Text="File name:" 
                                   Style="{StaticResource CaptionTextBlockStyle}"/>
                        <TextBlock TextWrapping="Wrap" Text="{Binding Name}" 
                                   Style="{StaticResource BodyTextBlockStyle}" Margin="10,0,0,30"/>
                        <TextBlock TextWrapping="Wrap" Text="Path:" 
                                   Style="{StaticResource CaptionTextBlockStyle}"/>
                        <TextBlock TextWrapping="Wrap" Text="{Binding Path}" 
                                   Style="{StaticResource BodyTextBlockStyle}" Margin="10,0,0,30"/>
                    </StackPanel>
    
    
    
  11. Presiona F5 para compilar y ejecutar la aplicación. Navega a la página de fotos. Haz clic en el botón Get photo para iniciar FileOpenPicker. Con los enlaces configurados, ahora se muestran las propiedades del archivo cuando se selecciona un archivo.

    Este es el aspecto de la aplicación cuando se selecciona una imagen y los bloques de texto están enlazados a los datos.

    Página de fotos con una imagen y enlaces de datos

Paso 3: Guardar y cargar el estado

En el segundo tutorial de esta serie, Administrar el ciclo de vida y el estado de la aplicación, aprendimos a guardar y restaurar el estado de la aplicación. Ahora que has agregado una nueva página a la aplicación, también debes guardar y cargar el estado de la nueva página. Para la página de fotos, solo tienes que guardar y restaurar el archivo de imágenes que se muestra actualmente.

Pero no puedes simplemente guardar la ruta de acceso al archivo y después volver a abrirlo usando esa ruta de acceso. Cuando un usuario usa FileOpenPicker para seleccionar un archivo, de forma implícita está dando a la aplicación permiso para usar ese archivo. Pero si más tarde intentas recuperar el archivo usando solo la ruta de acceso, se te denegará el permiso.

En lugar de eso, para que puedas conservar el acceso al archivo para usarlo más adelante, la clase StorageApplicationPermissions proporciona dos listas en las que puedes almacenar el archivo y los permisos que se otorgaron cuando el usuario lo abrió con el selector de archivos.

  • MostRecentlyUsedList: para almacenar los últimos 25 archivos a los que se accedió.
  • FutureAccessList: para el almacenamiento general de hasta 1.000 archivos para un futuro acceso.

Puesto que solo necesitas acceder al último archivo que seleccionó el usuario, puedes usarlo para restaurar el estado de la página. Para ello, MostRecentlyUsedList se adapta a los requisitos.

Cuando un usuario selecciona un archivo, lo agregas a MostRecentlyUsedList. Cuando agregas un archivo a esta lista, MostRecentlyUsedList devuelve un token que usas para recuperar el archivo más adelante. Guardas este token en el diccionario pageState y lo usarás para recuperar el archivo de imagen actual cuando restaures el estado de página.

Dn263165.wedge(es-es,WIN.10).gifPara guardar el estado

  1. En el Explorador de soluciones, abre PhotoPage.xaml.h. (Es posible que tengas que expandir el nodo PhotoPage.xaml para poder ver los dos archivos de código subyacente situados debajo de este).
  2. En la clase PhotoPage, agrega la siguiente variable privada. Declara una variable que almacenará el token devuelto por la MostRecentlyUsedList.
    
    Platform::String^ mruToken;
    
    
  3. En PhotoPage.xaml.cpp, agrega el siguiente código al controlador de eventos GetPhotoButton_Click como una continuación en el método then menos interno (cuya última instrucción sea this->DataContext = file). Esto agrega el archivo seleccionado a MostRecentlyUsedList y obtiene el token.
    
    .then([this, file]()
    			{
    				// Add picked file to MostRecentlyUsedList.
    				mruToken = Windows::Storage::AccessCache::StorageApplicationPermissions::MostRecentlyUsedList->Add(file);
    			})
    
    

    Este es el código con su código circundante:

    
    
     // Open the file picker.
        create_task(openPicker->PickSingleFileAsync()).then(
            [this](Windows::Storage::StorageFile^ file)
        {
            // file is null if user cancels the file picker.
            if (file != nullptr)
            {
                // Open a stream for the selected file.
                create_task([file]()
                {
                    return file->OpenAsync(Windows::Storage::FileAccessMode::Read);
                }).then([this, file](Windows::Storage::Streams::IRandomAccessStream^ fileStream)
                {
                    // Set the image source to the selected bitmap.
                    Windows::UI::Xaml::Media::Imaging::BitmapImage^ bitmapImage =
                        ref new Windows::UI::Xaml::Media::Imaging::BitmapImage();
    
                    bitmapImage->SetSource(fileStream);
                    displayImage->Source = bitmapImage;
                    this->DataContext = file;
                }).then([this, file]()
                {
                    // Add picked file to MostRecentlyUsedList.
                    mruToken = Windows::Storage::AccessCache::StorageApplicationPermissions::MostRecentlyUsedList->Add(file);
                });
            }
        });
    
    
    
  4. En el método PhotoPage::SaveState, agrega el código siguiente. Esto comprueba si existe el token y, si es así, lo guarda en el diccionario PageState.
    
        if (mruToken != nullptr && !mruToken->IsEmpty())
        {
            e->PageState->Insert("mruToken", mruToken);
        }     
    
    

    Este es el código completo para el método navigationHelper_SaveState:

    
    
    void PhotoPage::SaveState(Object^ sender, Common::SaveStateEventArgs^ e){
        (void) sender;	// Unused parameter
        (void) e; // Unused parameter
    
        if (mruToken != nullptr && !mruToken->IsEmpty())
        {
            e->PageState->Insert("mruToken", mruToken);
        }
    }
    
    

Dn263165.wedge(es-es,WIN.10).gifPara cargar el estado

  1. En PhotoPage.xaml.cpp, agrega el código siguiente al método PhotoPage::LoadState.

    Obtén el token del diccionario PageState y, luego, úsalo para recuperar el archivo de MostRecentlyUsedList y restaurar el estado de la interfaz de usuario.

    
        if (e->PageState != nullptr && e->PageState->HasKey("mruToken"))
        {
            Object^ value = e->PageState->Lookup("mruToken");
            if (value != nullptr)
            {
                mruToken = value->ToString();
    
                // Open the file via the token that you stored when adding this file into the MRU list::
                create_task(Windows::Storage::AccessCache::StorageApplicationPermissions::MostRecentlyUsedList->GetFileAsync(mruToken))
                    .then([this](Windows::Storage::StorageFile^ file)
                {
                    if (file != nullptr)
                    {
                        // Open a stream for the selected file->
                        //  Windows.Storage->Streams.IRandomAccessStream fileStream =
                        create_task(file->OpenAsync(Windows::Storage::FileAccessMode::Read))
                            .then([this, file](Windows::Storage::Streams::IRandomAccessStream^ fileStream)
                        {
                            // Set the image source to a bitmap.
                            auto bitmapImage =
                                ref new Windows::UI::Xaml::Media::Imaging::BitmapImage();
    
                            bitmapImage->SetSource(fileStream);
                            displayImage->Source = bitmapImage;
    
                            // Set the data context for the page
                            this->DataContext = file;
                        });
                    }
                });
            }
        }
    
    
    
  2. Presiona F5 para compilar y ejecutar la aplicación. Navega a la página de fotos y haz clic en el botón "Get photo" para iniciar FileOpenPicker y seleccionar un archivo. Ahora, cuando se suspende o termina una aplicación, al restaurarla se volverá a cargar la imagen.
    Nota  Revisa Administrar el ciclo de vida y el estado de la aplicación para obtener instrucciones sobre cómo suspender, finalizar y restaurar una aplicación.
     

Resumen

Te felicitamos por haber terminado el cuarto tutorial, donde aprendiste a usar los selectores de archivos y el enlace de datos en una aplicación de la Tienda Windows.

Consultar el código

¿Te has quedado atascado o quieres revisar tu trabajo? Si es así, consulta la muestra Hello World (C++) en la Galería de código.

 

 

Mostrar:
© 2017 Microsoft