Generation Apps – Día 29 – Usar el Kit de prueba del Windows Phone Store

El kit de prueba del Windows Phone Store provee una suite automatizada, monitoreada, y pruebas manuales que te ayudaran a preparar tu aplicación para ser aceptado en el Windows Phone Store en la primera vez que apliques. Este kit te permitirá identificar y corregir problemas antes de aplicar tu aplicación para su publicación, el cual te ahorra tiempo en el proceso de registro. Puedes utilizar este kit para versiones de Windows Phone 7.1 o posteriores.

Instalación

El kit de prueba de Windows Phone Store es un componente el SDK de Windows Phone, así que si ya instalaste el SDK, ya debes tener el kit de pruebas a tu disposición. En caso contrario, puedes conseguirlo dando clic aquí…

Qué me dirá el kit de prueba?

Ejecutando este kit en tu aplicación te ayudará a determinar si la aplicación podrá aprobar la certificación o no. En caso que falle, el kit te proveerá detalles de lo que necesitas solucionar en la aplicación antes de someterlo en la evaluación del Windows Phone Store.  La siguiente lista te muestra algunas pruebas que incluye el kit de prueba.

  • Si las imágenes especificadas y capturas de pantallas cumplen con los requerimientos de certificación.

  • Si el icono de la aplicación y la imagen de fondo usada cumplen con los requerimientos de certificación.

  • Que capacidades utiliza la aplicación (internet, cámara, GPS, etc.).

  • El archivo .XAP cumple con el rango de tamaño requerido y si el manifiesto de la aplicación es valido.

  • Si la aplicación maneja errores de manera adecuada.

  • Si la aplicación se ejecuta dentro del rango segundos requerido.

  • Si la aplicación excede el uso de memoria máxima. Esto es particularmente importante a considerar cuando desarrollas aplicaciones para teléfonos de 256 MB de memoria.

  • Si el manejo del botón Back es usado de manera correcta.

  • Si la aplicación usa APIs no permitidas en el teléfono.

  • Si la aplicación usa APIs no permitida en agentes de segundo plano.

  • Si hay excepciones no manejadas en tu aplicación y si la aplicación finaliza de manera apropiada.

Uso del kit de prueba de Windows Phone

Si deseas conocer mas detalles de como usar el kit de prueba, da clic al enlace siguiente;

Kit de prueba para el Windows Phone Store

Fuente:
Windows Phone Marketplace Test Kit

Generation Apps – Día 28 – Crear capturas de pantalla para enviar al de Windows Phone

Puedes crear screenshots de tu aplicación Silverlight  o XNA con la herramienta de screenshot del emulador de Windows Phone. Puedes tomar fotos en cualquier instante de tiempo de tu aplicación. Estos generan una imagen de formato PNG de 480 x 800 pixeles.

WP-Screenshots

Para crear un screenshots utilizando el emulador de Windows Phone

  1. Para optimizar la calidad de las capturas de pantalla, ajusta el zoom del emulador al 100%. A mayor  nivel de zoom, mejor será la calidad de las imágenes.

  2. Desactiva los contadores de rendimiento que aparecen en la esquina derecha superior de la pantalla. Puedes ejecutar la aplicación en modo de Debug, y detenerlo, y volverlo a ejecutar sin cerrar el emulador para que no aparezcan los contadores.

  3. En la barra de herramientas del emulador, da clic al botón de expansión para aparezca la ventana de herramientas adicionales

  4. Da clic a la pestaña Screenshot

  5. Cuando tu aplicación esta lista, da clic al botón Capture, y la imagen resultante se mostrara en la ventana

  6. Da clic en Save para que se despliegue la ventana de dialogo «Save as…»

  7. Nombra tu imagen capturada y da clic en Save.

Fuente:
How to: Create Screenshots for Windows Phone Marketplace

Generations Apps – Día 27 – Ajuste de rendimiento de tu aplicación

Puedes utiliza la herramienta de análisis de rendimiento de Windows Phone para mejorar los tiempos de respuesta de tus aplicaciones Windows Phone. Lo puedes lograr recolectando muestras de datos de rendimiento en una sesión de perfil para luego visualizar y analizar los datos. tienes la opción de analizar el rendimiento de ejecución o de uso de memoria de la aplicación en una sesión de perfil. Este tópico describe como capturar este muestre de datos y analizarlos usando el Performance Analysis Tool.

Para capturar los datos de rendimiento

  1. En Visual Studio 2010, abre un proyecto de Windows Phone version 7.1

  2. Compila el proyecto. (Para mejores resultados, deberías crear un compilado tipo Release)

  3. En la barra de herramientas estándar, selecciona el dispositivo objetivo (Se recomienda usar un dispositivo físico)

  4. En el menú Debug, selecciona Start Windows Phone Performance Analysis. Si estas abriendo un proyecto con versión de Windows Phone 7.0, no se desplegará esta opción de menú.Un log de rendimiento en blanco (un archivo .sap) se despliega en una pestaña de Visual Studio. El archivo .sap es nombrado automáticamente usando una combinación del nombre de tu proyecto con un formato de fecha y hora concatenada. Todos estos archivos son guardados a menos que tu los elimines.Este log de rendimiento se despliega con opciones que te permiten seleccionar que opciones de rendimientos deseas analizar.

    La siguiente imagen te muestra un log vacío de rendimiento y las opciones.

    WP-Performance

  5. En los settings, selecciona la opción Execution para analizar el rendimiento del despliegue ítems visuales y las llamadas a métodos en el código.

    -o-

    Selecciona la opción Memory para analizar el comportamiento de la memoria al instanciar objetos y el uso de texturas en la aplicación. Típicamente optarías por la opción Execution a menos que tu aplicación presente problemas de memoria.

  6. Opcionalmente, puedes dar clic en Advanced Settings para mostrar mas opciones para analizar tu aplicación como los detalles de la cache, colectar contadores de rendimientos detallados y analizar el stack de memoria.

  7. Cuando ya hayas terminado de seleccionar las opciones deseadas, da clic en Launch Applicacion para comenzar la sesión de perfil. Tu aplicación se ejecutara en el dispositivo objetivo (emulador o dispositivo físico).

  8. Ahora utiliza y navega en tu aplicación como un usuario común lo haría. Si hay problemas de rendimiento que ya has observado en casos anteriores, como un scrolling entrecortado o una falta de respuesta de la interfaz de usuario, asegúrate de usar esas funcionalidades para que sean analizadas por la sesión de perfil. Incluso puedes utilizar un cronometro para que tomes el tiempo de uso y tomar notas de observaciones encontradas en tu aplicación para analizarlas en el log de rendimiento. La siguiente imagen muestra una sesión de perfil en ejecución.

    WP-Profiling

  9. Cuando ya termines de interactuar con la aplicación, detiene la sesión de perfil dando clic o haciendo un gesto Tap en el botón Back, el cual causa que la aplicación se finalice. También puedes detenerla sesión yendo a la pestana de rendimiento, y dando clic en Stop Profiling.

Para analizar datos de rendimiento

  1. En el Explorador de Solución, da doble clic en el log de rendimiento (.sap) para abrirlo en Visual Studio. Si apenas terminaste la sesión de perfil, este log deberá estar abierto en Visual Studio. El grafico de la sesión es desplegada.

  2. Ahora utiliza el grafico para observar la tasa de imágenes por segundo (o frame rate) durante la sesión de perfil, al igual que el uso del CPU, uso de memoria, storyboards, cargas de imágenes y eventos de garbage collector.

  3. Para identificar problemas específicos con la aplicación, selecciona una región en la línea de tiempo graficada. Se desplegara información detallada de la región seleccionada, con mensajes e alerta, información y errores.

    WP-Graph

  4. En la tabla de análisis de rendimiento, veras tres tipos de entradas junto con los siguientes iconos:

    • Information icon Ítems de Información

    • Warning icon Ítems de alerta

    • Profiler error icon Ítems de Error

  5. Para cada entrada de Información, Alerta y Error, deberías seguir las instrucciones de la columna de Observation Summary para investigar a mayor detalle la fuente del ítem de información, alerta o error. Estas instrucciones te llevaran que selecciones hacer de la barra de navegación Performance Warnings, y que buscar una vez estando ahí. Puedes hacer clic en los hilos de los procesos para ver mas detalles, como se muestra en la imagen:

    WP-Thread

Fuente:
How to: Capture and Analyse Performance Data Using Windows Phone
How to: Identify and Fix Common Performance Issues Using Windows Phone

Generations Apps – Día 25 – Agregando publicidad a tu aplicación

El SDK de Microsoft Advertising para Windows Phone te permite desplegar publicidad en tu aplicación. Cuando Microsoft Advertising Mobile Ads están integradas en tu aplicación, la red de Microsoft Advertising le entregará las publicidades a tus usuarios – y te pagará una cantidad de los ingresos.

Para hacer que tu aplicación reciba publicidades móviles, primero deberás crear una cuenta en el Microsoft pubCenter. Utilizarás pubCenter para crear AdUnits y registras tus aplicaciones para que se enlacen a tu cuenta con un ApplicationId.

pubCenter gestiona el ingreso que tus aplicaciones generan, y te paga tu porcentaje correspondiente. Una vez que crees la cuenta, registra tu aplicación, y asegúrate que incluya el identificador único que se le genera automáticamente al momento que creaste la aplicación para Windows Phone (lleva el nombre de ApplicationId). El ApplicationId enlaza tu cuenta con los ingresos generados por la publicidad de tus aplicaciones.

Cuando usas un AdUnit en tu aplicación, estas creando un espacio en la pantalla del teléfono para mostrar publicidad. Luego es que puedes configurar un AdUnit en el pubCenter. La configuración de un AdUnit define como una publicidad es desplegada en tu aplicación, y el tipo de publicación que el usuario verá. Después de configurar el AdUnit en el pubCenter, le es asignado un identificador único denominado AdUnitId.

Tu código debe incluir el ApplicationId y el AdUnitId para que tu aplicación Windows Phone pueda solicitar las publicidades a los servidores de Microsoft Advertising. Estos identificadores son usados para identificar el AdUnit que mostrara la aplicación y la cuenta en la que va a recibir el ingreso.

Solo toma unos cuantos pasos sencillos para registrarse en el pubCenter, gestionar tus ingresos, e integrar las publicidades móviles de Microsoft Advertising a tu aplicación. Para mayor información de integración, puedes dar clic aquí…

Fuente:
Advertising in Windows Phone Applications

Generation Apps – Día 24 – Crear aplicaciones de prueba para Windows Phone

