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

Generation Apps – Día 17 – Integración con Music + Video Hub para Windows Phone

El Hub de música y videos es un punto focal para toda las actividades de música, video y podcast en un Windows Phone. Aplicaciones en el Hub de Música y Videos provee una experiencia  integral de música y video en el teléfono como una función primaria. Estas aplicación se integran con este Hub usando las clases MediaHistory y MediaHistoryItem para asegurar una experiencia de usuario consistente para reproducción de material multimedia.

La arquitectura del Hub de Música + Videos

El Hub de música y videos esta dividido en las siguientes cuatro listas:

  • Zune: es el punto inicial para música, videos, podcasts, radio y el Marketplace. El botón en la parte inferior de la pantalla reproduce tu música de tu colección de manera aleatoria.

  • Historia: contiene música, videos, podcasts, listas de reproducción, artistas y estaciones de radio recientemente reproducidas por el usuario.

  • Nuevo: contiene una lista actualizada de nueva música, podcasts y video sincronizados en el teléfono o descargadas del Marketplace de Zune. Actualiza esta lista cuando tu aplicación reproduzca algún material multimedia.

  • Apps: Contiene la lista de aplicación de Música y videos que fueron instalados en el dispositivo. El proceso de registro y certificación de aplicación detectara que estas llamando los APIs de MediaHistory y MediaHistoryItem, y te dará el HubType a tu aplicación. Esto hará que tu aplicación aparezca en esta lista una vez instalada en el dispositivo.

Requerimientos de certificación de aplicación

Tu aplicación del hub de música y videos debe aprobar los requisitos de certificación de aplicación para Windows Phone para integrarse al Hub de música y videos. El proceso de registro al Marketplace de Windows Phone detecta el uso de APIs de música y videos y te permite el acceso de tu aplicación desde el Hub multimedia.

Mosaicos que son desplegados en el Hub de música y videos deben conformar unas reglas de iconografía adicionales

  1. Debes incluir el titulo o logo de tu aplicación en cada mosaico.

  2. El Mosaico «Now Playing» debe ser 358 pixeles x 358 pixeles. El archivo debe pesar menos de 75KB.

  3. Otros mosaicos deben tener un tamaño de 173 pixeles x 173 pixeles.

  4. La propiedad Title de la clase MediaHistoryItem debe ser asignada como texto que represente el contenido, tales como un nombre de estación, o titulo de video.

Para evitar confundir a los usuarios, Mosaicos de Hubs no deberían contener el arte de álbumes a menos que el álbum comience a sonar al momento que el usuario le de Tap a dicho mosaico. Esto no es un requisito de certificación, pero es una buena practica.

Probando la integración al Hub de Música + Videos

Mientras tu aplicación debe pasar por el proceso de verificación y certificación para entrar al Marketplace de Windows Phone antes de ser integrada al Hub de Música y Videos, existe una forma para saltar esto para propósitos de prueba. En el archivo «WMAppManifest.xml», asegúrate que la propiedad HubType tenga valor 1. El siguiente código muestra un ejemplo al respecto:

<App xmlns="" ProductID=" {00000000-0000-0000-0000-000000000000} 
     Title="WindowsPhoneApplication" RuntimeType="Silverlight" 
     Version="1.0.0.0" Genre="NormalApp" Author="" Description="" 
     Publisher="" HubType="1">

NOTA; En el release actual, no es posible probar la integración de una aplicación con el Hub de Música y Videos en el emulador Windows Phone. Deberás contar con un dispositivo físico para instalarlo.

NOTA: si intentas actualizar los mosaicos en el Hub de música y videos mientras el teléfono se sincroniza con el software Zune, los APIs arrojarán una excepción.

Actualizando el mosaico «Now Playing» en la lista «History»

El siguiente código de ejemplo muestra como actualizar el mosaico «Now Playing» en la lista «Historia» usando las clases de MediaHistory y MediaHistoryItem:

MediaHistoryItem mediaHistoryItem = new MediaHistoryItem();

//<hubTileImageStream> must be a valid ImageStream.
mediaHistoryItem.ImageStream = <hubTileImageStream>; 
mediaHistoryItem.Source = "";
mediaHistoryItem.Title = "NowPlaying";
mediaHistoryItem.PlayerContext.Add("keyString", "Song Name");
MediaHistory.Instance.NowPlaying = mediaHistoryItem;

Actualizando la lista «History»

El siguiente ejemplo de código muestra como actualizar otros mosaicos además de Now Playing en la lista History usando las clases MediaHistory y MediaHistoryItem:

MediaHistoryItem mediaHistoryItem = new MediaHistoryItem();

//<hubTileImageStream> must be a valid ImageStream.
mediaHistoryItem.ImageStream = <hubTileImageStream>; 
mediaHistoryItem.Source = "";
mediaHistoryItem.Title = "RecentPlay";
mediaHistoryItem.PlayerContext.Add("keyString", "Song Name");
MediaHistory.Instance.WriteRecentPlay(mediaHistoryItem);

Actualizando la lista «New»

El siguiente ejemplo de código muestra como actualizar la lista New usando las clases MediaHistory y MediaHistoryItem:

MediaHistoryItem mediaHistoryItem = new MediaHistoryItem();

//<hubTileImageStream> must be a valid ImageStream.
mediaHistoryItem.ImageStream = <hubTileImageStream>; 
mediaHistoryItem.Source = "";
mediaHistoryItem.Title = "MediaHistoryNew";
mediaHistoryItem.PlayerContext.Add("keyString", "Song Name");
MediaHistory.Instance.WriteAcquiredItem(mediaHistoryItem);

Reproduciendo un ítem cuando es ejecutado de las listas «History» y «New»

El siguiente código de ejemplo te muestra como determinar si la aplicación fue ejecutada desde la lista History o New. Esto se logra en el manejador de evento OnNavigatedTo(NavigationEventArgs). La información en el NavigationContext es usado para determinar que tipo de elemento multimedia esta asociado, que en este caso, es una canción de la librería del dispositivo. La canción comienza a sonar en el manejador de evento Loader para el PhoneApplicationPage después que la pagina haya terminado de cargarse.

bool _historyItemLaunch = false;            // Indicates whether the app was launched from a MediaHistoryItem.
const String _playSongKey = "keyString";    // Key for MediaHistoryItem key-value pair.
Song _playingSong = null;                   // The song to play.

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    MediaLibrary library = new MediaLibrary();

    if (NavigationContext.QueryString.ContainsKey(_playSongKey))
    {
        // We were launched from a history item.
        // Change _playingSong even if something was already playing 
        // because the user directly chose a song history item.

        // Use the navigation context to find the song by name.
        String songToPlay = NavigationContext.QueryString[_playSongKey];

        foreach (Song song in library.Songs)
        {
            if (0 == String.Compare(songToPlay, song.Name))
            {
                _playingSong = song;
                break;
            }
        }

        // Set a flag to indicate that we were started from a 
        // history item and that we should immediately start 
        // playing the song after the UI has finished loading.
        _historyItemLaunch = true;
    }
}

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    if (_historyItemLaunch)
    {
        // We were launched from a history item, start playing the song.
        if (_playingSong != null)
        {
            MediaPlayer.Play(_playingSong);
        }
    }
}

Fuente:
How to: Integrate with the Music and Videos Hub for Windows Phone

Generation Apps – Día 16 – Extender la experiencia de Pictures Hub y visor de Imágenes

Con App Connect, tu aplicación puede extenderse a la experiencia del hub de fotos y observador de fotos. Tambien puedes extender la funcionalidad de compartición de fotos y permite a los usuarios para cargar sus imágenes a un servicio web. Esta sección describe como puedes usar App Connect con tu aplicación Windows Phone.

