Etiqueta: Visual Studio

Posts relacionados con el entorno de desarrollo (IDE) de Visual Studio

  • Saber cuando la aplicación está en modo Snapped y actuar en consecuencia

    Pues eso… que en las aplicaciones de Windows 8 (Windows Store) la forma de verla puede ser de tres formas distintas: que ocupe toda la pantalla (el modo predeterminado), que ocupe una parte más pequeña en uno de los laterales (modo snapped o acoplada) o que ocupe la parte grande (filled o vista de relleno) cuando otra aplicación esté en modo snapped.

    Para aclarar estos conceptos, te muestro en la figura 1 cómo se vería una aplicación a vista completa o en modo acoplado (esta imagen está sacada de: Directrices para vistas acopladas y rellenas (aplicaciones de la Tienda Windows).

     

    snappd_01
    Figura 1. Una aplicación que va de pantalla completa a vista acoplada

     

    Cuando la aplicación está en modo "relleno" el tamaño es aceptablemente grande como para que tengamos que preocuparnos demasiado, pero cuando está en modo acoplado (snapped) ahí si que hay que tener en cuenta que el contenido puede que no sea el adecuado.

    Por ejemplo, nuestra aplicación de reloj se vería así:

    snappd_02
    Figura 2. La aplicación de reloj acoplada a la izquierda

     

    Por tanto, para que el texto se adapte cuando esté en modo acoplado (snapped) vamos a escribir unas pocas líneas en las que simplemente cambiaremos el tamaño de la fuente de los dos textos mostrados, de esa forma, al acoplar la aplicación se verá más o menos como vemos en la figura 3.

     

    snappd_03
    Figura 3. Al acoplar la ventana de nuestra aplicación el texto se adapta

     

    Nota:
    El cambio de color (y aparentemente de tamaño) del botón Stop es porque la figura 2 está capturada del proyecto de C# y la captura de la figura 3 es del proyecto de Visual Basic en el que he cambiado el color predeterminado de los iconos y demás con idea de que no sean grises, si no de ese color azul.

     

    Veamos el código, el cual está en MainPage_SizeChanged y lo único que haremos es "empequeñecer" el tamaño de las fuentes cuando esté en modo Snapped y volver a poner el tamaño inicial al mostrarse en cualquiera de las otras dos formas.

    VB:

    ' En el evento SizeChanged de la página principal comprobamos
    ' si estamos en modo de vista Snapped y adaptamos el tamaño de las fuentes
    Private Sub MainPage_SizeChanged(sender As Object, e As SizeChangedEventArgs) _
    Handles Me.SizeChanged If ApplicationView.Value = ApplicationViewState.Snapped Then txtHora.FontSize = 36 txtFecha.FontSize = 18 Else txtHora.FontSize = 72 txtFecha.FontSize = 36 End If End Sub

     

    C#:

    // En el evento SizeChanged de la página principal comprobamos
    // si estamos en modo de vista Snapped y adaptamos el tamaño de las fuentes
    private void MainPage_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (ApplicationView.Value == ApplicationViewState.Snapped)
        {
            txtHora.FontSize = 36;
            txtFecha.FontSize = 18;
        }
        else
        {
            txtHora.FontSize = 72;
            txtFecha.FontSize = 36;
        }
    }
    

     

    En el caso de VB no tienes que hacer nada especial con ese código, simplemente lo pegas en MainPage.vb y ya está, pero en C# debes agregar el evento a ese método, eso lo tendrás que hacer (por ejemplo) en el código de diseño de MainPage.xaml justo antes del cierre de la definición de la página (elemento Page):

    mc:Ignorable="d" SizeChanged="MainPage_SizeChanged">
    
    

     

    Lo que hacemos en ese método de evento es bien simple, comprobamos si el valor de la propiedad Value de la clase ApplicationView (que está definida en el espacio de nombres Windows.UI.ViewManagement) tiene un valor Snapped, de ser así reducimos el tamaño de las fuentes y en caso contrario asignamos los valores "normales".

    En la página (de MSDN) de la enumeración ApplicationViewState puedes ver los cuatro valores soportados por esa enumeración y la descripción de cada uno de ellos.

     

    Y esto es todo por hoy… dentro de poco más… ¿el qué? ya veremos… porque en teoría la aplicación ya está como en un principio yo quería que estuviera, así que… lo mismo te pongo un resumen completo y el código fuente… ya veremos.

    En cualquier caso, espero que te sea de utilidad todo esto que te he contado, tanto en esta ocasión como en las anteriores (abajo tienes todos los enlaces).

     

    Nos vemos.

    Guillermo

     

    Enlaces a los otros artículos de este "paso a paso":

    1. Usar un temporizador en las aplicaciones de Windows store (Visual Basic)
    2. Usar un temporizador en las aplicaciones de Windows store (C#)
    3. Pon una AppBar en tu aplicación de Windows Store
    4. Acceder a los recursos de una aplicación de Windows Store desde código (vb, c#, xaml)
    5. Actualizar el icono (tile) de una aplicación de Windows Store (cada minuto)
    6. Saber cuando la aplicación está en modo Snapped y actuar en consecuencia

  • Actualizar el icono (tile) de una aplicación de Windows Store

    Pues eso… de lo que hoy te voy a hablar es de cómo actualizar el icono de nuestra aplicación para Windows Store. Y cuando digo actualizar me refiero a mostrar algún mensaje o alguna imagen o ambas dos cosas en el icono que está en la página de inicio de Windows 8. En concreto se trata de mostrar la hora actual y actualizarla cada minuto, por tanto el título podría haber sido: Actualizar el icono (tile) de una aplicación de Windows Store cada minuto.

     

    Para que entres en situación:

    En la pantalla de inicio de Windows hay algunos iconos (o tiles) que se actualizan, por ejemplo, el correo, el tiempo, los contactos, etc. Ver la figura 1.
    Esas actualizaciones pueden venir de la nube de un sitio web o de forma local (porque tú quieras actualizar el icono).

     
    Foto 1. Los iconos (tiles) de Windows 8 permiten mostrar notificaciones

     

    De forma predeterminada las notificaciones se pueden mostrar en el icono (o tile) de la aplicación, ya sea en modo alargado o cuadrado: el icono puede ser cuadrado (smaller) o alargado (larger), pero también se puede indicar que no haya "animaciones" en el icono (nosotros como usuarios podemos decidirlo). Para ello sólo hay que pulsar con el botón secundario del ratón en el icono y del menú, u opciones mostradas en la parte inferior, podemos indicar si no queremos esa animación (Turn live tile on/off) tal como vemos en la figura 2.

     

    notificar_tile_02
    Figura 2. El usuario puede activar/desactivar la notificación

     

    Nota:
    Sigue este enlace si quieres saber cómo capturar la pantalla en Windows 8 (tanto de la pantalla de inicio como del escritorio)

     

    ¿Cómo podemos hacer esas notificaciones?

    Sigue leyendo y sabrás cómo hacer algunas de esas notificaciones, aunque aquí te voy a explicar sólo la de notificar con texto, tanto para la imagen grande como la pequeña, si quieres saber cómo usar más tipos de notificaciones tendrás que leerte (entre otras cosas) la ayuda de la enumeración TileTemplateType o mirar en las páginas del centro de desarrollo de Microsoft para saber cómo desarrollar aplicaciones para la tienda de Windows.
    En mi caso particular, para este artículo me he basado en el inicio rápido (quickstart) de: enviar una actualización de icono (aplicaciones de la Tienda Windows con C#/VB/C++ y XAML) (Windows), aunque también estuve probando antes con la biblioteca NotificationsExtensions con idea de hacerlo más fácil y sin tanto manejo de código XML, pero no me funcionó… o lo mismo no la utilicé correctamente. Incluso estuve tentado de usar un temporizador en background, particularmente usando el código de Dave Smits: Tile Update every minute, que seguramente usaré en esta misma aplicación ya que según parece el temporizador se para cuando Windows pone en modo suspensión la aplicación (aún no lo he comprobado, pero me da la impresión de que ocurre algo de eso… ya te diré).

     

    Vamos a lo que vamos

    Lo  primero que he hecho es agregar dos importaciones de espacios de nombres al código de MainPage, el primero para usar las notificaciones y el segundo para usar el Xml.Dom:

    Imports Windows.UI.Notifications
    Imports Windows.Data.Xml.Dom
    
    using Windows.UI.Notifications;
    using Windows.Data.Xml.Dom;

    También necesito crear otro temporizador, ya que el que teníamos en nuestra aplicación (ver los enlaces al final de la página) se puede detener si se pulsa en cualquiera de los botones "stop" de la aplicación.

    Este segundo temporizador estará funcionando y haciendo comprobaciones cada 2 segundos (más o menos) e internamente comprobará si ha cambiado de minuto y en ese caso llamará al método asignarTile() para actualizar los textos del icono. Esos textos será la hora y minutos y además la fecha actual, si se muestra en el icono grande se mostrará en formato largo y si se muestra en el icono pequeño, la fecha estará reducida (porque si no, no se ve, así que).

    Dicho esto, al principio de la clase definimos el temporizador y un par de variables para indicar el intervalo y llevar la cuenta del último minuto mostrado.

    Visual Basic:

    Private timerNotif As DispatcherTimer
    
    ' Intervalo para realizar las notificaciones
    Private intervaloNotif As Integer = intervalo * 2
    ' para guardar el minuto en que se notificó
    Private minutoNotif As Integer = -1
    

     

    C#

    private DispatcherTimer timerNotif;
    
    // Intervalo para realizar las notificaciones
    // (en C# no se puede usar el valor de intervalo en la declaración)
    private int intervaloNotif = 900 * 2;
    // para guardar el minuto en que se notificó
    private int minutoNotif = -1;
    
    

     

    Si te preguntas porqué no he usado un intervalo de un minuto (o algo menos), decirte que el problema es que ese minuto se cuenta desde que activamos el temporizador, por tanto si esa activación del temporizador ocurre, digamos, en el segundo 40, hasta que no llegue el segundo cuarenta del próximo minuto no se actualizaría el contenido, así que… yo he usado 1800 milisegundos, es decir, dos veces el intervalo usado en el otro temporizador, llámame romántico si quieres, jeje.

    Fíjate también que en el código de C# he usado el valor 900 directamente en lugar del campo intervalo para asignar el valor de intervaloNotif, esto es así porque el compilador me decía que no se puede usar un campo (field) intervalo porque no es estático… o algo así…

    También lo podría haber declarado intervalo con el modificador "const" y así sí que podría usarlo como valor a asignar a intervaloNotif, hazlo como tu veas mejor.

     

    En el constructor tenemos que hacer las definiciones y demás cosillas para crear el nuevo temporizador. Yo lo he puesto después del código que ya teníamos (justo antes de acabar el método).

    Visual Basic:

    ' Crear el temporizador para las notificaciones
    timerNotif = New DispatcherTimer()
    AddHandler timerNotif.Tick, AddressOf timerNotif_Tick
    timerNotif.Interval = New TimeSpan(0, 0, 0, 0, intervaloNotif)
    

     

    C#:

    // Crear el temporizador para las notificaciones
    timerNotif = new DispatcherTimer();
    timerNotif.Tick += timerNotif_Tick;
    timerNotif.Interval = new TimeSpan(0, 0, 0, 0, intervaloNotif);
    

     

    Ahora veamos el código del método de evento para este nuevo temporizador.

    Aquí lo que hacemos es detener el temporizador, comprobar si tenemos que actualizar el icono (tile) y volver a poner en marcha el temporizador.

    Para lo del minuto de lapso, lo único que hago es comprobar si el valor de minutoNotif es distinto del minuto actual y en ese caso mostrar el mensaje actualizado. Te digo esto por si quieres hacer algo cada x segundos, en se caso, tendrías que utilizar alguna que otra comparación más "sofisticada".

    VB:

    Private Sub timerNotif_Tick(sender As Object, e As Object)
        timerNotif.Stop()
    
        ' actualizar el tile cada minuto
        If minutoNotif <> DateTime.Now.Minute Then
            minutoNotif = DateTime.Now.Minute
            asignarTile()
        End If
    
        timerNotif.Start()
    End Sub
    

     

    C#:

    private void timerNotif_Tick(object sender, object e)
    {
        timerNotif.Stop();
    
        // actualizar el tile cada minuto
        if (minutoNotif != DateTime.Now.Minute)
        {
            minutoNotif = DateTime.Now.Minute;
            asignarTile();
        }
    
        timerNotif.Start();
    }
    

     

    Como podrás imaginar el trabajo de notificación se realiza en el método asignarTile, pero antes de ver el código de ese método, veamos qué más tenemos que hacer… por ejemplo habilitar el tema de las notificaciones mediante colas, esto en realidad no haría falta, pero he comprobado que si no se usa esa llamada al método EnableNotificationQueue del método compartido CreateTileUpdaterForApplication de la clase TileUpdaterManager y tampoco se asigna un valor a la propiedad Tag del objeto TileNotification usado para hacer las notificaciones esas notificaciones no las hace como uno espera… al menos a mí me ha pasado que me mostraba el texto que inicialmente se usó al iniciar la aplicación y después el que se iba actualizando… así que…

    Dicho esto, agrega el siguiente código al principio del método OnNavigatedTo:

     

    Nota del 8 de diciembre:

    La llamada al método EnableNotificacionQueue(True) lo que en realidad hace es permitir que exista una cola de notificaciones (con un máximo de 5) y esas notificaciones se van alternando de forma automática. Por tanto, para la intención de nuestra aplicación no es precisamente lo más recomendable. Así que, comenta esa línea de código.

     

    VB:

    ' Permitir las notificaciones
    TileUpdateManager.CreateTileUpdaterForApplication().EnableNotificationQueue(True)
    
    minutoNotif = DateTime.Now.Minute
    timerNotif.Start()
    asignarTile()
    

     

    C#:

    // Permitir las notificaciones
    TileUpdateManager.CreateTileUpdaterForApplication().EnableNotificationQueue(true);
    
    minutoNotif = DateTime.Now.Minute;
    timerNotif.Start();
    asignarTile();
    

     

    Y ya sólo nos queda el código del método asignarTile, que (tal como su nombre indica) es el que se encarga de asignar el texto (o lo que indiquemos) a mostrar en el icono de la aplicación.

    Como ya te comenté, el icono (tile) puede tener dos tamaños, uno cuadrado y más pequeño (la imagen a usar en el icono es de 150 x150 píxeles) y otro alargado (la imagen es de 310 x 150), en este ejemplo utilizaré la notificación en esos dos tamaños, pero sólo con texto.

    Como veremos, cuando definimos la notificación para el icono ancho hay que definir también la del icono pequeño… aquí veremos cómo hacerlo.

    Y si lo tuyo son las imágenes en vez de los textos, también veremos cómo asignar imágenes, concretamente las de los iconos pequeños y grandes que están definidos en la aplicación (en la carpeta Assets), ya que haremos que se restauren las imágenes predeterminadas, si no hacemos esto, comprobarás que se queda el último texto que asignes… y en nuestro caso (un reloj que se actualiza cada minuto) no queda muy bien que digamos…

    Y ya sin más dilación (o rollo por mi parte) veamos el código del método asignarTile.

    VB:

    ''' <summary>
    ''' Asignar el texto a mostrar en el tile (icono) de la aplicación.
    ''' En el parámetro se indicará el texto a mostrar (la hora actual)
    ''' </summary>
    Private Sub asignarTile()
        Dim hora As String = DateTime.Now.ToString("HH:mm")
    
        ' La plantilla para el tamaño ancho: TileWideText01
        ' Esta plantilla soporta una cabecera y cuatro líneas de texto
        Dim tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideText01)
    
        Dim tileTextAttributes = tileXml.GetElementsByTagName("text")
        tileTextAttributes(0).InnerText = hora
        tileTextAttributes(1).InnerText = DateTime.Now.ToString("dddd, dd MMMM yyyy")
    
        ' La plantilla para el tamaño pequeño: TileSquareText01
        ' Esta plantilla es como la ancha
        Dim squareTileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquareText01)
        Dim squareTileTextAttributes = squareTileXml.GetElementsByTagName("text")
        squareTileTextAttributes(0).InnerText = hora
        squareTileTextAttributes(1).InnerText = DateTime.Now.ToString("dd/MMM/yyyy")
    
        ' La plantilla del icono pequeño la agregamos a la del icono ancho
        ' y así el propio Windows mostrará el texto que corresponda según el tamaño del icono
        Dim node = tileXml.ImportNode(squareTileXml.GetElementsByTagName("binding").Item(0), True)
        tileXml.GetElementsByTagName("visual").Item(0).AppendChild(node)
    
        Dim tileNotification = New TileNotification(tileXml)
    
        tileNotification.Tag = "ClockW8_eGi"
        TileUpdateManager.CreateTileUpdaterForApplication().Update(tileNotification)
    
    End Sub
    

     

    C#:

    /// <summary>
    /// Asignar el texto a mostrar en el tile (icono) de la aplicación.
    /// En el parámetro se indicará el texto a mostrar (la hora actual)
    /// </summary>
    private void asignarTile()
    {
        string hora = DateTime.Now.ToString("HH:mm");
    
        // La plantilla para el tamaño ancho: TileWideText01
        // Esta plantilla soporta una cabecera y cuatro líneas de texto
        var tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideText01);
    
        var tileTextAttributes = tileXml.GetElementsByTagName("text");
        tileTextAttributes[0].InnerText = hora;
        tileTextAttributes[1].InnerText = DateTime.Now.ToString("dddd, dd MMMM yyyy");
    
        // La plantilla para el tamaño pequeño: TileSquareText01
        // Esta plantilla es como la ancha
        var squareTileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquareText01);
        var squareTileTextAttributes = squareTileXml.GetElementsByTagName("text");
        squareTileTextAttributes[0].InnerText = hora;
        squareTileTextAttributes[1].InnerText = DateTime.Now.ToString("dd/MMM/yyyy");
    
        // La plantilla del icono pequeño la agregamos a la del icono ancho
        // y así el propio Windows mostrará el texto que corresponda según el tamaño del icono
        var node = tileXml.ImportNode(squareTileXml.GetElementsByTagName("binding")[0], true);
        tileXml.GetElementsByTagName("visual")[0].AppendChild(node);
    
        var tileNotification = new TileNotification(tileXml);
    
        tileNotification.Tag = "ClockW8_eGi";
        TileUpdateManager.CreateTileUpdaterForApplication().Update(tileNotification);
    }
    

     

    A ver que te explico de este código que ya no te haya explicado… hmmm… no se me ocurre nada…

    Bueno, sí, aclararte unas cosas:

    tileXml tiene la plantilla del icono ancho, asignamos el texto de la cabecera con la hora y en uno de los textos secundarios indicamos la fecha completa.

    squareTileXml tiene la plantilla del icono pequeño y también asignamos la fecha y la hora, pero como el icono es más "chico" la fecha la abreviamos.

    node lo usamos para contener el icono pequeño y agregarlo al grande.

    tileNotification es el objeto que tenemos que agregar a las notificaciones y como vemos en el constructor, lo creamos a partir de un objeto XmlDocument, concretamente el de la variable tileXml.

    Y finalmente llamamos al método compartido CreateTileUpdaterForApplication de la clase TileUpdateManager para actualizar las notificaciones.

    Si ejecutas el código tal como lo tenemos ahora, comprobarás que mientras el reloj esté funcionando, cada minuto se actualizará la hora en el icono mostrado en la página de inicio de Windows 8 (en la figura 1 lo puedes ver en la parte superior derecha).

    De todas formas, para que comprendas lo que quiero hacer, aquí tienes dos imágenes en las que se muestra el icono mientras se está ejecutando la aplicación (figura 3) con el texto a mostrar y cuando la aplicación está detenida (figura 4) con la imagen que he indicado para esta aplicación.

     

    notificar_tile_03

    Figura 3. EL icono mostrando la hora y fecha actual

    (actualizada durante la ejecución de la aplicación)

     

    notificar_tile_04

    Figura 4. La imagen "normal" de la aplicación.

     

    Como te decía antes, el problema es que al terminar la ejecución de la aplicación el icono mostrará el último valor que asignó (tanto al icono grande como al pequeño) mientras estaba en funcionamiento.

     

    Nota del 8 de diciembre:

    Esto que te comento a continuación no soluciona el problema.

    Dejo las explicaciones y el código, pero no sirve para lo que se busca, que no es otra cosa que dejar el logo original en el icono (o tile).

    Además de que al ponerlo en el método de evento OnSuspending resulta que cada vez que se suspendía la aplicación (el Windows 8 suspende las aplicaciones cuando "cree" que ya no están haciendo algo interesante) se mostraban los logos en el icono y no se actualizaba con la fecha y hora.

    Esto se puede arreglar de otra forma (anoche encontré la forma de hacer todo esto de las notificaciones sin siquiera tener un temporizador e incluso que se sigan mostrando cuando la aplicación termine.

    La forma de hacer esas notificaciones las publicaré en otro artículo, pero si quieres ir experimentando, decirte que es usando la clase ScheduledTileNotification y agregándola con el método AddToSchedule de TileUpdateManager.CreateTileUpdaterForApplication().

    El problema es que yo iba probando cosas, pero según parece cuando se "distribuyen" (deploy) las aplicaciones de Visual Studio 2012 en el propio equipo con Windows 8 no se hace correctamente, o al menos a mí me ha dado la impresión de que la aplicación seguía actuando de forma "distinta" a como yo tenía programado (de programar: escribir código).

    En fin…

    Nota del 10 de diciembre:

    Aquí tienes el código con un ejemplo que actualiza cada x tiempo el contenido del tile y lo hace hasta cuando tu le digas, incluso si la aplicación está finalizada.

    Actualizar cada minuto el icono (tile) de una aplicación de Windows Store hasta la hora indicada

     

    Ahora lo que hay que hacer es asignar a los iconos las imágenes de la aplicación, concretamente las dos usadas en el icono pequeño y en el grande.

    Esas imágenes las puedes ver en la carpeta Assets y el nombre predeterminado que tienen (tu se lo puedes cambiar, siempre que también se lo indiques en el fichero Package.appmanifest) son: WideLogo.png para el icono grande y Logo.png para el icono pequeño.

    Asignaremos estas imágenes cuando finalice la aplicación… sí, piensa dónde pondrías ese código y veremos si es así… o no…

    Veamos el código para asignar las imágenes predeterminadas de los dos iconos (tiles) que estamos modificando. Ese método lo he llamado restaurarTiles.

     

    VB:

    Private Sub restaurarTiles()
        Dim tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideImage)
    
        Dim tileImageAttributes = tileXml.GetElementsByTagName("image")
        TryCast(tileImageAttributes(0), XmlElement).SetAttribute("src", "ms-appx:///Assets/WideLogo.png")
        TryCast(tileImageAttributes(0), XmlElement).SetAttribute("alt", "WideLogo")
    
        Dim squareTileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquareImage)
        Dim squareTileTextAttributes = squareTileXml.GetElementsByTagName("image")
        TryCast(squareTileTextAttributes(0), XmlElement).SetAttribute("src", "ms-appx:///Assets/Logo.png")
        TryCast(squareTileTextAttributes(0), XmlElement).SetAttribute("alt", "Logo")
    
        Dim node = tileXml.ImportNode(squareTileXml.GetElementsByTagName("binding").Item(0), True)
        tileXml.GetElementsByTagName("visual").Item(0).AppendChild(node)
    
        Dim tileNotification = New TileNotification(tileXml)
    
        tileNotification.Tag = "ClockW8clr_eGi"
        TileUpdateManager.CreateTileUpdaterForApplication().Update(tileNotification)
    End Sub
    

     

    C#:

    private void restaurarTiles()
    {
        var tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideImage);
    
        var tileImageAttributes = tileXml.GetElementsByTagName("image");
        ((XmlElement)tileImageAttributes[0]).SetAttribute("src", "ms-appx:///Assets/WideLogo.png");
        ((XmlElement)tileImageAttributes[0]).SetAttribute("alt", "WideLogo");
    
        var squareTileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquareImage);
        var squareTileTextAttributes = squareTileXml.GetElementsByTagName("image");
        ((XmlElement)squareTileTextAttributes[0]).SetAttribute("src", "ms-appx:///Assets/Logo.png");
        ((XmlElement)squareTileTextAttributes[0]).SetAttribute("alt", "Logo");
    
        var node = tileXml.ImportNode(squareTileXml.GetElementsByTagName("binding")[0], true);
        tileXml.GetElementsByTagName("visual")[0].AppendChild(node);
    
        var tileNotification = new TileNotification(tileXml);
    
        tileNotification.Tag = "ClockW8clr_eGi";
        TileUpdateManager.CreateTileUpdaterForApplication().Update(tileNotification);
    }
    

     

    ¿Desde dónde llamar al método restaurarTiles?

    Como te habrás imaginado el sitio desde el que debemos llamar a ese método no es del método de evento equivalente al Form_Unload / Form_Closing de las aplicaciones de escritorio, que en la aplicación de Windows Store "supuestamente" es el método MainPage_Unloaded, pero por lo que yo he probado, a ese método de evento (de MainPage) no llega nunca… o al menos ahí no llega a pararse si pongo un breakpoint… así que… me puse a mirar en los distintos eventos que produce la página y no vi ninguno que me convenciera. Y cuando me pongo a buscar sobre el "life cicle" (ciclo de vida) de una aplicación de Windows Store sólo me he encontrado con que una aplicación puede estar en ejecución, en modo suspendido o no está en ejecución.

    También me he encontrado con la información de que si una aplicación el usuario la cierra (pulsando Alt+F4 o mediante la opción de cierre) ésta se suspende durante 10 segundos y después finaliza.

    Si quieres saber más sobre esto último puedes ver lo que dicen en este enlace:

    Administrar el ciclo de vida y el estado de la aplicación (aplicaciones de la Tienda Windows.

     

    La cuestión es que yo he puesto la llamada a ese método en el evento OnSuspending de la clase App, es decir en el código del fichero App.xaml.

    Después de la llamada a restaurarTiles hago una llamada al método Clear de CreateTileUpdaterForApplication para que el tile (o icono) se quede "quietecico" y no siga haciendo cambios entre las últimas notificaciones que ha tenido la aplicación.

    Y este es el código:

     

    VB:

    Private Sub OnSuspending(sender As Object, e As SuspendingEventArgs) Handles Me.Suspending
        Dim deferral As SuspendingDeferral = e.SuspendingOperation.GetDeferral()
        ' TODO: Save application state and stop any background activity
        deferral.Complete()
    
        restaurarTiles()
        ' Esto es necesario para que no siga actualizándose "a su bola"
        TileUpdateManager.CreateTileUpdaterForApplication().Clear()
    End Sub
    

     

    C#:

    private void OnSuspending(object sender, SuspendingEventArgs e)
    {
        var deferral = e.SuspendingOperation.GetDeferral();
        //TODO: Save application state and stop any background activity
        deferral.Complete();
    
        restaurarTiles();
        // Esto es necesario para que no siga actualizándose "a su bola"
        TileUpdateManager.CreateTileUpdaterForApplication().Clear();
    }
    

     

    Nota del 8 de diciembre:

    Por favor lee los comentarios que he puesto hoy sábado sobre algunas cosas que en realidad no funcionan como (al menos yo) esperaba.

     

    Nota del 10 de diciembre:

    Mira este otro artículo y lo mismo ves que sí funciona esto:

    Actualizar cada minuto el icono (tile) de una aplicación de Windows Store hasta la hora indicada

     

    Y ya está… bastante por hoy ¿no?

    Pues… casi… pero lo que te voy a contar lo haré en otro "capítulo" de esta novela por entregas… y será para adaptar nuestro reloj a la ventana "snapped", es decir, cuando el usuario arrastra la ventana principal a uno de los laterales de la pantalla…

     

    Nos vemos.

    Guillermo

     

    Enlaces a los otros artículos de este "paso a paso":

    1. Usar un temporizador en las aplicaciones de Windows store (Visual Basic)
    2. Usar un temporizador en las aplicaciones de Windows store (C#)
    3. Pon una AppBar en tu aplicación de Windows Store
    4. Acceder a los recursos de una aplicación de Windows Store desde código (vb, c#, xaml)
    5. Actualizar el icono (tile) de una aplicación de Windows Store (cada minuto)
    6. Saber cuando la aplicación está en modo Snapped y actuar en consecuencia

    Actualizaciones:

    Resulta que no todo lo dicho aquí es "operativo" y por tanto, hay que rectificar…

    El 7 de diciembre de 2012 a las 00:20 publiqué el original.

    El 8 de diciembre a las 19:50 publiqué las rectificaciones / actualizaciones.

    El 10 de diciembre a las 07:06 pongo el enlace al nuevo artículo con un ejemplo "operativo" y sin usar timer.

  • Acceder a los recursos de una aplicación de Windows Store desde código (vb, c#, xaml)

    Pues eso… seguimos con la aplicación Reloj para Windows 8, en esta ocasión vamos a añadir un par de cadenas a los recursos de la aplicación y veremos cómo acceder a esos recursos tanto desde código de Visual Basic y C# como desde el código de XAML.

    El recurso es para usarlo como cadena de texto en varias partes de la aplicación, por tanto lo definiremos a nivel de aplicación, pero más que nada porque es más fácil de recuperar (o al menos a mí me ha parecido más fácil).

    De todas formas, tal como veremos en el código XAML de la página principal, el uso desde el código XAML no afecta dónde esté definido, pero ese sitio si que lo tendremos que tener en cuenta a la hora de acceder a dichos recursos desde el código de VB o C#.

     

    Recursos 00
    Figura 1. El reloj en funcionamiento

     

    Te cuento que a la hora de buscar cómo hacer esto me encontré con ejemplos que utilizan FindResource, entre otros sitios, los que vienen en el libro Windows Presentation Foundation Unleashed (que tengo desde febrero de 2007 y que no usaba desde hace unos cuantos años, pero que he tenido que retomar para consultar las posibles dudas que me vayan surgiendo en mi "pelea" con el código XAML que hay que usar para las aplicaciones de Windows Store).

    En ese mismo libro indicaba que se podían acceder indicando el nombre de la clase (página XAML) en la que están definidos los recursos, pero que, tal como te he dicho, a mi no me ha funcionado. Que a lo mejor funciona, que no digo yo que no, pero yo no he sido capaz de utilizarlos como yo quería, así que…

    Bueno, vamos al tema…

    Lo primero que tenemos que hacer es crear los recursos como cadenas, en mi caso, estoy usando dos cadenas, una para el título de una especie de MessageBox (especie, ya que no son avisos como los que estamos acostumbrados en las aplicaciones de escritorio) y el otro para usarlo como texto de ese aviso y como Tooltip del botón "Acerca de…"

    Dichas cadenas las defino de esta forma:

    <x:String x:Key="ClockTitle_en">Clock for Windows 8 app Store</x:String>
    <x:String x:Key="AboutClock_en">A simple clock for Windows 8 app Store by elGuille.info</x:String>
    

     

    Yo he definido estas dos cadenas en el fichero App.xaml, es decir, en la "aplicación".

    Si abrimos el fichero App.xaml veremos que justo después de la cabecera tenemos un elemento llamado Application.Resources y dentro de ese hay otro elemento llamado ResourceDictionary que es el que contiene la definición de ResourceDictionary.MergedDictionaries que es donde está indicado el fichero de los estilos que vimos en el artículo anterior (StandardStules.xaml).

    Pues bien, esas dos definiciones de las cadenas las pondremos justo después del inicio del elemento ResourceDictionary, de forma que nos quede de esta forma:

    <Application.Resources>
        <ResourceDictionary>
            <x:String x:Key="ClockTitle_en">Clock for Windows 8 app Store</x:String>
            <x:String x:Key="AboutClock_en">A simple clock for Windows 8 app Store by elGuille.info</x:String>
    
            <ResourceDictionary.MergedDictionaries>
    
                <!-- 
                    Styles that define common aspects of the platform look and feel
                    Required by Visual Studio project and item templates
                 -->
                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
    
        </ResourceDictionary>
    </Application.Resources>
    

     

    Ahora vamos a modificar MainPage.xaml para asignar el Tooltip al botón buttonClock. Este será el código a usar para definir el botón que está en el segundo panel:

    <Button x:Name="buttonClock" Click="button_Clock" 
            Style="{StaticResource AboutClockAppBarButtonStyle}"
            ToolTipService.ToolTip="{StaticResource AboutClock_en}" />
    

     

    Como puedes comprobar, la forma de usar ese recurso desde XAML es igual que el resto de recursos definidos en nuestro proyecto.

    Pero al usarlo desde el código tendremos que indicar "dónde" está (obvio por otro lado).

    Así que, vamos a modificar el código, particularmente el evento Click del botón buttonClock.

    Lo que haremos es mostrar una especie de "popup" que viene a ser como el MessageBox de las aplicaciones de escritorio, pero al estilo de Windows 8.

    Para mostrar ese mensaje usaremos la clase MessageDialog que está definida en Windows.UI.Popups y que por medio del método ShowAsync mostraremos el mensaje.

    ¿Te has fijado en el nombre del método?

    Acaba con la palabra Async… lo cual significa que es un método asíncrono… y si queremos usarlo de esa forma (asíncronamente) tendremos que hacer la llamada desde un método asíncrono y por tanto usando Await (los que han visto mi presentación en la gira que hice el mes pasado por Perú sabrán de qué hablo, y tú también, claro, porque ya sabes de qué va esto de Async… ¡ejem! ¡ojú que carraspera tengo!)

    Bueno, dejemos las carrasperas y vamos a lo que vamos.

    Para acceder a la cadena del recurso ClockTitle_en y asignarlo al título del diálogo haremos algo así:

    En C#:

    dlg.Title = App.Current.Resources["ClockTitle_en"].ToString();

     

    En Visual Basic:

    dlg.Title = App.Current.Resources("ClockTitle_en").ToString()
    

     

    Es decir, accedemos a la colección Resources de la aplicación y le indicamos el nombre del recurso al que queremos acceder. Como es una cadena, usamos el ToString para convertirlo adecuadamente.

     

    Si en lugar definirlo en el fichero App.xaml queremos definirlo en la propia página en la que tenemos el código (y el diseño de la aplicación), es decir en MainPage.xaml, podemos hacerlo de esta forma:

    dlg.Title = Me.Resources("ClockTitle_en").ToString()
    

     

    dlg.Title = this.Resources["ClockTitle_en"].ToString();

     

    En ese caso, la definición de las cadenas las tendremos que poner justo después de la definición de la cabecera de la página y encerrarlas en el elemento Page.Resources tal como podemos ver a continuación (aquí sólo he puesto una de las dos cadenas):

    <Page.Resources>
        <x:String x:Key="ClockTitle_en">Clock for Windows 8 app Store</x:String>
    </Page.Resources>
    

     

    Por supuesto, también podemos definirlo en el fichero StandardStyles.xaml, y la forma de usarlo desde el código (o desde XAML) será de la misma forma que definiéndolo en el fichero App.xaml.

    De hacerlo ahí, tendrás que definir las cadenas justo después del "ResourceDictionary" que hay en la parte superior, por tanto lo tendrías que añadir a partir del cierre del elemento, concretamente después de esta línea:

    </ResourceDictionary.ThemeDictionaries>.

     

    Bien, dicho todo esto, veamos el código completo del evento Click del botón buttonClock y una captura de la pantalla al mostrar el mensaje.

    Visual Basic:

    Private Async Sub button_Clock(sender As Object, e As RoutedEventArgs)
        ' Mostrar la imagen del SplashScreen o el mismo texto del Tooltip...
        Dim dlg = New Windows.UI.Popups.MessageDialog(
    App.Current.Resources("AboutClock_en").ToString()) dlg.Title = App.Current.Resources("ClockTitle_en").ToString() Await dlg.ShowAsync()

     

    C#:

    private async void button_Clock(object sender, RoutedEventArgs e)
    {
        // Mostrar la imagen del SplashScreen o el mismo texto del Tooltip...
        var dlg = new Windows.UI.Popups.MessageDialog(
    App.Current.Resources["AboutClock_en"].ToString()); dlg.Title = App.Current.Resources["ClockTitle_en"].ToString(); await dlg.ShowAsync();

     

    Como la llamada al método ShowAsync queremos que sea asíncrona, tenemos que definir el método como asíncrono (con el modificador Async / async) y para que dicho método "realmente" sea asíncrono, tenemos que realizar la llamada con Await / await.

     

    Al pasar el ratón por el botón de la derecha (el que estamos modificando) se mostrará el ToolTip tal como vemos en la figura 2, y al pulsar en el botón se mostrará el mensaje que vemos en la figura 3.

    Recursos 02

    Figura 2. El mensaje (Tooltip) al pasar el cursor del ratón por el icono

     

    Recursos 03

    Figura 3. El mensaje mostrado al pulsar en el botón de la derecha

     

     

    Y esto es todo por ahora… espero que te sea de utilidad… 😉

     

    Nos vemos.

    Guillermo

     

    Temas relacionados:

    1. Usar un temporizador en las aplicaciones de Windows store (Visual Basic)
    2. Usar un temporizador en las aplicaciones de Windows store (C#)
    3. Pon una AppBar en tu aplicación de Windows Store
    4. Acceder a los recursos de una aplicación de Windows Store desde código (vb, c#, xaml)
    5. Actualizar el icono (tile) de una aplicación de Windows Store (cada minuto)
    6. Saber cuando la aplicación está en modo Snapped y actuar en consecuencia