El Windows Phone Store te permite diseñar en implementar modos de prueba de tu aplicación. La experiencia muestra que usuarios disfrutan probar una aplicación mas limitada antes de comprarla.

La plataforma Windows Phone Application te facilita la capacidad de proveer versiones de prueba y completas dentro de un mismo contenedor XAP. Usuarios deseando comprar una aplicación que están probando pueden hacerlo con una experiencia muy similar al Windows Phone Store.

No hay restricciones acerca de como tu diseñes la experiencia de prueba de tu aplicación. Puedes determinar el grado de funcionalidad que le quieres exponer al usuario, ya sea el modo de prueba tenga una duración limitada, o como quieres alentar al usuario a comprar tu aplicación. También puedes determinar si los datos y estados de la aplicación son mantenibles si un usuario desea descargar la versión de prueba de la aplicación.

Cuando quieras publicar un modo de prueba de aplicación al Windows Phone Store, marca el check box de Trial Application y el cliente del Windows Phone Store mostrará un botón Try para descargar la aplicación en modo de prueba.

Visión General de Modo de Prueba

Para implementar un modo de prueba de tu aplicación, debes definir e implementar como el comportamiento de prueba difiere del comportamiento de la versión completa de la aplicación. Para ejecutar la aplicación en el modo correcto cuando es ejecutado por el usuario, tu código debe determinar si la aplicación se esta ejecutando en versión de prueba o completa. Para proveer una ruta de compra tu código debe ejecutar el cliente del Windows Phone Store.

El estado de prueba/full de la ejecución de derechos del usuario para una aplicación son mantenidos en una licencia. Cuando el usuario prueba un aplicación, Licencias de prueba no expiran pero cuando el usuario compra la versión completa, se descarga la licencia completa de la aplicación.

Para determinar si la licencia en la aplicación instalada en un Windows Phone es modo Prueba o Completa, Windows Phone 7 provee  métodos que pueden retornar TRUE si la aplicación esta ejecutándose bajo la licencia de prueba y FALSE si la aplicación se esta ejecutando bajo una licencia completa.

Para permitir a un usuario corriendo una aplicación de prueba y desea comprar la versión completa, Windows Phone 7 provee métodos para iniciar la aplicación cliente de Windows Phone  Store en la pagina de compra de la aplicación.

Desarrolladores del Framework XNA deberán usar la clase GamerServices.Guide para crear la experiencia de prueba y compra. Utiliza la propiedad Guide.IsTrialMode para obtener el tipo de licencia actual y el método Guide.ShowMarketplace para iniciar la experiencia de compra del videojuego.

Aplicaciones de Silverlight pueden usar métodos del Framework XNA ya descritos en el párrafo anterior o usando el método IsTrial() para obtener el tipo de licencia actual directamente, y el método Show() de la clase MarketplaceDetailTask para iniciar la experiencia de compra.

Implementando el modo de prueba

Determinando el estado de modo prueba y navegación hacia el Windows Phone Store para la compra de la aplicación debe ser simulada por tu código cuando estas probando o depurando tu aplicación. Métodos para esta funcionalidad no funcionan en modos de debug o testing ya que al requerir la licencia de la aplicación debe estar completa y publicada en el Store.

Aplicaciones que usan el Framework XNA deberían siempre usar la clase GamerServices.Guide para usar esta funciones. Esta clase posee funcionalidades de simulación de  modos de prueba y compra.

Aplicaciones de Silverlight pueden utilizar la misma clase GamerServices.Guide o implementar su propio comportamiento personalizado. Usando llamadas de GamerServices.Guide en una aplicación Silverlight puede ahorrarte trabajo simulando el estado de licencia de prueba durante el momento de depuración y testing. Usando métodos como LicenceInformation.IsTrial y MarketplaceDetailTask.Show permiten mas oportunidades para personalizar tus métodos de depuración y testing.

Buenas practicas para Modos de Prueba

  • Aplicaciones XNA deberían siempre utilizar la clase GamerServices.Guide para implementar modos de prueba.

  • Al asignar la bandera Guide.SimulateTrialMode a TRUE, siempre enciérralos en un bloque #if DEBUG / #endif

  • Revisar el estado IsTrial() siempre cuando la aplicación cargue o resuma su ejecución.

  • No dependas de modos de prueba de tiempo limitado para proteger el valor de tu aplicación. Si no se valida esto correctamente, el usuario puede simplemente re-instalar tu aplicación para utilizar nuevamente todas las funcionalidades que ofrece.
  • Provee una forma fácil a los usuarios para comprar la aplicación una vez instalada en modo de prueba.

  • Informa al usuario el por que deberían comprar la aplicación completa. Típicamente en el caso de los juegos, la aplicación muestra screenshots de mas niveles, ítems y puntos que pueden alcanzar si compran la versión completa.

Fuente:
Creating Trial Applications Overview for Windows Phone

Generation Apps – Día 23 – Localizar tu aplicación

Este tópico describe como separar recursos localizables del código creando archivos de recursos idioma-específicos. Visual Studio utiliza estos archivos para crear ensamblados que le permitirán a tu aplicación soportar múltiples lenguajes.

Agregando un archivo de recurso a un proyectos y habilitando el soporte de localización

  1. Agrega un archivo de recurso para el lenguaje por defecto en tu aplicación

    • Abre el proyecto en Visual Studio

    • Agrega un archivo de recurso. En el Solution Explorer, clic derecho al nombre del proyecto, da clic en Add -> New Item

    • En la ventana de dialogo de New Item, selecciona Resources File y renombra el archivo a como lo desees (Ej: AppResources.resx). Este archivo contendrá los recursos para el lenguaje por defecto de la aplicación.

    • Identifica strings en tu aplicación y agrégalas al archivo de recurso. Puedes colocar un nombre, un valor y un comentario opcional por cada string. NOTA: El nombre debe ser único; hazlo lo mas descriptivo posible. El valor es el string que se mostrara en la interfaz al usuario final. Los comentarios son opcionales, sin embargo, son útiles en caso que se trabaje con grandes volúmenes de strings.

  2. Ahora agrega archivos de recursos adicionales por cada lenguaje que tu aplicación va a soportar. Cada archivo agregado deberá ser nombrado en el nombre correcto de cultura/lenguaje. Por ejemplo: para la cultura español de España, usa AppResources.es-ES.resx; para el lenguaje alemán de Alemania, usa AppResources.de-DE.resx.

  3. Define la cultura por defecto soportado por tu aplicación.

    • En el Solution Explorer, da clic derecho a tu proyecto, y selecciona Properties

    • Debajo de la pestaña Application, da clic al botón Assembly Information

    • En la lista de Neutral Language, selecciona default culture. Esto identifica el lenguaje de los strings en el archivo de recursos por defecto. Por Ejemplo, si el archivo de recurso por defecto es nombrado AppResources.resx, y los strings en el archivo están en ingles (United States), deberás seleccionar English (United States) como el lenguaje neutral del proyecto.

  4. Cierra el proyecto y abre el archivo de proyecto (<proyecto-nombre>.csproj) en un editor de texto. Localiza la etiqueta <SupportedCultures> y agrega los nombres de cada cultura adicional (lenguaje) que tu aplicación puede soportar. Deberás declarar tantas culturas como archivos .resx presentes en tu aplicación. Separa los nombres de lenguajes por un ‘;’. No debes agregar el idioma por defecto. Si tu aplicación tambien soporta aleman y español. La etiqueta quedaría:
    <SupportedCultures>de-DE;es-ES;</SupportedCultures>

Reemplazando strings dentro-del-código con astringes en un archivo de recurso

  1. En el Solution Explorer, abre un archivo de recurso y selecciona Public de la lista AccessModifier, en el tope del panel. Repite este paso por cada archivo de recurso en tu proyecto.

  2. Define una clase con la propiedad que apunta a los recursos. En el siguiente ejemplo, la clase LocalizesStrings contiene una propiedad que apunta al recurso AppResources en el namespace sdkGlobalizationCS:

    public class LocalizedStrings
    {
        public LocalizedStrings()
        {
        }
    
        private static sdkGlobalizationCS.AppResources localizedResources = new sdkGlobalizationCS.AppResources();
    
        public sdkGlobalizationCS.AppResources LocalizedResources { get { return localizedResources; } }
    }

    Si hay archivos de recursos adicionales en tu proyecto, puedes tener una clase LocalizedStrings con multiples propiedades, el cual cada uno retorna un recurso particular

  3. Abre el archivo App.xaml y agrega el siguiente código XAML a la sección <Application.Resources>

    <Application.Resources>
        <local:LocalizedStrings xmlns:local="clr-namespace:sdkGlobalizationCS" x:Key="LocalizedStrings" />
    </Application.Resources>
  4. Reemplaza aquellos valores de strings dentro del código con el siguiente código XAML:

    «{Binding Path=resourceFile.resourceName, Source={StaticResource LocalizedStrings}}»

    Donde resourceName es el nombre del recurso localizable, resourceFile es el nombre del apuntador que contiene el resourceName, y LocalizedStrings es el nombre asignado a una instancia de la clase que retorna el recurso.

    El siguiente XAML del ejemplo Globalization Sample se enlaza con el control ListBoxItem cn el string LangRegionNameFrFR retornado de la instancia de la clase LocalizedStrings.

    <ListBoxItem Content="{Binding Path=LocalizedResources.LangRegionNameFrFR, Source={StaticResource LocalizedStrings}}" />
  5. Repite el paso previo por cada string que quieres hacer localizable, en cada archivo XAML en tu aplicación que contiene contenido localizable.

  6. Compila tu aplicación – Y compilara una aplicación multilenguaje y mostrara al usuario una interfaz acorde a la configuración de idioma en su teléfono.

Localización para la barra de aplicación

En Windows Phone, se puede agregar una barra de aplicación en tu aplicación ya sea vía XAML o C#. Sin Embargo, en vista que la barra de aplicación no es un control de silverlight, no soporta el data binding dinámico para actualizar los textos debajo de los botones acorde al idioma de la aplicación. Por esta razón, si quieres localizar estos strings, debes crear y actualizar la barra de aplicación al momento de ejecución. El siguiente código de ejemplo muestra una función que despliega la barra de aplicación haciendo uso de los recursos de localización que provee los valores de los strings. Este ejemplo asume que ya creaste una librería DLL de recursos de localización con entradas de strings llamadas «ButtonText» y «MenuItemText.»