Declarando extensiones

Para usar App Connect, especifica la manera en que quieres extender la experiencia de fotografía declarando una extensión de fotos. Las extensiones están declaradas en el elemento Extensions del archivo manifesto de tu aplicación Windows Phone (WMAppManifest.xml). Cada Extensión de fotografía sigue el mismo formato general. El siguiente ejemplo muestra una extension al hub de fotos.

<Extensions>
      <Extension ExtensionName="Photos_Extra_Hub" ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5632}" TaskID="_default" />
<Extensions>
  • Hub de Fotos: permite a usuarios ejecutar tu aplicación relacionada con fotografía desde la página pivote en el hub de fotos. Para que tu aplicación aparezca en este espacio, deberás declarar la extensión explicada anteriormente, colocando el atributo ExtensionName con el valor Photos_Extra_Hub.

    Una vez hecho esto, e instalado tu aplicación en un dispositivo o el emulador, dirígete al Hub de imágenes, y desplázate hacia la pantalla pivote de «Apps». Nota que la pantalla pivote de Apps no es visible hasta que allá al menos una aplicación agregada. Desde esa pantalla, dale Tap a tu aplicación, y veras que se ejecutara.

  • Picture Viewer: o observador de foto permite a los usuarios ejecutar tu aplicación relacionada con fotografía desde el enlace de aplicaciones en el observador de fotos. Desde el enlace URL, obtienes un token que corresponde a la foto del cual tu aplicación fue ejecutada.

    Para agregar tu aplicación a este espacio, deberás declarar la extensión explicada al inicio de esta lección, especificando el atributo ExtensionName con el valor Photos_Extra_Viewer. Cuando tu aplicación es ejecutada de esta manera, un parámetro con el nombre Token es pasado a tu aplicación vía enlace URL. El parámetro Token, corresponde con la imagen con la cual tu aplicación fue ejecutada. Usando el token, puedes proveer una experiencia agradable al usuario basándote en la imagen con la que ejecuto tu aplicación.

  • Share Picker: permite a los usuarios ejecutar tu aplicación relacionada con fotografía desde el enlace de compartir en el observador de fotos y compartir sus fotos a un servicio web.

    Si deseas extender tu aplicación de esta forma, deberás declarar la declaración explicada al inicio de esta lección, especificando el atributo ExtensionName con el valor Photo_Extra_Share. Cuando tu aplicación es ejecutada de esta manera, un parámetro nombrado FIleId es pasado a tu aplicación vía enlace URL. El valor del parámetro, FileId, corresponde con la imagen en la cual tu aplicación fue ejecutada. Usando este parámetro, puedes proveer una experiencia agradable al usuario para comparar imágenes a través de un servicio web.

Fuente:
Pictures Extensibility Overview for Windows Phone

Generation Apps – Día 15 – Usar Iniciadores y Selectores con tu aplicación

En esta lección del día, te quiero familiarizar con Launchers y Choosers. Los launchers (o iniciadores) y choosers (o selectores) permiten al usuario realizar tareas comunes y proveen un experiencia de usuario consistente. Al mismo tiempo, usando Launchers y Choosers puedes distinguir tus aplicaciones proporcionando mayor funcionalidad.

Ejemplos de Iniciadores

  • Componiendo un correo
  • Compartiendo un enlace en una red social
  • Abriendo un navegador con una pagina en especifico.

Ejemplos de Selectores

  • Seleccionar un contacto existente en tu teléfono
  • Seleccionar una imagen en tu galería de imágenes
  • Capturar una fotografía con la cámara del dispositivo

Si van a la pagina de Launchers y Chooser en MSDN, encontraras muchas tareas que puedes aprovechar para tu aplicación. En este caso, te explicare un chooser y un launcher como ejemplo, y verás que utilizando otras tareas, la historia es bastante similar.

Implementando un Launcher: Invocando la tarea de componer un correo electrónico

  1. Crea un proyecto Silverlight para Windows Phone

  2. En el código C# de MainPage (el archivo MainPage.xaml.cs), agrega la siguiente directiva

    using Microsoft.Phone.Tasks;
  3. Agrega el siguiente código a tu aplicación donde sea que lo necesites, como en un manejador de evento clic de un botón. Para probar este fragmento de código, puedes copiarlo dentro del constructor de la clase. Este es el código que invoca la tarea de enviar componer un correo electrónico.

    EmailComposeTask emailComposeTask = new EmailComposeTask();
    
    emailComposeTask.Subject = "message subject";
    emailComposeTask.Body = "message body";
    emailComposeTask.To = "recipient@example.com";
    emailComposeTask.Cc = "cc@example.com";
    emailComposeTask.Bcc = "bcc@example.com";
    
    emailComposeTask.Show();

Si el usuario no ha iniciado sesión en su correo electrónico, la aplicación mostrara la pantalla para ingresar con una cuenta de correo antes de componer.

Implementando un Chooser: Seleccionando una foto dentro de una galería

  1. Crea un proyecto Silverlight para Windows Phone

  2. En el código C# de MainPage (el archivo MainPage.xaml.cs), agrega la siguiente directiva

    using Microsoft.Phone.Tasks;
  3. Declara el objeto task. Debe estar declarado como atributo de la clase. Agrégalo antes del constructor:

    PhotoChooserTask photoChooserTask;
  4. Agrega el siguiente código al contructor de la pagina. Este código inicializa el objeto task, e identifica el método a ejecutar después que el usuario complete la tarea.

    photoChooserTask = new PhotoChooserTask();
    photoChooserTask.Completed += new EventHandler<PhotoResult>(photoChooserTask_Completed);
  5. Agrega el siguiente código a tu aplicación donde sea que lo necesites, como en un manejador de evento clic de un botón. Para probar este fragmento de código, puedes copiarlo dentro del constructor de la clase. Este es el código que invoca la tarea para seleccionar una imagen en la galería del teléfono.

    try
    {
        photoChooserTask.Show();
    }
    catch (System.InvalidOperationException ex)
    {
        MessageBox.Show("An error occurred.");
    }

    Agrega el código para el manejador de evento completado a tu pagina. Este código se ejecuta después que el usuario completa la tarea (que en este caso, cuando el usuario escoge una imagen). El resultado es un objeto PhotoResult que expone un flujo conteniendo los datos de la imagen seleccionada.

    void photoChooserTask_Completed(object sender, PhotoResult e)
    {
        if (e.TaskResult == TaskResult.OK)
        {
            MessageBox.Show(e.ChosenPhoto.Length.ToString());
    
            //Code to display the photo on the page in an image control named myImage.
            //System.Windows.Media.Imaging.BitmapImage bmp = new System.Windows.Media.Imaging.BitmapImage();
            //bmp.SetSource(e.ChosenPhoto);
            //myImage.Source = bmp;
        }
    }

Espero que la lección de este día te haya sido de tu utilidad. Si estas teniendo problemas en tratar de utilizar un launcher o chooser, deja tu comentario en el articulo para mayor apoyo sobre tu situación.

Fuentes:
Launchers for Windows Phone
How to: Use the Email Composer Task for Windows Phone
Choosers for Windows Phone
How to: Use the Photo Chooser Task for Windows Phone

Generation Apps – Dia 14 – Descarga de datos usando un WebClient