// Helper function to build a localized ApplicationBar
private void BuildApplicationBar()
{     
    // Set the page's ApplicationBar to a new instance of ApplicationBar.     
    ApplicationBar = new ApplicationBar();     

    // Create a new button and set the text value to the localized string from AppResources.          
    ApplicationBarIconButton appBarButton = new ApplicationBarIconButton(new Uri("ApplicationIcon.png",UriKind.Relative));     
    appBarButton.Text = AppResources.ButtonText;     
    ApplicationBar.Buttons.Add(appBarButton);     

    // Create a new menu item with the localized string from AppResources.     
    ApplicationBarMenuItem appBarMenuItem = new ApplicationBarMenuItem(AppResources.MenuItemText);         
    ApplicationBar.MenuItems.Add(appBarMenuItem);
}

Fuente:
How to: Build a Localized Application for Windows Phone

Generation Apps – Día 22 – Implementar agentes de segundo plano en tu aplicación

Los agentes de segundo plano y las tareas programadas realizan tareas en segundo plano, aun si la aplicación de primer plano no esta abierta. Los diferentes tipos de tareas programadas están diseñadas para los diferentes tipos de situaciones de proceso de segundo plano y por eso tienen diferentes comportamientos y restricciones. Aprende como implementar una aplicación que usa una tarea programada para registrar un agente de segundo plano. También entenderás la programación, duración y limitaciones de las tareas programadas.

Creando una aplicación que usa Tareas Programadas

Para crear una aplicación que usa tareas programadas, debes seguir los siguientes pasos:

  1. En Visual Studio, crea un nuevo proyecto Aplicación Windows Phone. Esta plantilla se encuentra en la categoría de Silverlight para Windows pone

  2. Ahora, agrega un proyecto de tipo Tarea Programada a tu solución. Del menú Archivo, selecciona Agregar -> Nuevo Proyecto. En la ventana de dialogo de Agregar Nuevo proyecto, selecciona Agente de Tareas Programadas Windows Phone. Deja el nombre por defecto, ScheduledTaskAgent1, y da clic a OK.

  3. En tu proyecto de primario, debes agregar una referencia hacia el proyecto tipo Agente. En el explorador de soluciones, realiza un clic derecho sobre el proyecto principal, y da clic a «Agregar Referencia…» y en la ventana de dialogo de Agregar Referencias, selecciona la pestana de Proyectos. Selecciona el proyecto tipo Agente, ScheduledTaskAgent1, y da clic a OK.

  4. En el explorador de soluciones, da doble clic a ScheduledAgent.cs del proyecto ScheduledTaskAgent1 para abrir el archivo. Veras que este archivo contiene la definición de una clase ScheduledAgent, el cual hereda la clase base ScheduledTaskAgent. Para este ejemplo, agrega la siguiente directiva para el namespace Shell y Sistema al tope del archivo.

    using Microsoft.Phone.Scheduler;
    using Microsoft.Phone.Shell;
    using System;
  5. Hay un método implementado en la clase, OnInvoke(ScheduledTask). Este método es invocado por el sistema operativo cuando la Tarea Programada es ejecutada. Aquí es donde deberías implementar el código que desees que se ejecute cuando el agente de segundo plano se ejecute. Cada aplicacion puede tener un solo ScheduledTaskAgent registrado a la vez, pero puedes programar este agente como un tipo de agente recurso-intensivo y periódico. Si tu aplicación usa ambas tareas ResourceIntensiveTask y PeriodicTask, puedes verificar que tipo de objetoScheduledTask que es pasado al método OnInvoke para determinar cual tarea el agente esta siendo invocado y ramificar tu ejecución de código como desees. Si utilizas un solo tipo de agente, no necesitaras revisar el tipo de objeto ScheduledTask. En este ejemplo, el agente ejecuta un objeto ShellToast desde OnInvoke, indicando el tipo de rarea programada para al agente que fue llamado. Este Toast te mostrara cual agente se esta ejecutando. Sin embargo, no se mostrara mientras que la aplicación se encuentre en modo foreground. Cuando el código de la tarea programada se haya completado, deberás invocar NotifyComplete()para informarle al sistema operativo que ya no necesitas seguir ejecutándolo. Esto permite al sistema operativo lograr gestionar otros agentes programados.

    protected override void OnInvoke(ScheduledTask task)
    {
      //TODO: Add code to perform your task in background
      string toastMessage = "";
    
      // If your application uses both PeriodicTask and ResourceIntensiveTask
      // you can branch your application code here. Otherwise, you don't need to.
      if (task is PeriodicTask)
      {
        // Execute periodic task actions here.
        toastMessage = "Periodic task running.";
      }
      else
      {
        // Execute resource-intensive task actions here.
        toastMessage = "Resource-intensive task running.";
      }
    
      // Launch a toast to show that the agent is running.
      // The toast will not be shown if the foreground application is running.
      ShellToast toast = new ShellToast();
      toast.Title = "Background Agent Sample";
      toast.Content = toastMessage;
      toast.Show();
    
      // If debugging is enabled, launch the agent again in one minute.
    #if DEBUG_AGENT
      ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(60));
    #endif
    
      // Call NotifyComplete to let the system know the agent is done working.
      NotifyComplete();
    }
  6. Ahora en los siguientes pasos, la aplicación de primer plano es modificada para permitir a los usuarios para habilitar y deshabilitar los agentes periódicos y recurso-intensivos. Primero abre el archivo MainPage.xamly pega el siguiente código XAML dentro del Grid «ContentPanel»

    Este código agrega dos conjuntos de controles, uno para cada tipo de agente. La mayoría de los controles son text blocks que estarán enlazados con los objetos ScheduledTask que representan los agentes de segundo plano, permitiéndote observar las propiedades de estos objetos. Además, un check box es agregado por cada tipo de agente que muestra y permite que el usuario habilite o deshabilite el estado de cada agente. Manejadores de eventos Checked y Unchecked, los cuales son utilizados para apagar y prender los agentes.

  7. En MainPage.xaml.cs, agrega la siguiente directiva en el tope de la pagina:

    using Microsoft.Phone.Scheduler;
  8. Crea dos variables que representaran cada tipo de agente. Estos serán objetos que estarán enlazados a la interfaz de usuario. El servicio de acción programada identifica únicamente tareas programadas por su propiedad Name (Nombre). Crea dos variables conteniendo los nombres que se usaran para los agentes.  Crea una variable booleana para usar como determinar si algún agente de segundo plano haya sido deshabilitado por el usuario final. Agrega las siguiente líneas de código dentro de la definición de la clase:

    public partial class MainPage : PhoneApplicationPage
    {
      PeriodicTask periodicTask;
      ResourceIntensiveTask resourceIntensiveTask;
    
      string periodicTaskName = "PeriodicAgent";
      string resourceIntensiveTaskName = "ResourceIntensiveAgent";
      public bool agentsAreEnabled = true;
  9. Ahora, implementa un método helper llamado StartPeriodicAgent. Este método primero usa un método Find(String) para obtener la referencia de la tarea periódica con el nombre especificado. Si el objeto de tarea programada no es null, entonces podrás invocar Remove(String) para excluir el registro del sistema. No puedes actualizar agentes directamente. Deberás remover y luego agregar. Luego, crea un nuevo objeto PeriodicTask y asignar su nombre en el constructor.

    Ahora establece la propiedad Descripción. Esta propiedad es requerida para agentes periódicos y es usado para describir el agente al usuario en la pagina de configuración de tareas de segundo plano en el dispositivo. Luego, invoca Add(ScheduledAction) para registrar el agente periódico  en el sistema. Una excepción InvalidaOperationException  es lanzado cuando Add es invocado si el usuario ha deshabilitado agentes de segundo plano por la aplicación, por ende es importante utilizar un bloque try. Si la llamada es exitosa, establece el contexto de datos del elemento de la interfaz de usuario asociada y mostrar las propiedades del objeto al usuario. Si la llamada lanza una excepción, verifica el mensaje de la excepción. Si el mensaje dice «BNS Error: The action is disabled», entonces deberías alertar al usuario que los agentes de segundo plano están deshabilitados.El método LaunchForTest(String, TimeSpan) es incluido para ejecutar el agente un minuto después que este método es invocado.  Antes de la llamada anterior a este método, se coloca un bloque #If para permitir que la aplicación se intercambie fácilmente entre modos de depuración y producción. Para habilitar este método, incluye la siguiente línea al tope de MainPage.xaml.cs:

    #define DEBUG_AGENT

    Ahora pega el siguiente método en la definición de la clase MainPage

    private void StartPeriodicAgent()
    {
      // Variable for tracking enabled status of background agents for this app.
      agentsAreEnabled = true;
    
      // Obtain a reference to the period task, if one exists
      periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
    
      // If the task already exists and background agents are enabled for the
      // application, you must remove the task and then add it again to update 
      // the schedule
      if (periodicTask != null)
      {
        RemoveAgent(periodicTaskName);
      }
    
      periodicTask = new PeriodicTask(periodicTaskName);
    
      // The description is required for periodic agents. This is the string that the user
      // will see in the background services Settings page on the device.
      periodicTask.Description = "This demonstrates a periodic task.";
    
      // Place the call to Add in a try block in case the user has disabled agents.
      try
      {
        ScheduledActionService.Add(periodicTask);
        PeriodicStackPanel.DataContext = periodicTask;
    
        // If debugging is enabled, use LaunchForTest to launch the agent in one minute.
    #if(DEBUG_AGENT)
        ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(60));
    #endif
      }
      catch (InvalidOperationException exception)
      {
        if (exception.Message.Contains("BNS Error: The action is disabled"))
        {
          MessageBox.Show("Background agents for this application have been disabled by the user.");
          agentsAreEnabled = false;
          PeriodicCheckBox.IsChecked = false;
        }
    
        if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
        {
          // No user action required. The system prompts the user when the hard limit of periodic tasks has been reached.
    
        }
        PeriodicCheckBox.IsChecked = false;
      }
      catch (SchedulerServiceException)
      {
        // No user action required.
        PeriodicCheckBox.IsChecked = false;
      }
    }

    NOTA: Agentes de segundo plano no son soportados en dispositivos de 256-MB de memoria RAM. Al intentar  trabajar con agentes en estos dispositivos arrojaran una excepción InvalidaOperationException. En lugar de manejar esta excepción en un bloque catch, tu aplicación debería verificar si se esta ejecutando en un dispositivo 256-MB antes de agregar un agente.

  10. Ahora implementaremos el método helper para la tarea tipo recurso-intensivo. Este método es idéntico al método para el agente periódico a excepción que este utiliza la clase ResoucerIntensiveTaskpara programar el agente y otro nombre es utilizado.

    private void StartResourceIntensiveAgent()
    {
      // Variable for tracking enabled status of background agents for this app.
      agentsAreEnabled = true;
    
      resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask;
    
      // If the task already exists and background agents are enabled for the
      // application, you must remove the task and then add it again to update 
      // the schedule.
      if (resourceIntensiveTask != null)
      {
        RemoveAgent(resourceIntensiveTaskName);
      }
    
      resourceIntensiveTask = new ResourceIntensiveTask(resourceIntensiveTaskName);
    
      // The description is required for periodic agents. This is the string that the user
      // will see in the background services Settings page on the device.
      resourceIntensiveTask.Description = "This demonstrates a resource-intensive task.";
    
      // Place the call to Add in a try block in case the user has disabled agents.
      try
      {
        ScheduledActionService.Add(resourceIntensiveTask);
        ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask;
    
        // If debugging is enabled, use LaunchForTest to launch the agent in one minute.
    #if(DEBUG_AGENT)
        ScheduledActionService.LaunchForTest(resourceIntensiveTaskName, TimeSpan.FromSeconds(60));
    #endif
      }
      catch (InvalidOperationException exception)
      {
        if (exception.Message.Contains("BNS Error: The action is disabled"))
        {
          MessageBox.Show("Background agents for this application have been disabled by the user.");
          agentsAreEnabled = false;
    
        }
        ResourceIntensiveCheckBox.IsChecked = false;
      }
      catch (SchedulerServiceException)
      {
        // No user action required.
        ResourceIntensiveCheckBox.IsChecked = false;
      }
    }
  11. Agrega una variable booleana a la clase, ignoreCheckBoxEvents. Esta variable será utilizada para habilitar o deshabilitar los eventos de CheckBox al inicializar la pagina.

    bool ignoreCheckBoxEvents = false;
  12. Ahora, agrega manejadores de eventos Checked y Unchecked para los controles CheckBox. Estos manejadores invocan los métodos Start y Stop previamente implementados, para ejecutar y detener los agentes. Si ignoreCheckBoxEvents esta en verdadero, los manejadores de eventos serán retornados sin realizar ninguna función.

    private void PeriodicCheckBox_Checked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents) 
        return;
      StartPeriodicAgent();
    }
    private void PeriodicCheckBox_Unchecked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents)
       return;
      RemoveAgent(periodicTaskName);
    }
    private void ResourceIntensiveCheckBox_Checked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents)
        return;
      StartResourceIntensiveAgent();
    }
    private void ResourceIntensiveCheckBox_Unchecked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents)
        return;
      RemoveAgent(resourceIntensiveTaskName);
    }
  13. El método RemoveAgent simplemente invoca Remove(String) dentro de un bloque Try para evitar que alguna excepción altere el comportamiento de la aplicación.

    private void RemoveAgent(string name)
    {
      try
      {
        ScheduledActionService.Remove(name);
      }
      catch (Exception)
      {
      }
    }
  14. El paso final es hacer override el método OnNavigatedTo(NavigationEventArgs) de la clase PhoneApplicationPage. Este método es invocado cada vez que el usuario navega a esta pagina. Ahora debes asignar la variable booleana ignoreCheckBoxEvents

    a verdadero para que los manejadores de eventos de los CheckBox estén esencialmente deshabilitados. Luego, usa el método Find(string) para verificar por agentes tipo periódico o recurso-intensivo registrados en el sistema y actualizar los controles CheckBox para reflejar el estado actual de la aplicación. Finalmente, asigna el valor de la variable ignoreCheckBoxEventsa falso.

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
      ignoreCheckBoxEvents = true;
    
      periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
    
      if (periodicTask != null)
      {
        PeriodicStackPanel.DataContext = periodicTask;
      }
    
      resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask;
      if (resourceIntensiveTask != null)
      {
        ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask;
      }
    
      ignoreCheckBoxEvents = false;
    }

Fuente:
How to: Implement Background Agents for Windows Phone

Generation Apps – Día 21 – Acceder a la secuencia de imágenes de la cámara

Tu puedes crear una aplicación de cámara usando el API de la cámara para captura fotografías. En la lección del día de hoy te mostrare como desplegar el viewfinder de la cámara, disparar la cámara programáticamente, y salvar una fotografía tomada a la librería multimedia y en el almacenamiento aislado. Una vez que comprendas como crear una aplicación relacionada con la cámara, fácilmente estarás encaminado a implementar el flash y enfoque, cambiando la resolución de captura, y usando el botón físico de capturar imágenes.

Creando la interfaz de usuario de la cámara

En esta sección, crearemos una interfaz de usuario para la cámara que consiste en la región Viewfinder para desplegar las imágenes capturadas, y un botón disparador para capturar una imagen.

Creación del proyecto

  1. En Visual Studio 2010 Express para Windows Phone, crea un nuevo proyecto dando clic en Archivo –> Nuevo Proyecto

  2. En la ventana de nuevo proyecto. Expande las plantillas de Visual C#, y selecciona la plantilla Silverlight For Windows Phone

  3. Selecciona la plantilla Windows Phone Application. Coloca el nombre que desees para tu proyecto.

  4. Dale clic a OK. La pantalla de una Nueva aplicación para Windows Phone es desplegada

  5. Al momento de escoger la versión del S.O., asegúrate escoger la versión Windows Phone 7.1
  6. Da clic en OK. El nuevo proyecto es creado, y el archivo MainPage.xaml es abierto en el diseñado de Visual Studio

  7. Del menú de Proyecto, selecciona Add Reference. En la pestana de .NET, escoge Microsoft.XNA.Framework y luego da clic a OK.

  8. En MainPage.xaml, actualiza el elemento phone:PhoneApplicationPage con el siguiente código:

    SupportedOrientations="Landscape" Orientation="LandscapeLeft"
        shell:SystemTray.IsVisible="False"

    Esto configura la pagina para soportar la orientación horizontal y esconder la bandeja del sistema.

  9. Ahora en MainPage.xaml, reemplaza el Grid nombrado LayoutRootcon el siguiente código:

    <!--LayoutRoot is the root grid where all page content is placed-->
        <Grid x:Name="LayoutRoot" Background="Transparent">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="640" />
                <ColumnDefinition Width="160" />
            </Grid.ColumnDefinitions>
    
            <Canvas x:Name="viewfinderCanvas" Width="640" Height="480" 
                       HorizontalAlignment="Left" >
    
                <!--Camera viewfinder -->
                <Canvas.Background>
                    <VideoBrush x:Name="viewfinderBrush" />
                </Canvas.Background>
             </Canvas>
    
            <!--Button StackPanel to the right of viewfinder>-->
            <StackPanel Grid.Column="1" >
                <Button x:Name="ShutterButton" Content="SH" 
                 Click="ShutterButton_Click" FontSize="26" 
                 FontWeight="ExtraBold" Height="75" />
            </StackPanel>
    
            <!--Used for debugging >-->
            <TextBlock Height="40" HorizontalAlignment="Left" Margin="8,428,0,0" 
             Name="txtDebug" VerticalAlignment="Top" Width="626" FontSize="24" 
             FontWeight="ExtraBold" />
        </Grid>

    Este código crea una región Viewfinder de 640 x 480 pixeles con un control StackPanel que contiene un botón disparador. Ahora Implementaremos el evento ShutterButtonClick en los siguientes pasos.

  10. Dirígete al código C# MainPage.xaml.cs y agrega las siguientes directivas en la parte superior de la pagina:

    // Directives
    using Microsoft.Devices;
    using System.IO;
    using System.IO.IsolatedStorage;
    using Microsoft.Xna.Framework.Media;
  11. Ahora en la clase MainPage, declara los siguientes atributos arriba del constructor:

    // Variables
    private int savedCounter = 0;
    PhotoCamera cam;
    MediaLibrary library = new MediaLibrary();
  12. Para crear la aplicación de cámara, la capacidad de la cámara tiene que ser declarado en el archivo de manifesto del aplicación. Abre el archivo WMAppManifest.xmly confirma el siguiente elemento se encuentra presente:

    <Capability Name="ID_CAP_ISV_CAMERA"/>
  13. (Opcional) Si quieres que tu aplicación utilice cámara frontal, adicionalmente se debe agregar la siguiente capacidad en los elementos Capabilities en el archivo de manifesto de la aplicación:

    <Capability Name="ID_HW_FRONTCAMERA"/>

Implementando Viewfinder y eventos de la cámara

Para implementar el viewfinder, deberás establecer la fuente ViewfinderBrush en la cámara del Windows Phone. Además de esto implementaremos varios eventos relacionados de la cámara como la inicialización, captura completada, y disponibilidad de imagen.