En este día es momento que aprendas como descargar y obtener datos de un cliente web (o WebClient) creando un lector básico de RSS. La guía y ejemplos de códigos en este tópico son basado de un ejemplo llamado “RSS Reader Sample”, el cual puede ser descargado de los proyectos ejemplo para Windows Phone. En el siguiente video te mostrare paso a paso como cumplir esta tarea.

  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.Ahora debemos agregar una Referencia. Da un clic derecho sobre el proyecto, y da clic en Add Reference, y selecciona la pestana Browse.

    Navega hacia el directorio de Archivos de Programas (o ProgramFiles), y dirígete hacia el directorio Microsoft SDKs/Silverlight/v4.0/Libraries/Client/ y selecciona System.ServiceModel.Syndication.dll y da clic a Ok.

  7. Ahora agregaremos una nueva clase. Da clic derecho sobre el proyecto, selecciona Add -> Class.

  8. Lo nombraremos como RssTextTrimmer y le damos clic a Add

  9. Da doble clic sobre el archivo RssTextTrimmer.cs en el Solution Explorer

  10. Agrega la siguientes directivas

    using System.Windows.Data;
    using System.Globalization;
    using System.Text.RegularExpressions;
  11. Actualiza la definición de la clase, a modo que implemente la interfaz IValueConverter

    public class RssTextTrimmer : IValueConverter
  12. Agrega el siguiente código a la clase RssTextTrimmer:

    // Clean up text fields from each SyndicationItem. 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null) return null;
    
        int maxLength = 200;
        int strLength = 0;
        string fixedString = "";
    
        // Remove HTML tags and newline characters from the text, and decode HTML encoded characters. 
        // This is a basic method. Additional code would be needed to more thoroughly  
        // remove certain elements, such as embedded Javascript. 
    
        // Remove HTML tags. 
        fixedString = Regex.Replace(value.ToString(), "<[^>]+>", string.Empty);
    
        // Remove newline characters.
        fixedString = fixedString.Replace("\r", "").Replace("\n", "");
    
        // Remove encoded HTML characters.
        fixedString = HttpUtility.HtmlDecode(fixedString);
    
        strLength = fixedString.ToString().Length;
    
        // Some feed management tools include an image tag in the Description field of an RSS feed, 
        // so even if the Description field (and thus, the Summary property) is not populated, it could still contain HTML. 
        // Due to this, after we strip tags from the string, we should return null if there is nothing left in the resulting string. 
        if (strLength == 0)
        {
            return null;
        }
    
        // Truncate the text if it is too long. 
        else if (strLength >= maxLength)
        {
            fixedString = fixedString.Substring(0, maxLength);
    
            // Unless we take the next step, the string truncation could occur in the middle of a word.
            // Using LastIndexOf we can find the last space character in the string and truncate there. 
            fixedString = fixedString.Substring(0, fixedString.LastIndexOf(" "));
        }
    
        fixedString += "...";
        return fixedString;
    }
    
    // This code sample does not use TwoWay binding, so we do not need to flesh out ConvertBack.  
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
  13. Establece la clase RssTextTrimmer como un converter dándole doble clic a App.xaml en el Solution Explorer, y agregando el siguiente código dentro de la etiqueta <Application.Resources>

    <converter:RssTextTrimmer xmlns:converter="clr-namespace:sdkRSSReaderCS" x:Key="RssTextTrimmer" />
    Donde sdkRssReaderCS es el nombre del namespace de tu proyecto.

Actualizar el código XAML

  1. Da un doble-clic sobre MainPage.xaml en el Solution Explorer

  2. Reemplaza  <Grid x:Name=»ContentPanel» Grid.Row=»1″ Margin=»12,0,12,0″></Grid> con el siguiente código XAML:

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="0,0,12,0">
        <Button Content="Load Feed" Height="72" HorizontalAlignment="Left" Margin="9,6,0,0" Name="loadFeedButton" VerticalAlignment="Top" Width="273" Click="loadFeedButton_Click" />
    
        <ListBox Name="feedListBox" Height="468" HorizontalAlignment="Left" Margin="20,100,0,0" VerticalAlignment="Top" Width="444" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectionChanged="feedListBox_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel VerticalAlignment="Top">
                        <TextBlock TextDecorations="Underline" FontSize="24" Name="feedTitle" TextWrapping="Wrap" Margin="12,0,0,0" HorizontalAlignment="Left" Foreground="{StaticResource PhoneAccentBrush}" Text="{Binding Title.Text, Converter={StaticResource RssTextTrimmer}}" />
                        <TextBlock Name="feedSummary" TextWrapping="Wrap" Margin="12,0,0,0" Text="{Binding Summary.Text, Converter={StaticResource RssTextTrimmer}}" />
                        <TextBlock Name="feedPubDate" Foreground="{StaticResource PhoneSubtleBrush}" Margin="12,0,0,10" Text="{Binding PublishDate.DateTime}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Border BorderBrush="{StaticResource PhoneSubtleBrush}" BorderThickness="1" Height="2" HorizontalAlignment="Left" Margin="20,88,0,0" Name="border1" VerticalAlignment="Top" Width="438" />
    </Grid>

Actualizando el codigo C#

  1. Agrega las siguientes directivas

    using System.IO;
    using System.ServiceModel.Syndication;
    using System.Xml;
    using Microsoft.Phone.Tasks;
  2. Agrega el siguiente código en la clase MainPage, después del constructor:

    // Click handler that runs when the 'Load Feed' or 'Refresh Feed' button is clicked. 
    private void loadFeedButton_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        // WebClient is used instead of HttpWebRequest in this code sample because 
        // the implementation is simpler and easier to use, and we do not need to use 
        // advanced functionality that HttpWebRequest provides, such as the ability to send headers.
        WebClient webClient = new WebClient();
    
        // Subscribe to the DownloadStringCompleted event prior to downloading the RSS feed.
        webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
    
        // Download the RSS feed. DownloadStringAsync was used instead of OpenStreamAsync because we do not need 
        // to leave a stream open, and we will not need to worry about closing the channel. 
        webClient.DownloadStringAsync(new System.Uri("http://windowsteamblog.com/windows_phone/b/windowsphone/rss.aspx"));
    }
    
    // Event handler which runs after the feed is fully downloaded.
    private void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (e.Error != null)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                // Showing the exact error message is useful for debugging. In a finalized application, 
                // output a friendly and applicable string to the user instead. 
                MessageBox.Show(e.Error.Message);
            });
        }
        else
        {
            // Save the feed into the State property in case the application is tombstoned. 
            this.State["feed"] = e.Result;
    
            UpdateFeedList(e.Result);
        }
    }
    
    // This method determines whether the user has navigated to the application after the application was tombstoned.
    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
        // First, check whether the feed is already saved in the page state.
        if (this.State.ContainsKey("feed"))
        {
            // Get the feed again only if the application was tombstoned, which means the ListBox will be empty.
            // This is because the OnNavigatedTo method is also called when navigating between pages in your application.
            // You would want to rebind only if your application was tombstoned and page state has been lost. 
            if (feedListBox.Items.Count == 0)
            {
                UpdateFeedList(State["feed"] as string);
            }
        }
    }
    
    // This method sets up the feed and binds it to our ListBox. 
    private void UpdateFeedList(string feedXML)
    {
        // Load the feed into a SyndicationFeed instance.
        StringReader stringReader = new StringReader(feedXML);
        XmlReader xmlReader = XmlReader.Create(stringReader);
        SyndicationFeed feed = SyndicationFeed.Load(xmlReader);
    
        // In Windows Phone OS 7.1, WebClient events are raised on the same type of thread they were called upon. 
        // For example, if WebClient was run on a background thread, the event would be raised on the background thread. 
        // While WebClient can raise an event on the UI thread if called from the UI thread, a best practice is to always 
        // use the Dispatcher to update the UI. This keeps the UI thread free from heavy processing.
        Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            // Bind the list of SyndicationItems to our ListBox.
            feedListBox.ItemsSource = feed.Items;
    
            loadFeedButton.Content = "Refresh Feed";
        });
    }
    
    // The SelectionChanged handler for the feed items 
    private void feedListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ListBox listBox = sender as ListBox;
    
        if (listBox != null && listBox.SelectedItem != null)
        {
            // Get the SyndicationItem that was tapped.
            SyndicationItem sItem = (SyndicationItem)listBox.SelectedItem;
    
            // Set up the page navigation only if a link actually exists in the feed item.
            if (sItem.Links.Count > 0)
            {
                // Get the associated URI of the feed item.
                Uri uri = sItem.Links.FirstOrDefault().Uri;
    
                // Create a new WebBrowserTask Launcher to navigate to the feed item. 
                // An alternative solution would be to use a WebBrowser control, but WebBrowserTask is simpler to use. 
                WebBrowserTask webBrowserTask = new WebBrowserTask();
                webBrowserTask.Uri = uri;
                webBrowserTask.Show();
            }
        }
    }

Asegúrate que debes tener internet para capturar los datos solicitados.

Fuente:
How to: Create a Basic RSS Reader for Windows Phone

Generation Apps – Día 13 – Usar la Herramienta Explorador de Almacenamiento Aislado

El explorador de almacenamiento aislado (ISETool.exe) es una herramienta de línea de comando que fue instalada en el SDK de Windows Phone. Su utilidad es para listar, copiar y reemplazar archivos y directorios en el almacenamiento aislado. Esto te permite verificar que los archivos se estén guardando en la localidad actual con los datos correctos.

Prerrequisitos

  • Para realizar las siguientes pruebas, debes tener instalado el SDK de Windows Phone

  • Debes tener haber ejecutado el emulador con la aplicación que desees verificar la creación de archivos y directorios.

Listando archivos en el almacenamiento aislado

  1. Realiza el deploy de la aplicación que quieres poner a prueba al emulador o en el dispositivo.

  2. Ejecuta la aplicación y realiza las operaciones de relación de archivos y directorios en la sección de almacenamiento aislado.

  3. Obtén el Product GUID para la aplicación, especificada en el atributo ProductID del elemento App del archivo WPAppManifest.XML

  4. Abre una consola en tu pc, y navega adonde se encuentra ISETool.exe

  5. Para listar los archivos y directorios de la raíz, escribe el siguiente comando usando el Product GUID obtenido del paso anterior.

    ISETool.exe dir <xd|de> <Product GUID>

    El Siguiente ejemplo muestra un comando que lista los archivos y directorios de la raíz por defecto del emulador.

    ISETool.exe dir xd 11111111-2222-3333-4444-555555555555

    En cuanto al siguiente ejemplo muestra como mostrar archivos en la raíz usando el índice

    ISETool.exe dir deviceindex:2 11111111-2222-3333-4444-555555555555

    Si no hay archivos ni directorios, el siguiente texto es mostrado:

    Directory Listing Error. The system cannot find the file specified.
  6. Para el caso que se encuentren varios directorios, utiliza el siguiente comando:

    ISETool.exe dir:device-folder <xd|de> <Product GUID>

    El siguiente ejemplo te listara todo el contenido dentro del directorio «Images»:

    ISETool.exe dir:"Images" xd 11111111-2222-3333-4444-555555555555

Copiando archivos desde almacenamiento aislado

  1. Instala la aplicación que quiere evaluar en un dispositivo o el emulador.

  2. Ejecuta la aplicación, y realiza las operaciones de almacenamiento aislado.

  3. Adquiere el Product GUID para la aplicación especificada en el atributo ProductID del elemento App del archivo de WPAppManifest.xml.

  4. Abre una ventana de consola de comandos y navega hacia el archivo ISETool.exe

    NOTA: Cuando reemplazar los archivos en el emulador, la ventana de comandos debe estar corriendo en el mismo nivel de permisos que el emulador, o arrojara un error.

  5. Para copiar todos los archivos desde almacenamiento aislado hacia tu computadora. Escribe el siguiente comando usando el Product GUID obtenido en los pasos anteriores y especificando un directorio en tu computadora

    ISETool.exe ts <xd|de> <Product GUID> <desktop-path>

    El siguiente ejemplo muestra un comando que copia los archivos de almacenamiento aislado hacia el directorio «C:\Data\My Files» en tu computadora

    ISETool.exe ts xd 11111111-2222-3333-4444-555555555555 "C:\Data\My Files"

    Este comando crea un subdirectorio en tu computadora nombrado IsolatedStore y copia los archivos y directorios del almacenamiento aislado hacia el directorio IsolatedStore.NOTA: Si el directorio IsolatedStore ya existe y copias los archivos de nuevo, todo el contenido del directorio será reemplazado con los archivos y directorios traídos del almacenamiento aislado.

    NOTA: Para aplicaciones usando la plataforma de Windows Phone 7.1, el comando crea subdirectorios «Shared\Transfers» y «Shared\ShellContent». El directorio Transfers contiene datos de transferencia de segundo plano. El directorio ShellContent contiene los datos relacionados al mosaico.

Reemplazando archivos en el almacenamiento aislado

  1. Instala la aplicación que quiere evaluar en un dispositivo o el emulador.

  2. Adquiere el Product GUID para la aplicación especificada en el atributo ProductID del elemento App del archivo de WPAppManifest.xml.

  3. Abre una ventana de consola de comandos y navega hacia el archivo ISETool.exeNOTA: Cuando reemplazar los archivos en el emulador, la ventana de comandos debe estar corriendo en el mismo nivel de permisos que el emulador, o arrojara un error.

  4. Para reemplazar todos los archivos desde tu computadora hacia el almacenamiento aislado. Escribe el siguiente comando usando el Product GUID obtenido en los pasos anteriores y especificando un directorio en tu computadora
    ISETool.exe ts <xd|de> <Product GUID> <desktop-path>

    El siguiente ejemplo muestra un comando que reemplaza los archivos y directorios de almacenamiento aislado con los archivos y directorios de  «C:\Data\My Files\IsolatedStore» en tu computadora.

    ISETool.exe rs xd 11111111-2222-3333-4444-555555555555 "C:\Data\My Files\IsolatedStore"

    Si copiaste archivos usando el comando ts y quieres reemplazar los mismos archivos usando el comando rs , debes especificar el directorio IsolatedStore en <desktop-path>

    Este comando crea un subdirectorio en tu computadora nombrado IsolatedStore y copia los archivos y directorios del almacenamiento aislado hacia el directorio IsolatedStore.

Fuente:
How to: Use the Isolated Storage Explorer Tool

Generation Apps – Día 12 – Almacenar archivos y carpetas con Windows Phone

Ahora veremos cómo realizar las siguientes tareas relacionadas al almacenamiento aislado en tu aplicación:

  • Obtener un almacén virtual para la aplicación

  • Crear un directorio padre

  • Crear y agregar texto en un archivo en el almacén aislado

  • Leer texto de un archivo en el almacén aislado

El objetivo es crear una aplicación de una pagina en la cual ingresas un string en un archivo de texto, y luego leer el contenido del mismo.