Para implementar los eventos de cámara

  1. En MainPage.xaml.cs, agrega el siguiente código a la clase MainPage:

    //Code for initialization, capture completed, image availability events; also setting the source for the viewfinder.
    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
       // Check to see if the camera is available on the device.
       if ((PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true) ||
          (PhotoCamera.IsCameraTypeSupported(CameraType.FrontFacing) == true))
       {
          // Initialize the camera, when available.
          if (PhotoCamera.IsCameraTypeSupported(CameraType.FrontFacing))
          {
             // Use front-facing camera if available.
             cam = new Microsoft.Devices.PhotoCamera(CameraType.FrontFacing);
          }
          else
          {
             // Otherwise, use standard camera on back of device.
             cam = new Microsoft.Devices.PhotoCamera(CameraType.Primary);
          }
    
          // Event is fired when the PhotoCamera object has been initialized.
          cam.Initialized += new EventHandler<Microsoft.Devices.CameraOperationCompletedEventArgs>(cam_Initialized);
    
          // Event is fired when the capture sequence is complete.
          cam.CaptureCompleted += new EventHandler<CameraOperationCompletedEventArgs>(cam_CaptureCompleted);
    
          // Event is fired when the capture sequence is complete and an image is available.
          cam.CaptureImageAvailable += new EventHandler<Microsoft.Devices.ContentReadyEventArgs>(cam_CaptureImageAvailable);
    
          // Event is fired when the capture sequence is complete and a thumbnail image is available.
          cam.CaptureThumbnailAvailable += new EventHandler<ContentReadyEventArgs>(cam_CaptureThumbnailAvailable);
    
          //Set the VideoBrush source to the camera.
          viewfinderBrush.SetSource(cam);
       }
       else
       {
          // The camera is not supported on the device.
          this.Dispatcher.BeginInvoke(delegate()
          {
          // Write message.
          txtDebug.Text = "A Camera is not available on this device.";
          });
    
          // Disable UI.
          ShutterButton.IsEnabled = false;
       }
    }
    protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
    {
       if (cam != null)
       {
          // Dispose camera to minimize power consumption and to expedite shutdown.
          cam.Dispose();
    
          // Release memory, ensure garbage collection.
          cam.Initialized -= cam_Initialized;
          cam.CaptureCompleted -= cam_CaptureCompleted;
          cam.CaptureImageAvailable -= cam_CaptureImageAvailable;
          cam.CaptureThumbnailAvailable -= cam_CaptureThumbnailAvailable;
       }
    }
  2. En MainPage.xaml.cs, agrega el siguiente código a la clase MainPage:
    // Update the UI if initialization succeeds.
    void cam_Initialized(object sender, Microsoft.Devices.CameraOperationCompletedEventArgs e)
    {
       if (e.Succeeded)
       {
          this.Dispatcher.BeginInvoke(delegate()
          {
             // Write message.
             txtDebug.Text = "Camera initialized.";
          });
       }
    }

    Este código utiliza el evento de la cámara Initialize, y actualiza el TextBlock nombrado txtDebug. El método BeginInvoke es requerido para actualiza el estado porque la interfaz de la aplicación se ejecuta en otro hilo de proceso diferente.

  3. En MainPage.xaml.cs, agrega el siguiente código a la clase MainPage:
    // Ensure that the viewfinder is upright in LandscapeRight.
    protected override void OnOrientationChanged(OrientationChangedEventArgs e)
    {
         if (cam != null)
         {
              // LandscapeRight rotation when camera is on back of device.
              int landscapeRightRotation = 180;
    
              // Change LandscapeRight rotation for front-facing camera.
              if (cam.CameraType == CameraType.FrontFacing) landscapeRightRotation = -180;
    
              // Rotate video brush from camera.
              if (e.Orientation == PageOrientation.LandscapeRight)
              {
                   // Rotate for LandscapeRight orientation.
                   viewfinderBrush.RelativeTransform = new CompositeTransform() 
                   { CenterX = 0.5, CenterY = 0.5, Rotation = landscapeRightRotation };
              }
              else
              {
                   // Rotate for standard landscape orientation.
                   viewfinderBrush.RelativeTransform = new CompositeTransform() 
                   { CenterX = 0.5, CenterY = 0.5, Rotation = 0 };
              }
         }
    
         base.OnOrientationChanged(e);
    }

    Este código asegura que el viewfinder, viewFinderBrush esta orientado de la manera correcta en la manera en que el usuario este sosteniendo el Windows Phone. Si la cámara frontal esta siendo utilizada, el brush correspondiente debe ser rotada a la dirección opuesta como si se encontrara en la parte trasera del dispositivo.

  4. En MainPage.xaml.cs, agrega el siguiente código a la clase MainPage:

    private void ShutterButton_Click(object sender, RoutedEventArgs e)
    {    if (cam != null)    {       try       {          // Start image capture.          cam.CaptureImage();       }       catch (Exception ex)       {          this.Dispatcher.BeginInvoke(delegate()             {             // Cannot capture an image until the previous capture has completed.             txtDebug.Text = ex.Message;             });       }    }
    }
    void cam_CaptureCompleted(object sender, CameraOperationCompletedEventArgs e)
    {    // Increments the savedCounter variable used for generating JPEG file names.    savedCounter++;
    }

    Este código implementa los eventos del botón disparador y captura completada. El botón disparador es el elemento que agregamos en el código XAML para capturar fotografías con la cámara. El evento de captura completada es utilizado en este proyecto para incrementar la variable contador savedCounter. Es utilizado para la siguiente sección para nombrar las imágenes JPEG guardadas.

Guardando fotografías a la galería multimedia

Para una aplicación de cámara para Windows Phone 7.1, dos imágenes son capturadas cuando una foto es tomada. Una es la imagen con la resolución máxima, y la segunda es una imagen thumbnail usados para mostrar una versión mas reducida para mostrar en la galería de imágenes. Esta sección explica como guardar la imagen con resolución máxima en la librería multimedia del dispositivo. También aprenderá como salvar una imagen con resolución máxima y la imagen thumbnail en el almacenamiento aislado.

Para guardar la imagen en la librería multimedia y almacenamiento aislado

  1. En MainPage.xaml.cs, agrega el siguiente código a la clase MainPage:

    // Informs when full resolution picture has been taken, saves to local media library and isolated storage.
    void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
    {
       string fileName = savedCounter + ".jpg";
    
       try
       {   // Write message to the UI thread.
          Deployment.Current.Dispatcher.BeginInvoke(delegate()
          {
          txtDebug.Text = "Captured image available, saving picture.";
          });
    
          // Save picture to the library camera roll.
          library.SavePictureToCameraRoll(fileName, e.ImageStream);
    
          // Write message to the UI thread.
          Deployment.Current.Dispatcher.BeginInvoke(delegate()
          {
          txtDebug.Text = "Picture has been saved to camera roll.";
          });
    
          // Set the position of the stream back to start
          e.ImageStream.Seek(0, SeekOrigin.Begin);
    
          // Save picture as JPEG to isolated storage.
          using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
          {
             using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
             {
                // Initialize the buffer for 4KB disk pages.
                byte[] readBuffer = new byte[4096];
                int bytesRead = -1;
    
                // Copy the image to isolated storage. 
                while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
                {
                   targetStream.Write(readBuffer, 0, bytesRead);
                }
             }
          }
    
          // Write message to the UI thread.
          Deployment.Current.Dispatcher.BeginInvoke(delegate()
          {
             txtDebug.Text = "Picture has been saved to isolated storage.";
          });
       }
       finally
       {
          // Close image stream
          e.ImageStream.Close();
       }
    }
    
    // Informs when thumbnail picture has been taken, saves to isolated storage
    // User will select this image in the pictures application to bring up the full-resolution picture. 
    public void cam_CaptureThumbnailAvailable(object sender, ContentReadyEventArgs e)
    {
       string fileName = savedCounter + "_th.jpg";
    
       try
       {
          // Write message to UI thread.
          Deployment.Current.Dispatcher.BeginInvoke(delegate()
          {
          txtDebug.Text = "Captured image available, saving thumbnail.";
          });
    
          // Save thumbnail as JPEG to isolated storage.
          using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
          {
             using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
             {
                // Initialize the buffer for 4KB disk pages.
                byte[] readBuffer = new byte[4096];
                int bytesRead = -1;
    
                // Copy the thumbnail to isolated storage. 
                while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
                {
                   targetStream.Write(readBuffer, 0, bytesRead);
                }
             }
          }
    
          // Write message to UI thread.
          Deployment.Current.Dispatcher.BeginInvoke(delegate()
          {
             txtDebug.Text = "Thumbnail has been saved to isolated storage.";
          });
       }
       finally
       {
          // Close image stream
          e.ImageStream.Close();
       }
    }

    Este código implementa los eventos CaptureImageAvailable y CaptureThumbnailAvailable. El primer método guarda la imagen con resolución máxima en la librería multimedia y en almacenamiento aislado. El segundo método te muestra como se guarda una imagen thumbnail en almacenamiento aislado.

  2. En el dispositivo, ejecuta la aplicación seleccionando el comando de menú Debug|Start Debugging. Presiona el botón SH. El textblock txtDebug te indicara el estado de las operaciones para guardar las imágenes en almacenamiento aislado. Después de cerrar la aplicación, puedes encontrar las fotos que tomaste con tu aplicación la carpeta camera roll en el hub de fotos. En la siguiente imagen te ilustra como debería aparecer la interfaz grafica en este punto.

    En este ejemplo. el botón disparador SH, es mostrado en la esquina superior derecha de la interfaz. Una imagen de mangos es mostrado en el control Canvas nombrado viewFinderCanvas.

Cabe mencionar que estos ejemplos es solo una introducción a lo que se puede implementar usando la cámara. Existen mas funcionalidades útiles que involucran la cámara como usar el Flash, el enfoque, la resolución de la imagen capturada, etc.

Fuente:
How to: Create a Base Camera Application for Windows Phone

Generation Apps – Día 19 – El micrófono con Silverlight/XNA

Windows Phone provee una plataforma multimedia robusta que permite aplicaciones a hacer streaming de video y audio, manejo del radio, y la integración con el Hub de música y videos. En la lección de este día, te mostrare la forma como obtener input de audio utilizando el micrófono del Windows Phone con aplicaciones Silverlight usando la clase Microsoft.Xna.Framework.Audio.Microphone.

  1. Ejecuta Visual Studio 2010 Express para Windows Phone.

  2. Crea un nuevo proyecto seleccionando haciendo clic en Archivo | Nuevo Proyecto

  3. La ventana de Nuevo Proyecto aparece. Expande las plantillas de Visual C#, y selecciona la plantilla Silverlight para Windows Phone.

  4. Ahora selecciona la plantilla Aplicación Windows Phone. Coloca de nombre de proyecto tu preferencia.

  5. Da clic a OK. La ventana de selección de plataforma Windows Phone aparece. Selecciona Windows Phone OS 7.1 para la versión de Windows Phone objetivo.

  6. Da clic a OK. El proyecto nuevo es creado y se despliega el archivo MainPage.xaml en el diseñador de Visual Studio.

  7. En el Solution Explorer, da un clic derecho a References -> Add Reference…

  8. Agrega la librería Microsoft.Xna.Framework de la lista de componentes .NET y da clic a OK.

  9. Si ves un dialogo advirtiéndote acerca de agregar assembly de Silverlight, da clic en Yes.

  10. Agrega las siguientes directivas en el archivo MainPage.xaml.cs

    using System.IO;
    using System.Windows.Threading;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
  11. En MainPage.xaml.cs, declara las siguientes variables globales en tu clase MainPage

    public partial class MainPage : PhoneApplicationPage
    {
        Microphone microphone = Microphone.Default;
        byte[] buffer;
        MemoryStream stream = new MemoryStream();
        SoundEffect sound; 
        // ...
  12. Ya que estamos usando XNA Game Studio en una aplicación Silverlight, debemos simular el Game Loop que XNA implementa por nosotros. Agrega el siguiente código en el constructor de la clase MainPage después de la llamada InitializeComponent para simular el Game Loop de XNA:

    // Timer to simulate the XNA Game Studio game loop (Microphone is from XNA Game Studio)
    DispatcherTimer dt = new DispatcherTimer();
    dt.Interval = TimeSpan.FromMilliseconds(50);
    dt.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
    dt.Start();
  13. Ahora agrega un manejador de evento Microsoft.BufferReady en el constructor de la clase MainPage:

    microphone.BufferReady += new EventHandler<EventArgs>(microphone_BufferReady);

    Tu declaración de variables y eventos del constructor se deberá ver algo como esto:

    public partial class MainPage : PhoneApplicationPage
    {
        Microphone microphone = Microphone.Default;
        byte[] buffer;
        MemoryStream stream = new MemoryStream();
        SoundEffect sound;
    
        // Constructor
        public MainPage()
        {
            InitializeComponent();
    
            // Timer to simulate the XNA Game Studio game loop (Microphone is from XNA Game Studio)
            DispatcherTimer dt = new DispatcherTimer();
            dt.Interval = TimeSpan.FromMilliseconds(33);
            dt.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
            dt.Start();
    
            microphone.BufferReady += new EventHandler<EventArgs>(microphone_BufferReady);
        } 
        ...
  14. Implementa el manejador de evento BufferReady. Este manejador copia los datos del micrófono a un buffer y escribe ese buffer a un stream:

    void  microphone_BufferReady(object sender, EventArgs e)
    {
        microphone.GetData(buffer);
        stream.Write(buffer, 0, buffer.Length);
    }
  15. Ahora agrega una manera que el usuario pueda activar el micrófono para captar audio. Puedes agregar un botón Start (nombrado recordButton) donde este crea un buffer de 1 segundo y empieza a coleccionar datos llamando al método Microphone.Start.

    private void recordButton_Click(object sender, RoutedEventArgs e)
    {
        microphone.BufferDuration = TimeSpan.FromMilliseconds(1000);
        buffer = new byte[microphone.GetSampleSizeInBytes(microphone.BufferDuration)];
        microphone.Start();
    }
  16. Agrega una manera para que el usuario pueda detener la captura de audio del micrófono. Puedes agregar un botón Stop (nombrado stopButton), y agregarle un manejador de evento clic, donde este chequea si el estado del micrófono esta activo, lo detiene.

    private void stopButton_Click(object sender, RoutedEventArgs e)
    {
        if (microphone.State == MicrophoneState.Started)
        {
            microphone.Stop();
        }
    }
  17. Finalmente, agrega un botón para que el usuario pueda reproducir la grabación capturada por el micrófono. Agrégale un manejador de evento Clic, donde este instancia un nuevo objeto SoundEffect usando el stream de donde los datos de audio fueron guardados. Luego, se invoca SoundEffect.Play para reproducir la grabación.

    private void playButton_Click(object sender, RoutedEventArgs e)
    {
        sound = new SoundEffect(stream.ToArray(), microphone.SampleRate, AudioChannels.Mono);
        sound.Play();
    }

Observa que con unos cuantos pasos, puedes acceder al micrófono para hacer cualquier cantidad de cosas que desees implementar en tu aplicación.

Fuente:
How to: Acces the Microphone in a Silverlight Application for Windows Phone

Generation Apps – Día 18 – Extender la experiencia de búsqueda con App Connect

Con Windows Phone OS 7.1, aplicaciones pueden usar App Connect para extender la experiencia de búsqueda de Windows Phone. Este tópico describe como crear una aplicación que utiliza App Connect y realizar búsquedas desplegando resultados en listas de fichas informativas de productos, películas y lugares. Esta aplicación extrae parámetros del enlace de App Connect y las despliega en una página de la aplicación.

NOTA IMPORTANTE: Aplicaciones que abusen del App Connect arriesgan ser retirados del marketplace. Puedes registrar extensiones de búsqueda que solo sean relevantes en tu aplicación. El propósito del App Connect es ahorrarle tiempo al usuario. Aplicaciones que utilicen el App Connect deben usar los parámetros de una manera importante. Por ejemplo, utiliza App Connect para que automáticamente realice una búsqueda en tu aplicación cuando es ejecutado de una ficha informativa.

Este tópico involucra hacer los siguientes pasos:

  • Configurando el manifesto de la aplicación

  • Creando el archivo Extras.xml

  • Mapeando el URI desde el Quick Card

  • Creando el data model

  • Creando el ViewModel

  • Creando la pagina quick card target

  • Completando la aplicación

  • Probando con quick cards

  • Depurando la aplicación

En el proceso, iremos modificando y creando los siguientes archivos:

  • WMAppManifest.xml: Modificamos este archivo de manifesto de la aplicación para especificar las extensiones de búsqueda correspondientes con la aplicación.

  • Extensions\Extras.xml: Creamos este archivo para especificar el titulo de la aplicación que va aparecer en la pagina pivote del quick card correspondiente.

  • App.xaml: Modificamos este archivo para especificar el URI mapping desde el link del quick card hacia la pagina quick card target.

  • App.xaml.cs: Modificamos este archivo para habilitar el URI mapping en la aplicación.

  • Model\QuickCardUriParameters.cs: Creamos esta clase para representar un parámetro en un enlace URI de App Connect. Esta clase implementa la interfaz INotifyPropertyChanged.

  • ViewModel\QuickCardViewModel:  Creamos esta clase para representar el ViewModel de la pagina quick card target. Esta clase extrae los parámetros del enlace URI de App Connect  e  implementa la interfaz INotifyPropertyChanged.

  • QuickCardTargetPage: Modifica esta pagina para crear un objeto ViewModel y cargar los parámetros en el ViewModel cuando la pagina es cargada.

  • MainPage.xaml: Modificamos esta pagina para presentar texto para en la ejecución de una aplicación estándar. Esta pagina no es iniciada por App Connect.

Para modificar el manifiesto de la aplicación

  1. Ejecuta Visual Studio 2010 Express para Windows Phone.

  2. Crea un nuevo proyecto seleccionando haciendo clic en Archivo | Nuevo Proyecto

  3. La ventana de Nuevo Proyecto aparece. Expande las plantillas de Visual C#, y selecciona la plantilla Silverlight para Windows Phone.

  4. Ahora selecciona la plantilla Aplicación Windows Phone. Coloca de nombre de proyecto tu preferencia.

  5. Da clic a OK. La ventana de selección de plataforma Windows Phone aparece. Selecciona Windows Phone OS 7.1 para la versión de Windows Phone objetivo.

  6. Da clic a OK. El proyecto nuevo es creado y se despliega el archivo MainPage.xamlen el diseñador de Visual Studio.

  7. En el Solution Explorer, expande Properties y da doble clic en WPAppManifest.xml para modificarlo.

  8. En WPAppManifest.xml , agrega el siguiente código en el elemento App, de los elementos Tokens.

    <Extensions>
          <!-- Production extensions, for products: video games -->
          <Extension
            ExtensionName="Bing_Products_Video_Games"
            ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}"
            TaskID="_default"
            ExtraFile="Extensions\\Extras.xml" />
    
          <!-- Production extensions, for movies. -->
          <Extension
            ExtensionName="Bing_Movies"
            ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}"
            TaskID="_default"
            ExtraFile="Extensions\\Extras.xml" />
    
          <!-- Production extensions, for places: travel, food, and dining. -->
          <Extension
            ExtensionName="Bing_Places_Food_and_Dining"
            ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}"
            TaskID="_default"
            ExtraFile="Extensions\\Extras.xml" />
        </Extensions>

Este código agrega tres extensiones, uno por cada tipo de fichas informativas: ficha producto, ficha película y ficha lugar. Un ficha informativa que no este asociada con estas extensiones no se desplegarán en las páginas pivote correspondientes.

Creando el archivo Extras.xml

  1. En el Solution Explorer, da un clic derecho de tu proyecto y selecciona Add -> New Folder.

  2. Nombra el nuevo directorio Extensions.

  3. En el Solution Explorer, da un clic derecho sobre el directorio Extensions, y selecciona Add -> New Item.

  4. En la ventana de Add New Item, selecciona un archivo XML y nómbralo Extras.xml, luego da clic en Add.

  5. En Extras.xml, reemplaza el código con lo siguiente:

    <?xml version="1.0" encoding="utf-8" ?>
    <ExtrasInfo>
    
      <!-- Application title -->
      <AppTitle>
        <default>Display App Connect URI Parameters</default>
      </AppTitle>
    
      <!-- Search-related captions -->
      <Consumer ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}">
    
        <!-- Products caption for video games -->
        <ExtensionInfo>
          <Extensions>
            <ExtensionName>Bing_Products_Video_Games</ExtensionName>
          </Extensions>
          <CaptionString>
            <default>Product URI Details</default>
          </CaptionString>
        </ExtensionInfo>
    
        <!-- Movies caption -->
        <ExtensionInfo>
          <Extensions>
            <ExtensionName>Bing_Movies</ExtensionName>
          </Extensions>
          <CaptionString>
            <default>Movie URI Details</default>
          </CaptionString>
        </ExtensionInfo>
    
        <!-- Places caption for food and dining -->
        <ExtensionInfo>
          <Extensions>
            <ExtensionName>Bing_Places_Food_and_Dining</ExtensionName>
          </Extensions>
          <CaptionString>
            <default>Place URI Details</default>
          </CaptionString>
        </ExtensionInfo>
      </Consumer>
    </ExtrasInfo>