Creando el Proyecto Silverlight para Windows Phone

  1. En Expression Blend, crea un nuevo proyecto dando clic en archivo | nuevo Proyecto

  2. En la ventana de nuevo proyecto, expande la plantilla de C#, y selecciona Silverlight para Windows Phone

  3. En la plantilla de Windows Phone Application, llena el campo Nombre de tu preferencia

  4. En la ventana de versión para Windows Phone, selecciona 7.1 y dale Aceptar

  5. Dale clic a OK. El nuevo proyecto es creado, y se desplegara MainPage.xaml en el diseñador.

  6. Agrega los siguientes recursos en el código C# de MainPage para incluirlos en el namespace.

    using System.IO;
    using System.IO.IsolatedStorage;

Desarrollo de la interfaz

  1. En la pagina MainPage.xaml, agrega un control TextBox y un Button en la parte superior de la pagina. Escala y posiciona los controles usando la imagen como referencia.

  2. Renombra el TextBox a txtWrite y renombra el Button a btnWrite.

  3. Para el TextBox, borra el contenido en la propiedad Text, y para el botón, coloca “Write” en su propiedad Text.

  4. Ahora agrega un TextBlock (renombralo a txtRead) y un Button (renombralo a btnRead) en la parte inferior de la pagina.

AP_Core_IsoDesign

Manejador de Eventos

  1. Realiza un doble-clic en el botón btnWrite para agregar un manejador de evento para el evento clic. Se abre el archivo MainPage.xaml.cs

  2. El objetivo es agregar código para obtener el almacén virtual donde tu puedes crear archivos y directorios para tu aplicación. Para resaltar una simple jerarquía de un directorio y un archivo, crearemos un directorio y luego un archivo dentro de ese directorio. Para el manejador de evento btnWrite_Click, agrega el siguiente código:

    private void btnWrite_Click(object sender, RoutedEventArgs e)
    {
        // Obtain the virtual store for the application.
        IsolatedStorageFile myStore = IsolatedStorageFile.GetUserStoreForApplication();
    
        // Create a new folder and call it "MyFolder".
        myStore.CreateDirectory("MyFolder");
    
        // Specify the file path and options.
        using (var isoFileStream = new IsolatedStorageFileStream("MyFolder\\myFile.txt", FileMode.OpenOrCreate, myStore))
        {
            //Write the data
            using (var isoFileWriter = new StreamWriter(isoFileStream))
            {
                isoFileWriter.WriteLine(txtWrite.Text);
            }
        }
    }
  3. Ahora agrega un manejador de evento clic a btnRead, dando doble-clic al botón en el diseñador (MainPage.xaml). En seguida se despliega el código C#.

  4. Ahora en método btnRead_Click, agrega el siguiente código:

    // This code opens and reads the contents of myFile.txt.
    private void btnRead_Click(object sender, RoutedEventArgs e)
    {
        // Obtain a virtual store for the application.
        IsolatedStorageFile myStore = IsolatedStorageFile.GetUserStoreForApplication();
    
        try
        {
            // Specify the file path and options.
            using (var isoFileStream = new IsolatedStorageFileStream("MyFolder\\myFile.txt", FileMode.Open, myStore))
            {
                // Read the data.
                using (var isoFileReader = new StreamReader(isoFileStream))
                {
                    txtRead.Text = isoFileReader.ReadLine();
                }
            }
        }
        catch
        {
            // Handle the case when the user attempts to click the Read button first.
            txtRead.Text = "Need to create directory and the file first.";
        }
    }
  5. Ahora ejecuta la aplicación en modo Debug. Esto abrirá el emulador Windows Phone, ejecutando la aplicación.

Deberías ser capaz de ingresar texto en el control Textbox, oprime el botón Write, luego oprime el botón Read, y hacer que se despliegue el contenido del archivo en el TextBlock. De nuevo, esto muestra el proceso de crear un espacio de almacenamiento aislado para tu aplicación, creando un archivo nuevo, para luego hacerle operaciones de escritura y lectura.

Incluso cuando ejecutas la aplicación en el dispositivo, puedes cerrarla y volverla ejecutar. Al presionar al botón Read mostrara el ultimo string que ingresaste en el archivo.

Fuente:
How to: Store Files and Folder for Windows Phone

Generation Apps – Día 11 – Usar fotos con tu aplicación de Windows Phone

Buenas imágenes captan bastante la atención, y la facilidad de tomar y seleccionar fotos puede contribuir al uso de una aplicación. En el API de Windows Phone, hay ciertas aplicaciones innatas al sistema operativo que podemos invocar como «Tareas».

Tarea de Captura de fotos

Para sacar provecho de esta funcionalidad, te mostrare como implementarlo en un proyecto de ejemplo.

  1. Abre Expression Blend, crea un nuevo proyecto de Silverlight para Windows Phone.

  2. Agrega un botón en el centro de la pantalla y agrégale un evento de tipo «Tap». De esta forma:

  3. Ahora abre el c# asociado a la pagina MainPage, denominado MainPage.xaml.cs, y en las librerías, debajo de la instrucción «using Microsoft.Phone.Controls;», debes agregar la siguiente referencia:

    using Microsoft.Phone.Tasks;
  4. Debes declarar un atributo de tipo CameraCaptureTask antes del método constructor:

    CameraCaptureTask cameraCaptureTask;
  5. Dentro del constructor, después de la instrucción “InitializeComponents();”, debes instanciar y crear un evento que se dispara al momento de tomar una foto, y aceptarla:

    cameraCaptureTask = new CameraCaptureTask();
    cameraCaptureTask.Completed += new EventHandler<PhotoResult>(cameraCaptureTask_Completed);
  6. Ahora en el método del evento que agregaste del Botón, agrega la siguiente instrucción para invocar la aplicación de la cámara:

    try
    {
        cameraCaptureTask.Show();
    }
    catch (System.InvalidOperationException ex)
    {
        MessageBox.Show("An error occurred.");
    }
  7. Ahora agrega el siguiente método, que es el manejador de evento cuando se haya tomado una foto y lo aceptes en lugar de retomar la foto:

    void cameraCaptureTask_Completed(object sender, PhotoResult e)
    {
        if (e.TaskResult == TaskResult.OK)
        {
            MessageBox.Show(e.ChosenPhoto.Length.ToString());
    
            //Codigo para desplegar la foto tomada en una pagina con un controlador image
            //System.Windows.Media.Imaging.BitmapImage bmp = new System.Windows.Media.Imaging.BitmapImage();
            //bmp.SetSource(e.ChosenPhoto);
            //myImage.Source = bmp;
        }
    }
  8. Ahora queda es compilar y ejecutar el programa en el emulador Windows Phone.

Ya ves con unas cuantas líneas de código, puedes aprovechar toda la aplicación de la cámara, donde puedes manipular manualmente la configuración de captura, como el iso, escenario, efectos, etc.

Tarea de Selección de Imágenes