Este código especifica como el titulo de la aplicación aparecerán en la pantalla pivote Apps, dependiendo del tipo de ficha. En la página pivote de App para todas las fichas, el titulo de la aplicación será Display Apps Connect URI Parameters. El titulo será:

  • Para las fichas de productos (asociados con la extensión Bing_Products_Electronics): Product URI Details

  • Para las fichas de películas (asociados con la extensión Bing_Movies): Movie_URI_Details

  • Para las fichas de lugares (asociados con la extensión Bing_Places_Food_and_Dinning): Place URI Detals.

Mapping el URI desde el Quick Card

  1. En el Solution Explorer, da un clic derecho sobre tu proyecto y selecciona Add -> Class

  2. Nombra esta nueva clase QuickCardUriMapper.cs

  3. En la clase  QuickCardUriMapper.cs, agrega las siguientes directivas:

    using System;
    using System.Windows.Navigation;
    using System.Net;
  4. Actualiza la declaración de la clase, que hereda la clase UriMapperBase:

    public class QuickCardUriMapper : UriMapperBase
  5. En la clase QuickCardUriMapper, agrega el siguiente código. Este código re-implementa cada uno de los valores de parámetros del App Connect URI. La pagina destino del URI esta determinado por el string estático nombrado TargetPageName:

    // Navigation destination. 
    private static string TargetPageName = "QuickCardTargetPage.xaml";
    private string tempUri;
    
    public override Uri MapUri(Uri uri)
    {
        tempUri = uri.ToString();
    
        // Parse URI when launched by App Connect from Search
        if (tempUri.Contains("/SearchExtras"))
        {
            // Decode all characters in the URI.
            tempUri = HttpUtility.UrlDecode(tempUri);
    
            // Create a new URI for product cards.
            if (tempUri.Contains("Bing_Products"))
            {
                return GetProductCardUri(tempUri);
            }
    
            // Create a new URI for place cards.
            if (tempUri.Contains("Bing_Places"))
            {
                return GetPlaceCardUri(tempUri);
            }
    
            // Create a new URI for movie cards.
            if (tempUri.Contains("Bing_Movies"))
            {
                return GetMovieCardUri(tempUri);
            }
        }
    
        // Immediately return the URI when it is not related to App Connect for Search.
        return uri;
    }
    
    // Return a parsed Product Card URI.
    private Uri GetProductCardUri(string uri)
    {
        // Extract parameter values from URI.
        string ProductNameValue = GetURIParameterValue("ProductName=",uri);
        string CategoryValue = GetURIParameterValue("Category=",uri);
    
        // Create new URI.
        string NewURI = String.Format("/{0}?ProductName={1}&Category={2}", 
                                TargetPageName, ProductNameValue, CategoryValue);
    
        return new Uri(NewURI, UriKind.Relative);
    }
    
    // Return a parsed Place Card URI.
    private Uri GetPlaceCardUri(string uri)
    {
        // Extract parameter values from URI.
        string PlaceNameValue = GetURIParameterValue("PlaceName=", uri);
        string PlaceLatitudeValue = GetURIParameterValue("PlaceLatitude=", uri);
        string PlaceLongitudeValue = GetURIParameterValue("PlaceLongitude=", uri);
        string PlaceAddressValue = GetURIParameterValue("PlaceAddress=", uri);
        string CategoryValue = GetURIParameterValue("Category=", uri); 
    
        // Create new URI.
        string NewURI = String.Format("/{0}?PlaceName={1}&PlaceLatitude={2}&PlaceLongitude={3}&PlaceAddress={4}&Category={5}", 
                                TargetPageName, PlaceNameValue, PlaceLatitudeValue, PlaceLongitudeValue, PlaceAddressValue, CategoryValue);
    
        return new Uri(NewURI, UriKind.Relative);       
    }
    
    // Return a parsed Movie Card URI.
    private Uri GetMovieCardUri(string uri)
    {
        // Extract parameter values from URI.
        string MovieNameValue = GetURIParameterValue("MovieName=", uri);
        string CategoryValue = GetURIParameterValue("Category=", uri);
    
        // Create new URI.
        string NewURI = String.Format("/{0}?MovieName={1}&Category={2}",
                    TargetPageName, MovieNameValue, CategoryValue);
    
        return new Uri(NewURI, UriKind.Relative);
    }
    
    // This method extracts the string values that correspond to parameters in an App Connect URI.
    private string GetURIParameterValue(string parameteridentifier, string uri)
    {
        string tempValue = "";
    
        // If the parameter exists in the string, extract the corresonding parameter value.
        if (uri.Contains(parameteridentifier))
        {
            string subUri; 
    
            // Extract the characters that contain and follow the parameter identifier.
            subUri = uri.Substring(uri.LastIndexOf(parameteridentifier));
    
            // Remove the parameter identifier from the substring.
            subUri = subUri.Replace(parameteridentifier, "");
    
            // Obtain the position of the next parameter in the substring.
            int nextParameterPosition = FindNextParameter(subUri);
    
            if (nextParameterPosition < int.MaxValue)
            {
                // Remove the characters that contain and follow the next parameter.
                tempValue = subUri.Substring(0, nextParameterPosition);
            }
            else
            {
                // No more parameters follow in the string. 
                tempValue = subUri;
            }
    
            // Encode the parameter values to help prevent issues in the URI.
            tempValue = HttpUtility.UrlEncode(tempValue);
        }
    
        return tempValue;
    }
    
    // Returns the string position of the next App Connect URI parameter, if applicable.
    private int FindNextParameter(string subUri)
    {
        int lowestPosition = int.MaxValue;
        int tempPosition;
    
        tempPosition = subUri.IndexOf("&ProductName");
        if ((tempPosition > -1) && (tempPosition < lowestPosition)) lowestPosition = tempPosition;
    
        tempPosition = subUri.IndexOf("&Category");
        if ((tempPosition > -1) && (tempPosition < lowestPosition)) lowestPosition = tempPosition;
    
        tempPosition = subUri.IndexOf("&PlaceName");
        if ((tempPosition > -1) && (tempPosition < lowestPosition)) lowestPosition = tempPosition;
    
        tempPosition = subUri.IndexOf("?PlaceName");
        if ((tempPosition > -1) && (tempPosition < lowestPosition)) lowestPosition = tempPosition;
    
        tempPosition = subUri.IndexOf("&PlaceLatitude");
        if ((tempPosition > -1) && (tempPosition < lowestPosition)) lowestPosition = tempPosition;
    
        tempPosition = subUri.IndexOf("&PlaceLongitude");
        if ((tempPosition > -1) && (tempPosition < lowestPosition)) lowestPosition = tempPosition;
    
        tempPosition = subUri.IndexOf("&PlaceAddress");
        if ((tempPosition > -1) && (tempPosition < lowestPosition)) lowestPosition = tempPosition;
    
        tempPosition = subUri.IndexOf("&MovieName");
        if ((tempPosition > -1) && (tempPosition < lowestPosition)) lowestPosition = tempPosition;
    
        return lowestPosition;
    }

Para asignar el URI-Mapper al Application Frame

En App.xaml.cs, agrega el siguiente código al método InitializePhoneApplication. Nota que puede que necesites expandir la región de código titulada «Phone Application initialization» para localizar el método:

// Assign the quick card URI-mapper class to the application frame.
RootFrame.UriMapper = new QuickCardUriMapper();

Este código asigna la clase QuickCardUriMapper a la propiedad UriMapper del application frame. No modifiques nada del código existente en el método InitializePhoneApplication; solo agrega la asignación Uri-Mapper, como se muestra en el ejemplo:

private void InitializePhoneApplication()
{
    if (phoneApplicationInitialized)
        return;

    // Create the frame but don't set it as RootVisual yet; this allows the splash
    // screen to remain active until the application is ready to render.
    RootFrame = new PhoneApplicationFrame();
    RootFrame.Navigated += CompleteInitializePhoneApplication;

    // Assign the quick card URI-mapper class to the application frame.
    RootFrame.UriMapper = new QuickCardUriMapper();

    // Handle navigation failures
    RootFrame.NavigationFailed += RootFrame_NavigationFailed;

    // Ensure we don't initialize again
    phoneApplicationInitialized = true;
}

Creando el data model

  1. En el Solution Explorer, da un clic derecho a tu proyecto, y selecciona New Folder.

  2. Nombra el nuevo directorio Model.

  3. En el Solution Explorer, da un clic derecho sobre el directorio recién creado Model, y selecciona Add -> New Item.

  4. En la ventana de Add New Item, selecciona Code File y nómbralo AppConnectUriParameter.cs y da clic en Add.

  5. En AppConnectUriParameter.cs, agrega el siguiente código

    using System.ComponentModel;
    
    namespace AppConnectExample.Model
    {
        // Represents a parameter from a quick card in an App Connect deep link URI
        public class AppConnectUriParameter : INotifyPropertyChanged
        {
            // The parameter name
            private string _paramName;
            public string ParamName
            {
                get {return _paramName;}
                set
                {
                    if (_paramName != value)
                    {
                        _paramName = value;
                        NotifyPropertyChanged("ParamName");
                    }
                }
            }
    
            // The parameter value
            private string _paramValue;
            public string ParamValue
            {
                get {return _paramValue;}
                set
                {
                    if (_paramValue != value)
                    {
                        _paramValue = value;
                        NotifyPropertyChanged("ParamValue");
                    }
                }
            }
    
            // Class constructor
            public AppConnectUriParameter(string pName, string pValue)
            {
                _paramName = pName.Trim();
    
                if (_paramName == "Category")
                {
                // Place multiple categories on new lines.
                    _paramValue = pValue.Replace(",",",\n");
                }
                else
                {
                    _paramValue = pValue;
                }
            }
    
            #region INotifyPropertyChanged Members
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            // Used to notify that a property changed
            private void NotifyPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
            #endregion
        }
    }

    Esta clase consiste de propiedades para el parámetro nombre y valor, un constructor que acepta el nombre y valor, y los miembros INotifyPropertyChanged.

Creando el ViewModel

  1. En el Solution Explorer, da un clic derecho a tu proyecto, y selecciona New Folder.

  2. Nombra el nuevo directorio ViewModel.

  3. En el Solution Explorer, da un clic derecho sobre el directorio recién creado ViewModel, y selecciona Add -> New Item.

  4. En la ventana de Add New Item, selecciona Code File y nómbralo QuickCardTargetPageViewModel.cs y da clic en Add.

  5. En QuickCardTargetPageViewModel.cs, agrega el siguiente código:

    using System.ComponentModel;
    using System.Collections.ObjectModel;
    using System.Collections.Generic;
    
    // Reference the data model.
    using AppConnectExample.Model;
    
    namespace AppConnectExample.ViewModel
    {    
        public class QuickCardTargetPageViewModel: INotifyPropertyChanged
        {
            // Observeable collection for the App Connect deep link URI parameters.
            private ObservableCollection<AppConnectUriParameter> _AppConnectUriParameters;
            public ObservableCollection<AppConnectUriParameter> AppConnectUriParameters
            {
                get {return _AppConnectUriParameters;}
                set
                {
                    if (_AppConnectUriParameters != value)
                    {
                        _AppConnectUriParameters = value;
                        NotifyPropertyChanged("AppConnectUriParameters");
                    }
                }
            }
    
            // Class constructor.
            public QuickCardTargetPageViewModel()
            {
                // Create observeable collection object.
                AppConnectUriParameters = new ObservableCollection<AppConnectUriParameter>();
            }
    
            // Load parameters from quick page; extract from the NavigationContext.QueryString
            public void LoadUriParameters(IDictionary<string,string> QueryString)
            {
                // Clear parameters in the ViewModel.
                AppConnectUriParameters.Clear();
    
                // Loop through the quick card parameters in the App Connect deep link URI.
                foreach (string strKey in QueryString.Keys)
                {
                    // Set default value for parameter if no value is present.
                    string strKeyValue = "<no value present in URI>";
    
                    // Try to extract parameter value from URI.
                    QueryString.TryGetValue(strKey, out strKeyValue);
    
                    // Add parameter object to ViewModel collection.
                    AppConnectUriParameters.Add(new AppConnectUriParameter(strKey, strKeyValue));
                }
            }
    
            #region INotifyPropertyChanged Members
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            // Used to notify that a property has changed.
            private void NotifyPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
            #endregion
        }
    }

    En el método LoadUriParameters, el ViewModel extra los parámetros del enlace URI del App Connect y los carga en la colección observable del tipo AppConnectUriParameter.