Ahora en este video te enseñare como puedes seleccionar una imagen

  1. Abre Expression Blend, crea un nuevo proyecto de Silverlight para Windows Phone.

  2. Agrega un botón en el centro de la pantalla y agrégale un evento de tipo «Tap». De esta forma:

  3. Ahora abre el c# asociado a la pagina MainPage, denominado MainPage.xaml.cs, y en las librerías, debajo de la instrucción «using Microsoft.Phone.Controls;», debes agregar la siguiente referencia:

    using Microsoft.Phone.Tasks;
  4. Debes declarar un atributo de tipo PhotoChooserTask antes del método constructor:

    PhotoChooserTask photoChooserTask;
  5. Dentro del constructor, después de la instruccion InitializeComponents();, debes instanciar y crear un evento que se dispara al momento de tomar una foto, y aceptarla:

    photoChooserTask = new PhotoChooserTask();
    photoChooserTask.Completed += new EventHandler<PhotoResult>(photoChooserTask_Completed);
  6. Ahora en el método del evento que agregaste del Botón, agrega la siguiente instruccion para invocar la aplicacion de la camara:

    try
    {
        photoChooserTask.Show();
    }
    catch (System.InvalidOperationException ex)
    {
        MessageBox.Show("An error occurred.");
    }
  7. Ahora agrega el siguiente método, que es el manejador de evento cuando se haya tomado una foto y lo aceptes en lugar de retomar la foto:

    void photoChooserTask_Completed(object sender, PhotoResult e)
    {
        if (e.TaskResult == TaskResult.OK)
        {
            MessageBox.Show(e.ChosenPhoto.Length.ToString());
    
            //Code to display the photo on the page in an image control named myImage.
            //System.Windows.Media.Imaging.BitmapImage bmp = new System.Windows.Media.Imaging.BitmapImage();
            //bmp.SetSource(e.ChosenPhoto);
            //myImage.Source = bmp;
        }
    }
  8. Ahora queda es compilar y ejecutar el programa en el emulador Windows Phone.

Puedes ver que las lecciones de este día son particularmente útiles para aquellos trabajando con imágenes en su aplicación, y gracias al API de Windows Phone, puedes ver como nos facilita estas tareas.

Fuente:
How to: Use the Camera Capture Task for Windows Phone

Fuente:
How to: Use the Photo Chooser Task for Windows Phone

Generation Apps – Día 10 – Trabajar con mosaicos en Windows Phone

Cuando vemos la pantalla de inicio de Windows Phone, todo lo que vemos son Tiles, o mosaicos; y en cada mosaico (o la mayoría), nos muestra información de nuestro interés, sin ninguna necesidad de abrir la aplicación, como es el caso del mosaico de correos electrónicos y mensajería, que sin necesidad de entrar a estas aplicaciones, podemos enterarnos si hemos recibido un correo o un mensaje simplemente viendo el mosaico correspondiente.

Ha llegado el momento que aprendas a desarrollar esta súper útil funcionalidad. En este ejemplo vas a implementar un nuevo proyecto con tres paginas que se muestran a continuación: MainPage, ApplicationTile y SecondaryTile

Creando el proyecto y Layout de la Interfaz

  1. Crea un nuevo proyecto Silverlight For Windows Phone y nómbralo TileSample

  2. Utiliza Microsoft Paint o cualquier otro programa de editor de imágenes para crear tres archivos de imágenes de mosaico de 173 x 173 pixeles usados como imágenes de fondo. Nómbralos «Red.jpg», «Blue.jpg» y «Green.jpg». Puedes descargarte el proyecto de ejemplo y utilizas las imágenes que este utiliza. Agrega estas tres imágenes a tu proyecto haciendo clic derecho al proyecto, y selecciona Add -> Existing Item. Selecciona las tres imágenes y da clic en Add. Para cada imagen, selecciona la propiedad Build Action a tipo Content.

  3. Establece el nombre de la aplicación y el titulo de la pagina reemplazando el TitlePanelcon el siguiente código XAML.

    <!--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="Tile Sample" Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock x:Name="PageTitle" Text="Tiles" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>
  4. El código del MainPage se encuentra mas abajo. Contiene un text block y dos botones:

    Cada botón tendrá un manejador de evento clic. Para crear la IU de MainPage, reemplaza <Grid x:Name=»ContentPanel» Grid.Row=»1″ Margin=»12,0,12,0″></Grid> de MainPage.xaml con el siguiente código:

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <TextBlock Height="170" HorizontalAlignment="Left" Margin="12,6,0,0" Name="textBlockDescription" Text="This sample demonstrates how to update Application Tiles.  It also demonstrates how to create, update, and delete secondary Tiles." VerticalAlignment="Top" Width="438" TextWrapping="Wrap" TextAlignment="Left" />
        <Button Content="Change Application Tile" Height="72" HorizontalAlignment="Left" Margin="28,182,0,0" Name="buttonChangeApplicationTile" VerticalAlignment="Top" Width="392" Click="buttonChangeApplicationTile_Click" />
        <Button Content="Change Secondary Tile" Height="72" HorizontalAlignment="Left" Margin="28,278,0,0" Name="buttonChangeSecondaryTile" VerticalAlignment="Top" Width="392" Click="buttonChangeSecondaryTile_Click" />
    </Grid>
  5. El siguiente paso es crear la interfaz de la pagina ApplicationTile.xaml. Para crear la IU, Da clic derecho sobre el proyecto, selecciona Add->New Item. Selecciona Windows Phone Portrait Page. Nómbralo ApplicationTile, da clic en Add.

  6. Establece el nombre de la aplicación y el titulo de la pagina reemplazando el TitlePanelcon el siguiente código XAML.

    <!--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="Tile Sample" Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock x:Name="PageTitle" Text="Application Tile" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" FontSize="64" />
    </StackPanel>
  7. El código del ApplicationTile se encuentra mas abajo. Aquí te muestro los controles que que lo componen:

    El buttonSetApplicationTiletendra su manejador de evento clic. , reemplaza <Grid x:Name=»ContentPanel» Grid.Row=»1″ Margin=»12,0,12,0″></Grid> de MainPage.xaml con el siguiente código:

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <TextBlock Height="75" HorizontalAlignment="Left" Margin="12,6,0,0" Name="textBlockDescription" Text="Sets Application Tile properties as a group. Leave text and image properties blank to clear the property. Set Count to 0 to clear the Count property." VerticalAlignment="Top" Width="438" TextWrapping="Wrap" FontSize="16" />
        <TextBlock Height="30" HorizontalAlignment="Left" Margin="164,96,0,0" Name="textBlockTitle" Text="Title" VerticalAlignment="Top" FontSize="18" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="197,74,0,0" Name="textBoxTitle" Text="" VerticalAlignment="Top" Width="260" />
        <TextBlock Height="30" HorizontalAlignment="Left" Margin="46,171,0,0" Name="textBlockBackgroundImage" Text="Background Image" VerticalAlignment="Top" FontSize="18" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="197,152,0,0" Name="textBoxBackgroundImage" Text="" VerticalAlignment="Top" Width="260" />
        <TextBlock Height="30" HorizontalAlignment="Left" Margin="23,249,0,0" Name="textBlockCount" Text="Count (1-99, 0=Clear)" VerticalAlignment="Top" FontSize="18" Width="175" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="197,230,0,0" Name="textBoxCount" Text="" VerticalAlignment="Top" Width="260" InputScope="Number" />
        <TextBlock Height="30" HorizontalAlignment="Left" Margin="127,359,0,0" Name="textBlockBackTitle" Text="BackTitle" VerticalAlignment="Top" FontSize="18" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="197,338,0,0" Name="textBoxBackTitle" Text="" VerticalAlignment="Top" Width="260" />
        <TextBlock Height="30" HorizontalAlignment="Left" Margin="10,436,0,0" Name="textBlockBackBackgroundImage" Text="BackBackground Image" VerticalAlignment="Top" FontSize="18" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="197,416,0,0" Name="textBoxBackBackgroundImage" Text="" VerticalAlignment="Top" Width="260" />
        <TextBlock Height="30" HorizontalAlignment="Left" Margin="97,515,0,0" Name="textBlockContent" Text="BackContent" VerticalAlignment="Top" FontSize="18" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="197,494,0,0" Name="textBoxBackContent" Text="" VerticalAlignment="Top" Width="260" />
        <Button Content="Set Application Tile Properties" Height="72" HorizontalAlignment="Left" Margin="23,551,23,0" Name="buttonSetApplicationTile" VerticalAlignment="Top" Width="410" FontSize="20" FontFamily="Calibri" Click="buttonSetApplicationTile_Click" />
    </Grid>
  8. El ultimo paso es crear la IU para la pagina SecondaryTile.xaml. Para crear la IU, Da clic derecho sobre el proyecto, selecciona Add->New Item. Selecciona Windows Phone Portrait Page. Nómbralo ApplicationTile, da clic en Add.

  9. Establece el nombre de la aplicación y el titulo de la pagina reemplazando el TitlePanelcon el siguiente código XAML.

    <!--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="Tile Sample" Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock x:Name="PageTitle" Text="Secondary Tile" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" FontSize="64" />
    </StackPanel>
  10. El código del ApplicationTile se encuentra mas abajo. Aquí te muestro los controles que que lo componen:

    El control checkBoxDisplaySecondaryTile contara con los manejadores de eventos checkboxDisplaySecondaryTile_Checked y checkboxDisplaySecondaryTile_Unchecked. Cada botón manejara un evento clic. Reemplaza <Grid x:Name=»ContentPanel» Grid.Row=»1″ Margin=»12,0,12,0″></Grid> de MainPage.xaml con el siguiente código:

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <CheckBox Content="Display Secondary Tile " Height="72" HorizontalAlignment="Left" Margin="20,4,0,0" Name="checkBoxDisplaySecondaryTile" VerticalAlignment="Top" Checked="checkBoxDisplaySecondaryTile_Checked" Unchecked="checkBoxDisplaySecondaryTile_Unchecked" />
        <TextBlock Height="33" HorizontalAlignment="Center" Margin="9,91,0,0" Name="textBlockDescription" Text="Sets properties of the secondary Tile individually." VerticalAlignment="Top" Width="441" TextAlignment="Center" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="9,127,0,0" Name="textBoxTitle" Text="" VerticalAlignment="Top" Width="260" />
        <Button Content="Set Title" Height="72" HorizontalAlignment="Left" Margin="259,127,0,0" Name="buttonSetTitle" VerticalAlignment="Top" Width="200" Click="buttonSetTitle_Click" FontSize="16" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="9,205,0,0" Name="textBoxBackgroundImage" Text="" VerticalAlignment="Top" Width="260" />
        <Button Content="Set Background" Height="72" HorizontalAlignment="Left" Margin="259,205,0,0" Name="buttonSetBackgroundImage" VerticalAlignment="Top" Width="200" FontSize="16" Click="buttonSetBackgroundImage_Click" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="9,283,0,0" Name="textBoxCount" Text="" VerticalAlignment="Top" Width="260"  InputScope="Number"/>
        <Button Content="Set Count(1-99,0=Clear)" Height="72" HorizontalAlignment="Left" Margin="259,283,0,0" Name="buttonSetCount" VerticalAlignment="Top" Width="200" Click="buttonSetCount_Click" FontSize="14" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="9,391,0,0" Name="textBoxBackTitle" Text="" VerticalAlignment="Top" Width="260" />
        <Button Content="Set BackTitle" Height="72" HorizontalAlignment="Left" Margin="259,391,0,0" Name="buttonSetBackTitle" VerticalAlignment="Top" Width="200" Click="buttonSetBackTitle_Click" FontSize="16" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="9,469,0,0" Name="textBoxBackBackgroundImage" Text="" VerticalAlignment="Top" Width="260" />
        <Button Content="Set BackBackground" Height="72" HorizontalAlignment="Left" Margin="259,469,0,0" Name="buttonSetBackBackgroundImage" VerticalAlignment="Top" Width="200" FontSize="16" Click="buttonSetBackBackgroundImage_Click" />
        <TextBox Height="72" HorizontalAlignment="Left" Margin="9,547,0,0" Name="textBoxBackContent" Text="" VerticalAlignment="Top" Width="260" />
        <Button Content="Set BackContent" Height="72" HorizontalAlignment="Left" Margin="259,547,0,0" Name="buttonSetBackContent" VerticalAlignment="Top" Width="200" FontSize="16" Click="buttonSetBackContent_Click" />
    </Grid>

Implementando MainPage

Ahora entrando en materia de la MainPage.xaml.cs, debemos agregar el manejador de eventos de los botones buttonChangeApplicationTile y buttonChangeSecondaryTile. Dentro de la clase MainPage, agrega el siguiente código:

private void buttonChangeApplicationTile_Click(object sender, RoutedEventArgs e)
{
    this.NavigationService.Navigate(new Uri("/ApplicationTile.xaml", UriKind.Relative));
}

private void buttonChangeSecondaryTile_Click(object sender, RoutedEventArgs e)
{
    this.NavigationService.Navigate(new Uri("/SecondaryTile.xaml?DefaultTitle=FromMain", UriKind.Relative));
}

Implementando la pagina ApplicationTile

  1. Para implementar los manejadores de eventos en esta pagina, como trabajaremos con recursos de mosaicos, agrega la siguiente directiva en el tope de la clase ApplicationTile:
    using Microsoft.Phone.Shell;
  2. Ahora agrega el siguiente código dentro de la clase. Este implementara el manejador del evento buttonSetApplicationTile_Click. Tomara los valores ingresados en los text box y establecer todas las propiedades en el mosaico de una vez:

    // Set all the properties of the Application Tile.
    private void buttonSetApplicationTile_Click(object sender, RoutedEventArgs e)
    {
        int newCount = 0;
    
        // Application Tile is always the first Tile, even if it is not pinned to Start.
        ShellTile TileToFind = ShellTile.ActiveTiles.First();
    
        // Application should always be found
        if (TileToFind != null)
        {
            // if Count was not entered, then assume a value of 0
            if (textBoxCount.Text == "")
            {
                // A value of '0' means do not display the Count.
                newCount = 0;
            }
            // otherwise, get the numerical value for Count
            else
            {
                newCount = int.Parse(textBoxCount.Text);
            }
    
            // Set the properties to update for the Application Tile.
            // Empty strings for the text values and URIs will result in the property being cleared.
            StandardTileData NewTileData = new StandardTileData
            {
                Title = textBoxTitle.Text,
                BackgroundImage = new Uri(textBoxBackgroundImage.Text, UriKind.Relative),
                    Count = newCount,
                    BackTitle = textBoxBackTitle.Text,
                    BackBackgroundImage = new Uri(textBoxBackBackgroundImage.Text, UriKind.Relative),
                    BackContent = textBoxBackContent.Text
            };
    
            // Update the Application Tile
            TileToFind.Update(NewTileData);
        }
    }