Creando la pagina objetivo de la ficha informativa

  1. En el SolutionExplorer, da un clic derecho sobre el proyecto y selecciona Add -> New Item.

  2. En la ventana Add New Item, selecciona Windows Phone Portrait Page y nómbralo QuickTargetPage.xaml. Da clic en Add.

  3. En el QuickTargetPage.xaml, reemplaza el grid nombrado LayoutRoot con el siguiente código:

    <!--LayoutRoot is the root grid where all page content is placed-->
        <Grid x:Name="LayoutRoot" Background="Transparent">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
    
            <!--TitlePanel contains the name of the application and page title-->
            <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
                <TextBlock 
                    x:Name="ApplicationTitle" 
                    Text="QUICK CARD EXAMPLE" 
                    Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock 
                    x:Name="PageTitle" 
                    Text="URI details" 
                    Margin="9,-7,0,0" 
                    Style="{StaticResource PhoneTextTitle1Style}"/>
            </StackPanel>
    
            <!--ContentPanel contains ListBox and ListBox ItemTemplate.-->
            <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
                <ListBox x:Name="paramsListBox" Margin="0,0,-12,0" ItemsSource="{Binding AppConnectUriParameters}" >
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel >
                                <TextBlock 
                                    Text="{Binding ParamName}" 
                                    TextWrapping="Wrap" 
                                    Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                                <TextBlock 
                                    Text="{Binding ParamValue}" 
                                    TextWrapping="Wrap" 
                                    Margin="12,-6,12,0" 
                                    Style="{StaticResource PhoneTextAccentStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </Grid>
        </Grid>

    En esta pagina, un control ListBox esta enlazado con una colección observable en el ViewModel, , nombrado AppConnectUriParameters. Dentro del ListBox, dos controles TextBlock que están enlazados en cada parámetro de la colección observable. Un control TextBlock enlazado a la propiedad ParamName, otro control TextBlock enlazado a la propiedad ParamValue.

  4. En el código C# para la pagina objetivo de la ficha, QuickCardTargetPage.xaml.cs, agrega la siguiente directiva en el tope de la página:

    // Reference the ViewModel.
    using AppConnectExample.ViewModel;
  5. En QuickCardTargetPage.xaml.cs, agrega el siguiente código dentro del constructor de la clase después de la instrucción InitializeComponent().

    // Create the ViewModel object.
    this.DataContext = new QuickCardTargetPageViewModel();
    
    // Create event handler for the page Loaded event.
    this.Loaded += new RoutedEventHandler(QuickCardTargetPage_Loaded);

    Este código crea un nuevo objeto QuickCardTargetPageViewModel y lo asigna al contexto de datos de la pagina. Este código también crea un manejador de evento Loaded cuando se carga la pagina.

  6. En QuickCardTargetPage.xaml.cs, agrega el siguiente código en la clase:
    // A property for the ViewModel.
    QuickCardTargetPageViewModel ViewModel
    {
        get { return (QuickCardTargetPageViewModel)DataContext; }
    }
    
    private void QuickCardTargetPage_Loaded(object sender, RoutedEventArgs e)
    {
        // Load the quick card parameters from the App Connect deep link URI.
        ViewModel.LoadUriParameters(this.NavigationContext.QueryString);
    }

    Esto crea una propiedad para el ViewModel para que el método LoadUriParameters pueden ser llamados. En el manejador de evento Loaded, la propiedad NavigationContext.QueryString es pasado al ViewModel para que los parámetros pueden ser extraídos desde el enlace URI del App Connect.

Completando la aplicación

En el MainPage.xaml, reemplaza el siguiente código con el grid nombrado LayoutRoot

<!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock 
                x:Name="ApplicationTitle" 
                Text="QUICK CARD EXAMPLE" 
                Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock 
                x:Name="PageTitle" 
                Text="main page" 
                Margin="9,-7,0,0" 
                Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="24,0,42,338">
            <TextBlock 
                Text="With App Connect, you can navigate directly to a relevant page in your app from a Bing quick card."
                TextWrapping="Wrap"  
                Style="{StaticResource PhoneTextAccentStyle}" 
                Margin="0,0,12,181" />

            <TextBlock Text="To launch this app from a quick card, perform the testing steps outlined in the documentation."
                TextWrapping="Wrap" 
                Style="{StaticResource PhoneTextAccentStyle}" 
                Margin="0,94,0,83" />
        </Grid>
    </Grid>

Esto completa la implementación de la aplicación.

Probando con Quick Cards

Esta aplicación esta diseñada para ayudar explorar fichas informativas. En esta sección, realizaremos búsqueda de productos, películas y lugares asociados con las extensiones que configuramos en el archivo de manifiesto de la aplicación. Después de localizar una ficha, iniciarás tu aplicación para ver los parámetros que fueron pasados a tu aplicación desde el enlace URI del App Connect.

NOTA: Para usar esta aplicación, debes contar con una conexión de internet.

Demo con las fichas informativas de producto

  1. Presiona F5 para comenzar la depuración del proyecto, ejecutando el emulador con la aplicación.

  2. Una vez la aplicación cargada, da clic sobre el botón de búsqueda de hardware.

  3. En Bing, ingresa un termino para buscar que este relacionado a productos de videojuegos, como un nombre de consola de videojuegos. Por ejemplo, «xbox 360» te retornara fichas de productos relacionados con la consola Xbox 360. Para probar con fichas de producto, dos cosas son requeridas:Bing debe considerar que el termino a buscar sea relevante a productos de videojuegos.Las fichas de producto relevantes al termino a buscar deben ya existir en Bing  y estar asociados a la extensión Bing_Products_Video_Games

  4. En la pagina de pivote web, selecciona un producto debajo  de la cabecera products. Esto iniciara la ficha relacionado al producto.
  5. En la ficha del producto, realiza un gesto swipe sobre el pivote apps y realiza un gesto Tap en la aplicación titulada Display URI Parameters. Nota que el titulo lee Products URI Details.

  6. Después de hacer el gesto Tap en la pagina pivote apps, observa que la pagina QuickCardTargetPage.xaml muestra los parámetros del enlace URI del App Connect para el producto

Demo con las fichas rápidas de películas

  1. Presiona F5 para comenzar la depuración del proyecto, ejecutando el emulador con la aplicación en caso que no lo has hecho para la demostración anterior.

  2. Una vez la aplicación cargada, da clic sobre el botón de búsqueda de hardware.

  3. En Bing, ingresa un termino para buscar que este relacionado a películas, que actualmente se encuentran en cartelera. Para probar con fichas de películas , dos cosas son requeridas:Bing debe considerar que el termino a buscar sea relevante a películas que se encuentren en cartelera.Las fichas de películas relevantes al termino a buscar deben ya existir en Bing  y estar asociados a la extensión Bing_Products_Movies

  4. En la pagina de pivote web, selecciona una película debajo de la cabecera web. Esto iniciara la ficha rápida relacionada a la película.

  5. En la ficha rápida de la película, realiza un gesto Swipe sobre el pivote apps y realiza un gesto Tap en la aplicación titulada Display URI Parameters. Nota que el titulo lee Movie URI Details.

  6. Después de hacer el gesto Tap en la pagina pivote apps, observa que la pagina QuickCardTargetPage.xaml muestra los parámetros del enlace URI del App Connect para la película

Demo con las fichas rápidas de lugares

  1. Presiona F5 para comenzar la depuración del proyecto, ejecutando el emulador con la aplicación en caso que no lo has hecho para la demostración anterior.

  2. Una vez la aplicación cargada, da clic sobre el botón de búsqueda de hardware.

  3. En Bing, ingresa un termino para buscar que este relacionado con comida, como «comida» o algún restaurant cerca de donde te encuentras. Para trabajar con fichas de lugares con la extensión Bing_Places_Food_and_Dining, dos cosas son requeridas:Bing debe considerar que el termino a buscar sea relevante a comida y ubicación de locales de comida. Las fichas de lugares relevantes al termino a buscar deben ya existir en Bing  y estar asociados a la extensión Bing_Places_Food_and_Dining

  4. En la pagina de pivote local, selecciona una lugar de comida debajo del mapa que es desplegado. Esto iniciara la ficha rápida relacionada al lugar.

  5. En la ficha rápida del lugar, realiza un gesto Swipe sobre el pivote apps y realiza un gesto Tap en la aplicación titulada Display URI Parameters. Nota que el titulo lee Place URI Details.

  6. Después de hacer el gesto Tap en la pagina pivote apps, observa que la pagina QuickCardTargetPage.xaml muestra los parámetros del enlace URI del App Connect para el lugar

Depurando la aplicación

El proceso de depuración se rompe cuando una aplicación es re-ejecutada desde una ficha rápida. Para depurar una ejecución App Connect a tu aplicación, deberás simular un enlace URI. Para hacer esto, realiza los siguientes pasos para reemplazar de manera temporal el elemento DefaultTask en el archivo WPAppManifest.xml

  1. En el archivo WPAppManifest.xml, comenta de manera temporal el elemento DefaultTask.

  2. Ahora agrega el siguiente elemento DefaultTask al elemento Task.

    <DefaultTask Name="_default" NavigationPage="SearchExtras?MovieName=Test&amp;Category=Bing_Movies" />

    Este elemento DefaultTask simula una ficha de película llamada «Test»

  3. Presiona F5 y depura tu aplicacion instalandola en un Dispositivo o el emulador.

  4. En WPAppManifest.xml, comenta el elemento DefaultTask y descomenta el original. Una vez terminado, el elemento Taskdebería verse como en el siguiente código:

    <Tasks>
      <DefaultTask  Name ="_default" NavigationPage="MainPage.xaml"/>
      <!--<DefaultTask  Name="_default" NavigationPage="SearchExtras?MovieName=Test&amp;Category=Bing_Movies" />-->
    </Tasks>

Fuente:
How to: Extend Search with App Connect for Windows Phone

Diseña un sitio como este con WordPress.com
Comenzar