Implementando la pagina SecondaryTile

  1. Para implementar los manejadores de eventos en esta pagina, como trabajaremos con recursos de mosaicos, agrega la siguiente directiva en el tope de la clase ApplicationTile:

    using Microsoft.Phone.Shell;
  2. En el manejador de evento OnNavigatedTo, debemos chequear si ya hay mosaicos secundarios existentes y asignar el checkbox correspondiente. El valor por defecto para titulo es establecido aquí también, dependiendo de la asignación de DefaultTitle en el QueryString.

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
    
        ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("DefaultTitle=FromTile"));
        checkBoxDisplaySecondaryTile.IsChecked = (TileToFind != null);
        textBoxTitle.Text = this.NavigationContext.QueryString["DefaultTitle"];
    }
  3. Agrega el siguiente código que implementa el manejador de evento checkBoxDisplaySecondaryTile_Checked. Este código chequea si un tile con un ID designado previamente ya existe. Si no existe, el código crea un objeto StandardTileData y lo usa para establecer las propiedades de la cara frontal y trasera del mosaico.

    private void checkBoxDisplaySecondaryTile_Checked(object sender, RoutedEventArgs e)
    {
        ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("DefaultTitle=FromTile"));
    
        if (TileToFind == null)
        {
            StandardTileData NewTileData = new StandardTileData
            {
                BackgroundImage = new Uri("Red.jpg", UriKind.Relative),
                Title = "Secondary Tile",
                Count = 12,
                BackTitle = "Back of Tile",
                BackContent = "Welcome to the back of the Tile",
                BackBackgroundImage = new Uri("Blue.jpg", UriKind.Relative)
            };
    
            ShellTile.Create(new Uri("/SecondaryTile.xaml?DefaultTitle=FromTile", UriKind.Relative), NewTileData);
        }
    }
  4. Agrega el siguiente código que implementa el manejador de evento  checkBoxDisplaySecondaryTile_Unchecked. Este código busca un mosaico con el ID que le dimos cuando fue creado, y lo remueve de la pantalla de inicio:

    private void checkBoxDisplaySecondaryTile_Unchecked(object sender, RoutedEventArgs e)
    {
        ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("DefaultTitle=FromTile"));
    
        if (TileToFind != null)
        {
            TileToFind.Delete();
        }
    }
  5. Agregamos cada manejador de evento clic a los botones. Cada manejador de evento busca por el mosaico a actualizar, toma el nuevo valor tomado del text box correspondiente, y actualiza el mosaico con el nuevo valor.

    // Handle the Title button clicked event by setting the front of Tile title.
    private void buttonSetTitle_Click(object sender, RoutedEventArgs e)
    {
        // Find the Tile we want to update.
        ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("DefaultTitle=FromTile"));
    
        // If the Tile was found, then update the Title.
        if (TileToFind != null)
        {
            StandardTileData NewTileData = new StandardTileData
            {
                Title = textBoxTitle.Text
            };
    
                    TileToFind.Update(NewTileData);
        }
    }
    
    // Handle the Background Image button clicked event by setting the front of Tilebackground image.
    private void buttonSetBackgroundImage_Click(object sender, RoutedEventArgs e)
    {
        // Find the Tile we want to update.
        ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("DefaultTitle=FromTile"));
    
        // If the Tile was found, then update the background image.
        if (TileToFind != null)
        {
            StandardTileData NewTileData = new StandardTileData
            {
                BackgroundImage = new Uri(textBoxBackgroundImage.Text, UriKind.Relative)
            };
    
                    TileToFind.Update(NewTileData);
        }
    }
    
    // Handle the Count button clicked event by setting the front of Tile count value.
    private void buttonSetCount_Click(object sender, RoutedEventArgs e)
    {
        int newCount = 0;
    
        // Find the Tile we want to update.
        ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("DefaultTitle=FromTile"));
    
        // If the Tile was found, then update the count.
        if (TileToFind != null)
        {
            // if Count was not entered, then assume a value of 0.
            if (textBoxCount.Text == "")
            {
                newCount = 0;
            }
            // Otherwise, get the numerical value for Count.
            else
            {
                newCount = int.Parse(textBoxCount.Text);
            }
    
            StandardTileData NewTileData = new StandardTileData
            {
                Count = newCount
            };
    
            TileToFind.Update(NewTileData);
        }
    }
    
    // Handle the Back Title button clicked event by setting the back of Tile title.
    private void buttonSetBackTitle_Click(object sender, RoutedEventArgs e)
    {
        // Find the Tile we want to update.
        ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("DefaultTitle=FromTile"));
    
        // If the Tile was found, then update the title on the back of the Tile.
        if (TileToFind != null)
        {
            StandardTileData NewTileData = new StandardTileData
                    {
                        BackTitle = textBoxBackTitle.Text
                    };
    
            TileToFind.Update(NewTileData);
        }
    }
    
    // Handle the Back Background Image button clicked event by setting the back of Tile background image.
    private void buttonSetBackBackgroundImage_Click(object sender, RoutedEventArgs e)
    {
        // Find the Tile we want to update.
        ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("DefaultTitle=FromTile"));
    
        // If the Tile was found, then update the background image on the back of the Tile.
        if (TileToFind != null)
        {
            StandardTileData NewTileData = new StandardTileData
            {
                BackBackgroundImage = new Uri(textBoxBackBackgroundImage.Text, UriKind.Relative)
            };
    
            TileToFind.Update(NewTileData);
        }
    }
    
    // Handle the Back Content button clicked event by setting the back of Tile content.
    private void buttonSetBackContent_Click(object sender, RoutedEventArgs e)
    {
        // Find the Tile we want to update.
        ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("DefaultTitle=FromTile"));
    
        // If the Tile was found, then update the content on the back of the Tile.
        if (TileToFind != null)
        {
            StandardTileData NewTileData = new StandardTileData
                {
                    BackContent = textBoxBackContent.Text
                };
    
            TileToFind.Update(NewTileData);
        }
    }

Finalmente, has completado el ejemplo para crear, eliminar y actualizar mosaicos secundarios.

  1. Presiona F5 para comenzar a depurar el proyecto. Se ejecutara la aplicación en el emulador.

  2. Navega hacia la pagina Change Application Tile. Establece algunas propiedades para el mosaico de la aplicación como se muestra en el siguiente screenshot y da clic sobre el botón Set Application Tile Properties.

  3. Para ver los resultados de las propiedades que has establecido, navega hacia la pagina de inicio dando un clic sobre el botón Start en el emulador del teléfono. Navega hacia la lista de las aplicaciones instaladas y busca TileSample. Realiza un Tap y Hold (Clic dejándolo presionado unos segundos) sobre TileSample y luego selecciona el comando Pin to start. El emulador te navegara hacia la pagina de inicio mostrándote el mosaico agregado en la pagina de inicio con las propiedades que estableciste en la aplicación.

  4. Presiona el botón Back en el emulador para regresar a la aplicación. Presionando Back, tu sesión de depuración no ha finalizado.

  5. Presiona el botón Back de nuevo para navegar hacia la pagina principal de la aplicación.

  6. Navega hacia la pagina SecondaryTile y da clic sobre un check box para crear un mosaico secundario. El teléfono te llevara a la pagina de inicio, mostrando el nuevo mosaico agregado.

  7. Presiona el botón Back en el emulador para regresar a la aplicación.

  8. Ingresa un nuevo valor para la propiedad Tile y da clic sobre el botón Set Title.

  9. Navega hacia la pagina de inicio dando un clic sobre el botón Start en el emulador del teléfono. Veras la nueva propiedad Title desplegada en el mosaico.

  10. Ahora presiona el botón Backe intenta cambiar otras propiedades.

  11. Desde la pagina de Inicio, intenta dar clic sobre un mosaico secundario, y veras que la aplicación TileSample se ejecutara y navegara directamente sobre la pagina SecondaryTile. El valor por defecto de Title es FromTile.

Como puedes darte cuenta, debes tomar muy en serio el diseño grafico del mosaico de tu aplicación, a fin de respetar el espacio del nombre del mismo en la esquina izquierda inferior, y la zona del contador indicador de algún evento. Utiliza un fondo de un color solido y evita los gradientes, en el caso que quieras regirte ante los principios de la interfaz Metro, y así aumentas las posibilidades que el usuario lo encuentre estéticamente agradable para anclarlo a su pantalla de inicio.

Fuente:
How to: Create, Delete, and Update Tiles for Windows Phone

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