Archivo por meses: diciembre 2012

que dice el Guille que…

 

Pues eso… en este día 31 de diciembre de 2012… ¿qué quieres que te diga?…

¡¡¡ Feliz 2013 !!!

 

 

 

Nos vemos… el año que viene…
Guillermo

Como usar GetFileFromApplicationUriAsync (para que no se te cuelgue la aplicación)

 

Pues eso… que este post iba a ser una especie de declaración de "mi" incapacidad a la hora de trabajar de forma asíncrona con los ficheros de una aplicación de Windows Store, y al final será una especie de truco o consejo de cómo hacer las cosas con todo esto de el acceso a ficheros de forma asíncrona (la única que conozco para Windows Store).

Y la solución no es porque "yo" haya dado con la respuesta, bueno, un poco sí, ya que si no hago un par de búsquedas en Google lo mismo no hubiese dado con la respuesta.

Y digo "un par" de búsquedas por no decir tres, ya que es complicado algunas veces encontrar respuestas, sobre todo porque la mayoría de las preguntas con respuestas están en inglés y después porque no sabes con certeza cómo "plantear" la búsqueda…

Y lo curioso es que al tercer intento con esto: "GetFileFromApplicationUriAsync don’t" es cuando ha salido la respuesta, y precisamente en el primer lugar:

 

getfilefromapplicationuriasync don t  Buscar con Google

 

Y eso que en la búsqueda anterior lo puse un poco más concreto, en fin… estos buscadores y/o los usuarios de los mismos… ¡habrá que apañarlos! 😉

 

Te explico de qué va la cosa:

Estoy haciendo una aplicación para Windows 8 (Windows Store) en la que quiero mostrar el contenido de unos ficheros de textos. Para ello estoy usando una versión adaptada del tipo de proyecto Split App en el que todo el contenido está basado en "bindings", pero ese no es el problema, el problema es que en la clase SampleDataSource estoy haciendo una serie de cambios para que se adapte a lo que yo quiero, y como el texto a mostrar es bastante grande como para "pegarlo" en la propia clase (tal como hacen en el proyecto de ejemplo) me puse a crear un código que leyera el contenido del fichero y lo asignara adecuadamente a una propiedad de la clase usada como "binding" que es la que muestra el texto final.

Así que… se me ocurrió usar este código (y algún otro con distintas pruebas):

 

Dim ficUri = New Uri("ms-appx:///contenido/" & fic)

' aquí se queda colgado (algunas veces)
' las veces que pasa de aquí es en modo debug y haciendo un break
' (pero no siempre)
Dim file = Await StorageFile.GetFileFromApplicationUriAsync(ficUri)

' Aquí también se para, y pasa si hay un breakpoint
Dim sf = Await file.OpenStreamForReadAsync

Using sr As New StreamReader(sf, Encoding.UTF8, True)
    sBody = sr.ReadToEnd()
End Using

Return sBody


 

Y tal como comento en los comentarios (valga la redundancia) cuando estaba en funcionamiento "natural" no pasaba de ahí y se quedaba colgada la aplicación.

Ya estaba por desistir cuando me ha dado el punto de buscar en Google ya que suponía que no sería cosa mía y que seguramente a alguien más le habrá ocurrido… ¡y así es!

Nota: antes de buscar en Google ya busqué información en la documentación de Visual Studio, pero no encontré nada, aparte de los ejemplos triviales que suelen poner que que dan por hecho de que prácticamente te lo sabes todo, todo, todo… en fin…

No me voy a enrollar más de la cuenta y te pongo el código correcto para hacer esa tarea de leer un fichero de forma asíncrona y que no se quede colgada la aplicación de Windows Store.

 

También te pondré el enlace a esa pregunta y un artículo del autor de la respuesta donde explica porqué pasa eso, no, mejor dicho: "porqué nos pasa eso a los principiantes" del acceso asíncrono, o eso he entendido yo de este párrafo:

"I think it’s the most-asked question by async newcomers once they’ve learned the basics."

Y tiene razón ya que reconozco que soy un newcomer en esto de el acceso asíncrono, al menos para los ficheros en el directorio de la aplicación, ya que antes he estado accediendo de forma asíncrona a otros ficheros (del Local storage) y ha funcionado… en fin…

 

Dim ficUri = New Uri("ms-appx:///contenido/" & fic)

Dim file = Await StorageFile.GetFileFromApplicationUriAsync(ficUri).AsTask().ConfigureAwait(False)

Dim sf = Await file.OpenStreamForReadAsync().ConfigureAwait(False)

Using sr As New StreamReader(sf, Encoding.UTF8, True)
    sBody = sr.ReadToEnd()
End Using


Return sBody

 

Como puedes ver, la solución es agregar .AsTask().ConfigureAwait(False) al final del método GetFileFromApplicationUriAsync que es el que se encarga de obtener el fichero desde el directorio de la aplicación, y de ConfigureAwait(False) al método OpenStreamForReadAsync que es el que se encarga de convertir dicho fichero en un Stream de lectura.

 

La explicación del porqué del uso de esos métodos está en el comentario que hizo Nito al preguntarle el "buscador de respuestas" porqué era necesario hacer eso:

(te lo pego en inglés que la traducción automática como que no me convence)

Explanation is quite simple: when you use task.Result ortask.Wait() on GUI thread in conjunction with await keyword you’re causing deadlock. This happens because after awaiting code is resumed on the same context it was invoked (in your case – GUI thread). And because GUI thread currently waiting for task to complete (via Result or Wait()) deadlock arises and code after await keyword will never be invoked. ConfigureAwait(false)specifies that current context can be ignored and thus allows your code to complete successfully. More details on this here: http://nitoprograms.blogspot.com/2012/07/dont-block-on-async-code.html

 

Este es el enlace a la pregunta/respuesta esa que te comentaba y este otro es al artículo de Nito Programming.

 

Espero que te sea de utilidad.

 

Nos vemos.

Guillermo

En ocasiones Visual Studio no avisa de los errores en XAML

 

Pues eso… aunque al leer el título: "en ocasiones…" parece lo del niño ese de la peli Sexto Sentido (no del disco de Melendi), pero la realidad es que es así… y lo de "en ocasiones" lo he puesto como quien dice "presunto" asesino, por aquello de no pillarse los dedos…

Te cuento… como últimamente estoy aburrío (jeje), pues me puse a trastear en los estilos de una de las páginas incluidas en los proyectos de tipo Windows Store, ya que en la ayuda vi que puedes modificar un estilo existente y agregarlo a varios sitios: la propia página en la que está el estilo original, en App.xaml y en otra página (del tipo resource dictionary) que tengas en tu proyecto.

Todo esto es porque no daba con la tecla de cómo usar mi propio fichero de estilos que define estilos que usan otros estilos que están definidos en otra página… ¡QUIETOOOORRRR!
¡No te vayas!, que no es que te esté intentando liar… es que es así… sí, además, si estás leyendo esto lo mismo ya has leído lo que he publicado hace unos minutos y que precisamente soluciona el problema ese que te comento en este mismo párrafo:

Crear un fichero de estilos XAML y acceder a estilos definidos en otro fichero

 

Sigamos, pero veamos "gráficamente" lo que te comento, por ejemplo, abrimos (en modo diseño) la página SplitPage.xaml de un proyecto del tipo SplitApp (vale cualquier otra que tenga el botón para ir atrás), si seleccionas el botón de ir atrás y vas a las propiedades para acceder a la propiedad Styles (que está en el grupo Miscellaneous) verás que el estilo actual está indicado con un puntito verde a la derecha (ver figura 1), si pulsas en ese "punto" te mostrará un menú con varias opciones (ver figura 2)

propiedades 1propiedades 2
Figuras 1 y 2. Propiedades del estilo y convertir a nuevo recurso

 

Si seleccionas Convert to New Resource te permitirá hacer una copia de ese recurso en el fichero que indiques, en mi caso (tal como vemos en la figura 3) lo quiero incluir en mi fichero de estilos.

 

propiedades 3
Figura 3. El cuadro de diálogo para indicar dónde estará el nuevo recurso

 

Nota:
Los ficheros mostrados en esa lista desplegable del cuadro de diálogo de crear un nuevo recurso de estilos, deben estar previamente agregados a MergedDictionary de App.xaml.
Para saber cómo hacer esto que te comento, mira el enlace que te puse más arriba.

 

Pues bien, eso hice yo. Agregué el nuevo recurso a mi fichero, e hice todo lo que te comenté en el post anterior. Ahora voy a probar los cambios (que no había sido nada, sólo lo de crear el recurso en otro fichero) y no funciona la aplicación.
Bueno, funcionar si que funciona, de hecho me mostraba la página principal (ItemsPage) pero al pulsar en una de las opciones mostradas no me mostraba la otra página con los detalles… ¿adivinas cómo se llama la otra página! ¡BINGO! SplitPage.
Pero claro, yo como iba a pensar que "yo" era el culpable (¿he sido yo? que diría Steve Urkel), así que… me puse a "debuguear" y nada, se quedaba en la llamada a esa página, pero no mostraba nada, ni daba error, ni ná de ná.
Así que… aburrido (esta vez de probar y probar y no saber qué pasaba) quité ese proyecto y volví a crear otro del mismo tipo, pero sin cambiar nada del recurso… La pruebo y, claro, como es de esperar: ¡funciona!
Eso sí, se paró en el mismo punto de interrupción que puse en la aplicación anterior ¿por qué? ni idea, lo que si te digo es que las dos aplicaciones se llamaban igual (sólo renombre el directorio para que no me diese error).

Volví a probar sin el breakpoint y seguía funcionando. Así que… descargue ese proyecto y volví a cargar el anterior (el que estuve trasteando). Y me fije en cómo se llamaba el recurso que utilizaba el botón ese de la página SplitPage, y como era de esperar al hacer la copia del recurso se había cambiado el nombre y como resulta que yo había eliminado (en realidad quitado del proyecto) el fichero en el que se creó dicho recurso de estilos, pues… ¡no existía!

Resumen, que ya es noche y me estoy alargando más de lo necesario:
Si usas un estilo que no existe en un control XAML, "es posible" que Visual Studio no te alerte de ese "pequeño detalle".

Y lo de es posible lo digo porque a mi otras veces si me ha avisado de que no existe, pero no avisando con un mensaje de alerta, si no , detallándolo en la ventana de errores, ya que si compilas y ejecutas si que funciona… es decir, no se para porque haya un error en el código XAML.

Aunque también está la contrapartida, la de que te muestre cuadro de diálogo con un error (o que se pare la aplicación en el código oculto de la clase App) pero no veas el error en ningún sitio (ni en la ventana de errores), eso sí, con suerte puedes ver que el mensaje de error es:
DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
como ya te comenté en su día: Error al usar inadecuadamente el XAML.

 

Pues nada, creo que ya está bien por hoy… y si esto te sirve para no tener los quebraderos de cabeza que he tenido por "no saber", pues… mejor, eso es de lo que se trata algunas veces: que los demás solucionen antes los errores que yo ya tuve… 😉

 

Nos vemos.
Guillermo

Crear un fichero de estilos XAML y acceder a estilos definidos en otro fichero

 

Pues eso… que estaba yo haciendo modificaciones en los estilos que vienen en el fichero StandardStyles.xaml (incluido en las plantillas de aplicaciones para Windows Store), y como no tenía muy claro cómo definir esas modificaciones en otro fichero diferente del que se incluye en las plantillas de los proyectos para Windows Store, al final acabé dejándolos en ese mismo fichero.

Pero hoy (o ayer, ya no llevo la cuenta de los días y las noches) me puse a hacerlo (de nuevo), es decir, me fui a agregar un nuevo fichero del tipo ResourceDictionary (Dictionary1.xaml) y ahí pegué los estilos que yo definí para usar en otra aplicación y que lo mismo me podrán ser de utilidad en esta nueva.

Una vez que has creado un nuevo "diccionario de recursos", debes añadirlo al elemento ResourceDictionary de App.xaml, concretamente en ResourceDictionary.MergedDictionaries, que uno sabe esto, entre otras cosas porque ahí es donde está indicado el fichero de estilos estándar.

Como mis nuevos estilos (algunos de ellos) están basados en los que se incluyen en StandardStyles.xaml, lo que hice es agregarlo después del StandardStyles (por aquello de que así referencie primero el estándar y después el mío). Pero no… así no vale.

El problema es que además el Visual Studio no te dice nada de que eso está mal, la cuestión es que la aplicación no funciona y, lo más frustrante es que no sabes por qué.
Ahora sé que es porque estaba haciendo referencia a estilos que no estaban definidos (al menos en mi fichero).
Pero el problema es que yo "sabía" que sí, que esos estilos estaban definidos, pero en otro fichero.
Y ese era el problema, que estaban en otro fichero.

Busqué en la ayuda a ver… y curiosamente vi que en uno de los ejemplos había dos ficheros definidos en MergedDictionaries de App.xaml. Y en realidad eso hay que hacerlo así:

 

<Application.Resources>
    <ResourceDictionary>
        <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 Source="Dictionary1.xaml"/>
            
        </ResourceDictionary.MergedDictionaries>

        <!-- Application-specific resources -->

        <x:String x:Key="AppName">Libros SplitApp</x:String>
    </ResourceDictionary>
</Application.Resources>

 

Pero lo que debes saber (todo este rollo para contarte esto) es que si tu fichero va a usar estilos definidos en otro fichero (al menos si tu intención es crear nuevos estilos que se basen en algunos de los definidos en ese fichero) debes indicarlo de forma explícita en tu fichero, y sería incluyéndolo en un elemento del tipo MergedDictionaries, pero en tu fichero.

Por ejemplo, si desde mi fichero Dictionary1.xaml quiero utilizar estilos definidos en StandardStyles debo añadir este código al principio del fichero (o en la parte superior, después de las definiciones de ResourceDictionary):

 

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Libros_SplitApp">

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

    <!-- Mis estilos basados en otros existentes en StandardStyles.xaml -->
  

 

Para que nos entendamos:

1.- Si quiero que en mi aplicación los recursos definidos en mi fichero estén accesibles, debo indicarlo en MergedDictionary de App.xaml.

2.- Si quiero que mi fichero utilice estilos definidos en otro fichero de estilos XAML debo crear un elemento MergedDictionary y añadirlo de la misma forma que el mío está indicado en App.xaml.

 

Y eso es todo…

Pero habrá más, ya que el XAML no se lleva muy bien que digamos con el "depurador" de Visual Studio y hay más cosas que pueden pasar sin que sepas por qué pasan… aunque para eso estoy yo aquí, par contártelo 😉

 

Nos vemos.

Guillermo

Las plantillas de Windows Store o la dejadez para con Visual Basic

 

Pues eso… que "trasteando" con las plantillas (tipos de aplicaciones) para Windows Store usando Visual Basic como lenguaje de programación, me he topado con un par de "chorradas" que aunque no son importantes creo que son muestra de, siendo benévolo (por aquello de las fechas en que estamos), un poco de dejadez por parte de "a quién corresponda".

Nota del 29/Dic/12:
He agregado un extra…

Me estoy refiriendo a las plantillas de proyectos para Windows Store de más de una página es decir: GridApp y SplitApp (ver la figura 1).

 

proyectos_appstore
Figura 1. Crear un nuevo proyecto para Windows Store con Visual Basic

 

Si tienes Option Strict On, es decir: ser estricto con las declaraciones y sobre todo con las asignaciones, lo primero que te encuentras es con esta asignación en App.xaml.vb:

Dim rootFrame As Frame = Window.Current.Content

Y la comprobación estricta te indica que esa conversión implícita no es correcta, por suerte, Visual Basic nos ofrece una solución a ese error, tal como vemos en la figura 2, en la que propone que hagamos la conversión con CType.

 

error de conversion implicita

Figura 2. Error de conversión implícita y la solución

Como vemos, la conversión de tipos propuesta es usando CType aunque yo prefiero usar TryCast que es más liviano sobre todo si sabemos que esa conversión es correcta y no producirá un error en tiempo de ejecución.

 

TryCast devuelve un valor nulo si no puede hacer la conversión, mientras que CType producirá una excepción.

 

Otra de esas "chorradillas" que te comento que me he encontrado es en el método ItemsCollectionChanged de la clase SampleDataGroup que está en la carpeta DataNodel de estos dos proyectos.

En dos de los "Case" que realiza al hacer una doble comprobación, es decir, comprueba si esto y aquello está ocurriendo, utilizar And en lugar de AndAlso.

¡¡¡ En este código vemos las dos líneas que contienen los operadores And fatídicos !!!

 

If e.NewStartingIndex < 12 And e.OldStartingIndex < 12 Then


While TopItems.Count < Items.Count And TopItems.Count < 12

 

Puede que creas que no es para tanto, pero si de verdad conoces lo que hace cada uno de esos dos operadores… La cuestión es que yo no utilizo And para una comparación desde que salió la primera versión de Visual Basic para .NET, de hecho antes casi tampoco la usaba y prefería hacer una doble comprobación: usar dos Ifs en lugar de usar And.

Además la documentación de Visual Studio sobre el operador And te indica la diferencia entre And y AndAlso:

En una comparación booleana, el operador And evalúa siempre las dos expresiones, lo que podría incluir llamadas a procedimientos. AndAlso realiza un cortocircuito, lo que significa que si expression1 es False, no se evalúa expression2.

 

Lo curioso de esos And es que si el código está convertido de C# es extraño que no conviertan adecuadamente el operador usado en C#: &&, pero bueno… como And también puede ser un solo ampersand: & pues… es fácil que el conversor de código se equivoque.

 

Ya te dije que eran cosas triviales, pero en ocasiones ver que no se cuidan esos pequeños detalles te pueden hacer pensar si habrá algo más que también pueda fallar… ¡esperemos que no! 😉

 

Nos vemos.

Guillermo

 

Más de lo mismo (addendum del 29 de diciembre de 2012):

Efectivamente, tiene toda la pinta de ser una traducción de C# a Visual Basic, pero "presuntamente" sin mucho cuidado en el resultado final (o casi), ya que en la clase SampleDataSource, concretamente en el constructor se asignan ciertos valores de prueba y una cadena con el contenido, que no es más que la misma cadena repetida varias veces, pero en C# usan \n\n para crear cambios de líneas y en VB lo han dejado con esos mismos "retornos de línea" que no hacen más que mostrar esos caracteres en la cadena en lugar de hacer los cambios de línea.

La posible solución a esta "chorradilla" de fallo sería algo así:

Dim ITEM_CONTENT As String = String.Format("Item Content: {0}{1}{0}{1}{0}{1}{0}{1}{0}{1}{0}{1}{0}",
            "Curabitur class aliquam vestibulum nam curae maecen ..."
            vbCrLf)

 

Pues eso… en fin… es que me hierve la sangre… si no quieren que usemos el Visual Basic, ¡que lo quiten! pero si lo dejan que esté donde tiene que estar: en lugar preferente.

Ejemplo de ListView y equivalencia a subitems

 

Pues eso… si has intentado usar el control ListView en una aplicación de tipo WPF (Windows Presentation Foundation), seguramente te habrás dado cuenta que no tiene nada que ver con el control de las aplicaciones de Windows Forms (WinForms), sobre todo a la hora de agregar y recuperar los elementos, de hecho la propiedad SubItems no existe.
Sí, es más complicado.

 

WPF = .NET + XAML (Windows Presentation Foundation) WPF = .NET + XAML

 

Hace unos años (más de 5) ya te comentaba algunos de esos cambios en los puntos 9, 10 y 11 de Equivalencias entre las clases de WPF y las de .NET (1).

Aquí te voy a explicar lo que yo creo que se debe hacer para acceder a los elementos de un ListView, que en realidad no ha cambiado mucho, pero lo que sí ha cambiado es la forma de acceder a los "sub elementos", es decir ya no existe una propiedad / colección subitems que es la forma que tenemos en WinForms para acceder a las diferentes columnas de cada elemento.

Cómo se hace en WinForms

Por ejemplo, si tenemos un ListView con más de una columna, la forma de acceder a la segunda columna de la primera fila es: LisView1.Items(0).SubItems(1).Text

Pero eso no lo podemos hacer en WPF ya que SubItems no existe.

Cómo se hace en WPF

Los controles ListView de WPF para definir columnas deben tener un control del tipo GridView como contenido de la propiedad ListView.View.

Ese GridView define tantas columnas como sean necesarias, esas columnas son del tipo GridViewColumn y en la definición de las propiedades de ese objeto-columna, concretamente en DisplayMemberBinding es donde está "la magia" de cómo acceder a cada uno de los "sub-elementos" de cada fila de datos.

Por ejemplo, si tenemos un formulario tal como el mostrado en la figura 1, ese ListView lo podemos definir tal como vemos en el listado 1.

El formulario WPF
Figura 1. El formulario de WPF

 

Este es el código XAML para crear ese ListView (más abajo te mostraré el código completo):

<ListView Name="lvDatos" Margin="0" MinHeight="240"
      VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<ListView.View>
    <GridView AllowsColumnReorder="True">
        <!-- Utilizo valores en Binding distintos a los nombres de las cabeceras
             para que se vea que no hay relación entre el nombre del Header
             y el nombre de Binding -->
            <GridViewColumn Header="Nombre y apellidos" 
                            DisplayMemberBinding="{Binding Nombre}" Width="150"/>
            <GridViewColumn Header="Correo" 
                            DisplayMemberBinding="{Binding email}" Width="190"/>
            <GridViewColumn Header="How old" 
                            DisplayMemberBinding="{Binding Edad}" Width="70"/>
    </GridView>
</ListView.View>
</ListView>

Listado 1. El código XAML del control ListView

Como puedes comprobar en cada columna hay tres asignaciones a otras tantas propiedades:

Width que es para indicar el ancho, Header que es para indicr el texto a mostrar en la cabecera de la columna y DisplayMemberBinding que es la propiedad que enlaza con el dato a mostrarse en esa columna.

Como puedes comprobar el valor que le estoy asignando a esa propiedad "enlazada" es: {Binding Nombre}.

¿Qué significa eso?

Eso significa que lo que se muestre en esa columna será un "campo" (o propiedad) del dato que se agregue a una fila.

Y tu dirás… ¡pos vale! pero no me entero…

Eso quiere decir que cada dato que añadas a cada fila del ListView debe ser un objeto que defina una propiedad con el mismo nombre que indicamos después de Binding. En ese ejemplo, será una propiedad llamada Nombre.

Es decir, en WPF el ListView no funciona "tan fácil" como lo hace en WinForms, en WPF los elementos agregados deben ser (o deberían ser) de un tipo concreto y las columnas (o cabeceras) deben hacer referencia a las propiedades que queremos mostrar.

En el ejemplo que he puesto del ListView para WPF podemos usar una clase que defina al menos tres propiedades (no campos públicos) con los nombres usados después de Binding, en este ejemplo esas propiedades deben ser: Nombre, email y Edad.

Nota:

Los nombres usados en Binding deben coincidir "exactamente" con los nombres de las propiedades, es decir, no deben diferenciarse en las mayúsculas/minúsculas… ¡ni siquiera en Visual Basic!

Además deben estar definidos como propiedades, no como métodos ni campos públicos.

 

Código de cómo agregar y recuperar los elementos

Veamos cómo agregar elementos al ListView y después veremos cómo recuperar esos elementos.

Te voy a mostrar dos formas de hacerlo:

1.- usando tipos anónimos (no es recomendable, pero si sabes lo que haces, es fácil de utilizar, aunque a la hora de recuperar los datos en Visual Basic deberías hacerlo usando Option Strict Off al menos en el fichero de código en el que estés usando el tipo anónimo. En C# tendrás que acceder a ese dato usando dynamic.

2.- usando un tipo de datos previamente definido con esas tres propiedades.

 

Agregar datos usando un tipo anónimo

Para agregar los datos en este ejemplo, voy a usar un bucle que agregue 10 objetos al ListView. Como son objetos de tipo anónimo usamos un tipo anónimo que defina las propiedades que necesitamos.

VB:

' Rellenar el listview con datos
lvDatos.Items.Clear()
For i = 1 To 10

    ' Asignamos un tipo anónimo
    ' Si las columnas (GridViewColumn) tienen un Binding a las propiedades
    ' los valores se asignarán a cada columna, si no están enlazadas
    ' se agregará el valor de ToString a cada columna (estará el texto repetido)
    lvDatos.Items.Add(New With 
                      {.Nombre = "Nombre anónimo" & i.ToString,
                       .email = "correo" & i.ToString & "@outlook.com",
                       .Edad = 17 + i
                      })

Next

C#:

// Rellenar el listview con datos
lvDatos.Items.Clear();
for (var i = 1; i <= 10; i++)
{

    // Asignamos un tipo anónimo
    // Si las columnas (GridViewColumn) tienen un Binding a las propiedades
    // los valores se asignarán a cada columna, si no están enlazadas
    // se agregará el valor de ToString a cada columna (estará el texto repetido)
    lvDatos.Items.Add(new
    {
        Nombre = "Nombre anónimo" + i.ToString(),
        email = "correo" + i.ToString() + "@outlook.com",
        Edad = 17 + i
    });

}

 

Recuperar los datos usando un tipo anónimo

La forma de recuperar esos datos es usando una variable de tipo Object en Visual Basic y en el caso de C# esa variable debe ser de tipo dynamic, ya que en ambos lenguajes vamos a usar late-binding o enlace tardío, es decir, hasta que no se ejecute el código no se sabrá si esas propiedades están definidas o no en el objeto recuperado del ListView.

Es importante que leas y pruebes el comentario que te hago al final del código sobre qué ocurriría si quisiéramos acceder a una propiedad que no está definida en el "tipo anónimo".

VB:

' Si tenemos Option Strict Off podemos hacer esto:
Dim v = lvDatos.SelectedItem

txtMostar.Text = v.Nombre & " (" & v.email & ")"

' El problema es que queramos acceder a una propiedad que no exista
' (esto dará error)
'txtMostar.Text = v.Nombre & " (" & v.cagonto & ")"

C#:

// En C# debemos definir la variable como dynamic:
dynamic v = lvDatos.SelectedItem;

txtMostar.Text = v.Nombre + " (" + v.email + ")";

// El problema es que queramos acceder a una propiedad que no exista
// (esto dará error)
//txtMostar.Text = v.Nombre + " (" + v.cagonto + ")";

 

Agregar datos usando un tipo definido (Colega)

Como podrás comprobar, el código de agregar los datos es muy parecido al anterior, la diferencia está en que después de "NEW" usamos el tipo Colega en vez de no indicar qué tipo de datos estamos usando.

VB:

' Rellenar el listview con datos
lvDatos.Items.Clear()
For i = 1 To 10

    ' Asignamos un valor de un tipo definido (Colega)
    ' que tiene como mínimo las propiedades que vamos a usar
    ' en el listiView

     lvDatos.Items.Add(New Colega With
                      {.Nombre = "Nombre " & i.ToString,
                       .email = "email" & i.ToString & "@outlook.com",
                       .Edad = 17 + i
                      })

Next

C#:

// Rellenar el listview con datos
lvDatos.Items.Clear();
for (var i = 1; i <= 10; i++)
{

    // Asignamos un valor de un tipo definido (Colega)
    // que tiene como mínimo las propiedades que vamos a usar
    // en el listiView

    lvDatos.Items.Add(new Colega
    {
        Nombre = "Nombre " + i.ToString(), 
        email = "email" + i.ToString() + "@outlook.com", 
        Edad = 17 + i
    });

}

 

Recuperar los datos usando un tipo definido (Colega)

En este caso, como estamos usando un tipo definido previamente, lo único que tenemos que hacer a la hora de recuperar el elemento seleccionado es hacer un "cast", es decir, una conversión al tipo Colega, ya que el valor devuelto por SelectedItem es del tipo object, ya que los elementos de un ListView pueden ser de cualquier tipo, incluso tipos mezclados… aunque esto último no lo he probado, pero hacer se puede hacer… otra cosa es hacerlo bien, jeje.

VB:

Dim v = TryCast(lvDatos.SelectedItem, Colega)

txtMostar.Text = v.Nombre & " (" & v.email & ")"

' como ahora usamos un tipo previamente definido
' si usamos esto:
'   txtMostar.Text = v.Nombre & " (" & v.cagonto & ")"
' nos dirá que "cagonto" no es un miembro de Colega

C#:

var v = ((Colega)lvDatos.SelectedItem);

txtMostar.Text = v.Nombre + " (" + v.email + ")";

// como ahora usamos un tipo previamente definido
// si usamos esto:
//   txtMostar.Text = v.Nombre + " (" + v.cagonto + ")";
// nos dirá que "cagonto" no es un miembro de Colega

 

Y esto es todo… espero que te haya aclarado algunos conceptos y dudas… y si quieres saber más sobre los ListView, los GridView y cómo hacer maravillas con el contenido de los ListView, te recomiendo que le eches un vistazo a los enlaces que te he puesto en cada una de esos tipos de datos.

 

Un poco más abajo tienes el código compelto tanto del XAML como el de Visual Basic y C#.

Ese código lo he hecho con Visual Studio 2012, pero supongo que será válido en otras versiones anteriores: en el caso de VB desde el VS2008, y en el caso de C# desde Visual Studio 2010.

 

Nota:

En ese código hay comentadas un par de pruebas que puedes hacer.

Entre ellas está la de no asignar ningún valor a la propiedad Binding de los GridViewColumn y asignar cadenas normales a los elementos del ListView, después al ejecutar el programa y al recuperar el contenido del elemento seleccionado verás que… no te lo cuento… así lo tendrás que probar 😉

 

 

Espero que te sea de utilidad.

Nos vemos.

Guillermo

 


Código Xaml El código Xaml

En VB usa esta definición de la ventana:

<Window x:Class="MainWindow"

En C# usa esta definición de la ventana (o pon el nombre del espacio de nombres del proyecto delante de MainWindows):

<Window x:Class="WpfListView_cs.MainWindow"

 

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" 
    Height="380" Width="525">
    <Grid>
        <StackPanel Orientation="Vertical" Margin="10,10">
            <ListView Name="lvDatos" Margin="0" MinHeight="240"
                  VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
                  SelectionChanged="lvDatos_SelectionChanged" >
            <ListView.View>
                <GridView AllowsColumnReorder="True">
                    <!-- Utilizo valores en Binding distintos a los nombres de las cabeceras
                         para que se vea que no hay relación entre el nombre del Header
                         y el nombre de Binding -->
                        <GridViewColumn Header="Nombre y apellidos" 
                                        DisplayMemberBinding="{Binding Nombre}" Width="150"/>
                        <GridViewColumn Header="Correo" 
                                        DisplayMemberBinding="{Binding email}" Width="190"/>
                        <GridViewColumn Header="How old" 
                                        DisplayMemberBinding="{Binding Edad}" Width="70"/>
                </GridView>
            </ListView.View>
            </ListView>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">
                <Button Content="Rellenar" Click="Button_Click_1" />
                <Label />
                <Button Name="btnMostrar" Content="Mostrar seleccionado" 
                        IsEnabled="False" Click="btnMostrar_Click" />
                <Label />
                <CheckBox Name="chkAnonimo" IsChecked="True" 
                          Content="Usar tipo anónimo (si no, tipo definido)" />
            </StackPanel>
            <TextBlock Name="txtMostar" Text="Una fila"/>
            <TextBlock Name="txtMostar2" Text="Una fila"/>
        </StackPanel>
    </Grid>
</Window>

 

Código para Visual Basic.NET (VB.NET) El código para Visual Basic .NET
'------------------------------------------------------------------------------
' Ejemplo de ListView usando WPF para Desktop                       (27/Dic/12)
' y cómo acceder a las columnas (sin usar SubItems)
'
' ©Guillermo 'guille' Som, 2012
'------------------------------------------------------------------------------

Option Strict Off
Option Infer On

Class MainWindow

    Private Sub Button_Click_1(sender As Object, e As RoutedEventArgs)
        ' Rellenar el listview con datos
        lvDatos.Items.Clear()
        For i = 1 To 10

            '' Esto añadirá el mismo texto a todas las columnas
            '' siempre y cuando no hayamos usado ningún Binding
            '' si no, esto no añade nada de nada...
            '.Items.Add("Item número " & i.ToString("00"))


            If chkAnonimo.IsChecked Then
                ' Asignamos un tipo anónimo
                ' Si las columnas (GridViewColumn) tienen un Binding a las propiedades
                ' los valores se asignarán a cada columna, si no están enlazadas
                ' se agregará el valor de ToString a cada columna (estará el texto repetido)
                lvDatos.Items.Add(New With
                                  {.Nombre = "Nombre anónimo" & i.ToString,
                                   .email = "correo" & i.ToString & "@outlook.com",
                                   .Edad = 17 + i
                                  })

            Else
                ' Asignamos un valor de un tipo definido (Colega)
                ' que tiene como mínimo las propiedades que vamos a usar
                ' en el listiView

                lvDatos.Items.Add(New Colega With
                                  {.Nombre = "Nombre " & i.ToString,
                                   .email = "email" & i.ToString & "@outlook.com",
                                   .Edad = 17 + i
                                  })

            End If

        Next

    End Sub

    Private Sub btnMostrar_Click(sender As Object, e As RoutedEventArgs)
        ' Mostar en el TextBlock el elemento seleccionado

        ' Dependiendo de que sea el tipo anónimo o el definido
        ' el valor mostrado será diferente.
        ' En el caso de la clase Colega, si no tenemos sobrecargado el método ToString
        ' mostrará el nombre de la clase: WpfListView_vb.Colega
        txtMostar2.Text = lvDatos.SelectedItem.ToString

        If chkAnonimo.IsChecked Then
            ' Si tenemos Option Strict Off podemos hacer esto:
            Dim v = lvDatos.SelectedItem

            txtMostar.Text = v.Nombre & " (" & v.email & ")"

            ' El problema es que queramos acceder a una propiedad que no exista
            ' (esto dará error)
            'txtMostar.Text = v.Nombre & " (" & v.cagonto & ")"

        Else

            Dim v = TryCast(lvDatos.SelectedItem, Colega)

            txtMostar.Text = v.Nombre & " (" & v.email & ")"

            ' como ahora usamos un tipo previamente definido
            ' si usamos esto:
            '   txtMostar.Text = v.Nombre & " (" & v.cagonto & ")"
            ' nos dirá que "cagonto" no es un miembro de Colega

        End If

    End Sub

    Private Sub lvDatos_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
        ' habilitar según haya o no elementos seleccionados
        btnMostrar.IsEnabled = (lvDatos.SelectedItems.Count > 0)
    End Sub

End Class

''' <summary>
''' Tipo Colega, con las popiedades (o campos)
''' que tendrán las columnas del ListView.
''' Aunque puede tener más propiedades
''' aunque no estén enlazadas con las columnas
''' </summary>
Class Colega
    Public Property Nombre As String
    Public Property email As String
    Public Property Edad As Integer

    ' Podemos tener más propiedadas
    Public Property Apellidos As String
End Class

 

Código para C Sharp (C#) El código para C#
//-----------------------------------------------------------------------------
// Ejemplo de ListView usando WPF para Desktop                      (27/Dic/12)
// y cómo acceder a las columnas (sin usar SubItems)
//
// ©Guillermo 'guille' Som, 2012
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfListView_cs
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            // Rellenar el listview con datos
            lvDatos.Items.Clear();
            for (var i = 1; i <= 10; i++)
            {

                //' Esto añadirá el mismo texto a todas las columnas
                //' siempre y cuando no hayamos usado ningún Binding
                //' si no, esto no añade nada de nada...
                //.Items.Add("Item número " + i.ToString("00"));


                if ((bool)chkAnonimo.IsChecked)
                {
                    // Asignamos un tipo anónimo
                    // Si las columnas (GridViewColumn) tienen un Binding a las propiedades
                    // los valores se asignarán a cada columna, si no están enlazadas
                    // se agregará el valor de ToString a cada columna (estará el texto repetido)
                    lvDatos.Items.Add(new
                    {
                        Nombre = "Nombre anónimo" + i.ToString(),
                        email = "correo" + i.ToString() + "@outlook.com",
                        Edad = 17 + i
                    });

                }
                else
                {
                    // Asignamos un valor de un tipo definido (Colega)
                    // que tiene como mínimo las propiedades que vamos a usar
                    // en el listiView

                    lvDatos.Items.Add(new Colega
                    {
                        Nombre = "Nombre " + i.ToString(), 
                        email = "email" + i.ToString() + "@outlook.com", 
                        Edad = 17 + i
                    });
                }
            }

        }

        private void btnMostrar_Click(object sender, RoutedEventArgs e)
        {
            // Mostar en el TextBlock el elemento seleccionado

            // Dependiendo de que sea el tipo anónimo o el definido
            // el valor mostrado será diferente.
            // En el caso de la clase Colega, si no tenemos sobrecargado el método ToString
            // mostrará el nombre de la clase: WpfListView_vb.Colega
            txtMostar2.Text = lvDatos.SelectedItem.ToString();

            if ((bool)chkAnonimo.IsChecked)
            {
                // En C# debemos definir la variable como dynamic:
                dynamic v = lvDatos.SelectedItem;

                txtMostar.Text = v.Nombre + " (" + v.email + ")";

                // El problema es que queramos acceder a una propiedad que no exista
                // (esto dará error)
                //txtMostar.Text = v.Nombre + " (" + v.cagonto + ")";

            }
            else
            {
                
                var v = ((Colega)lvDatos.SelectedItem);

                txtMostar.Text = v.Nombre + " (" + v.email + ")";

                // como ahora usamos un tipo previamente definido
                // si usamos esto:
                //   txtMostar.Text = v.Nombre + " (" + v.cagonto + ")";
                // nos dirá que "cagonto" no es un miembro de Colega

            }
        }

        private void lvDatos_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // habilitar según haya o no elementos seleccionados
            btnMostrar.IsEnabled = (lvDatos.SelectedItems.Count > 0);
        }        

    }

    /// <summary>
    /// Tipo Colega, con las popiedades (o campos)
    /// que tendrán las columnas del ListView.
    /// Aunque puede tener más propiedades
    /// aunque no estén enlazadas con las columnas
    /// </summary>
    class Colega
    {
        public string Nombre { get; set; }
        public string email { get; set; }
        public int Edad { get; set; }

        // Podemos tener más propiedadas
        public string Apellidos { get; set; }
    }
}


Espacios de nombres usados en el código de este artículo:

System.Windows

System.Windows.Controls

 

 

El artículo original está en mi sitio, este de mi blog sólo es por si quieres hacer comentarios sobre lo aquí explicado.

El Windows 8 dice que…

 

 

Navidad all day

 

 

Pues eso…

¡ Feliz 25 de diciembre !

 

Nos vemos.
Guillermo

[FB] 24 Dic, 18:28 – esta noche es…

Publicado en Facebook el December 24, 2012 at 06:28PM:

pa quién lo celebre y esas cosas:

¡¡¡ FELIZ NAVIDAD !!!

 

 

Además el Windows 8 también nos recuerda qué día será "tomorrow"

Navidad: tomorrow all day

Nos vemos.
Guillermo

#delfeis #ifttt #elguilleinfo

Mostrar un menú contextual (aplicación para Windows Store)

Pues eso… voy a seguir mostrándote algunas de las cosas que voy aprendiendo mientras voy creando mi primera aplicación para la tienda de Windows. En esta ocasión lo que te voy a explicar es cómo mostrar un menú contextual (por llamarlo de alguna forma) al hacer Click en un botón.

Para mostrar el menú contextual vamos a usar las clases PopupMenu y UICommand que están definidas en el espacio de nombres Windows.UI.Popups, que es el mismo en el que está definida la clase MessageDialog que ya vimos que sirve para mostrar mensaje "al estilo Windows 8".

Seguiremos con el código que vimos por última vez el 7 de diciembre en: Saber cuando la aplicación está en modo Snapped y actuar en consecuencia.

También usaremos el código para actualizar periódicamente el icono de la aplicación que ya vimos en Actualizar cada minuto el icono (tile) de una aplicación de Windows Store hasta la hora indicada, es decir el método asignarTiles, por tanto ese método no te lo mostraré en este artículo, más que nada para no repetir el código.

Lo primero que haremos es añadir un botón a la barra de la aplicación (AppBar) y le asignaremos el siguiente estilo: (el cual guardaremos en el fichero StandardStyles.xaml junto con el de los otros botones)

<Style x:Key="LangAppBarButtonStyle" TargetType="ButtonBase" 
       BasedOn="{StaticResource AppBarButtonStyle}">
    <Setter Property="AutomationProperties.AutomationId" Value="LangAppBarButton"/>
    <Setter Property="AutomationProperties.Name" Value="Idioma"/>
    <Setter Property="Content" Value="&#xE128;"/>
</Style>

Ahora añadimos el código XAML del botón, éste lo pondremos en el segundo StackPanel, es decir, junto con el botón About:

<Button x:Name="buttonLang" Click="buttonLang_Click"
        Style="{StaticResource LangAppBarButtonStyle}" />

Una vez hecho esto, nuestra barra de botones tendrá la apariencia mostrada en la figura 1:

menucontextual_01

Figura 1. La barra de botones de la aplicación reloj

Después de ver la captura sabrás que el nombre del botón es buttonLang y el método del evento Click será: buttonLang_Click.

Veamos el código de ese método de evento, además de otro método que usaremos para saber cuál es el menú elegido (ese código, así como el código para mostrar el menú contextual por medio de la clase PopupMenu está sacado de un ejemplo del SDK de Windows: Context menu sample).

También usaremos una serie de variables para los textos a mostrar en las opciones del menú y otra más para asignarle el valor que corresponda al idioma seleccionado en ese menú contextual.

Lo que no veremos (por ahora) son un par de métodos en los que se asignan las cadenas a los controles con el texto del idioma que hemos elegido, ya que para hacer eso antes tenemos que añadir una serie de recursos con los textos en español e inglés que serán los dos idiomas soportados por esta aplicación.

Lo que me interesa es que sepas cómo mostrar ese menú contextual, cómo interceptar el menú seleccionado y actuar en consecuencia.

En este ejemplo concreto los métodos usados para hacer la asignación que corresponde según la opción elegida, los crearemos de forma anónima, es decir, será una expresión lambda en lugar de un método de evento real, ahora lo veremos en el código.

Empecemos por las declaraciones de las variables y el método getElementRect que es el que se encarga de indicar qué opción del menú es la que se ha elegido (si es que se ha elegido alguna).

VB:

''' <summary>El idioma a usar</summary>
Private culLanguage As String = "es"

''' <summary>Contiene el texto para Español</summary>
Private culEs As String = "Español"
''' <summary>Contiene el texto para Inglés</summary>
Private culEn As String = "Inglés"


''' <summary>
''' Para saber el menú elegido
''' </summary>
''' <remarks>
''' Del ejemplo de la SDK: Context menu sample
''' </remarks>
Private Function getElementRect(element As FrameworkElement) As Rect
    Dim buttonTransform As GeneralTransform = element.TransformToVisual(Nothing)
    Dim point As Point = buttonTransform.TransformPoint(New Point)
    Return New Rect(point, New Size(element.ActualWidth, element.ActualHeight))
End Function

 

C#:

/// <summary>El idioma a usar</summary>
private string culLanguage = "es";

/// <summary>Contiene el texto para Español</summary>
private string culEs = "Español";
/// <summary>Contiene el texto para Inglés</summary>
private string culEn = "Inglés";


private static Rect getElementRect(FrameworkElement element)
{
    GeneralTransform buttonTransform = element.TransformToVisual(null);
    Point point = buttonTransform.TransformPoint(new Point());
    return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
}

Ahora le toca el turno al método del evento clic del botón de idioma.

Empezaremos creando una variable del tipo PopupMenu a la que le añadimos dos opciones (menús o comandos). La forma de añadirlo es mediante el método Add de la propiedad/colección Commands, Lo que espera ese método es un objeto del tipo UICommand. Así que, lo creamos directamente en la asignación, y en la sobrecarga que estamos utilizando los parámetros que el constructor de los comandos espera recibir son: el texto a mostrar y la dirección (puntero) de un método que cumpla con el delegado UICommandInvokedHandler.

Como ya te comenté antes, el método para ese segundo parámetro lo vamos a definir directamente, es decir, como un método anónimo o función lambda. Y en ese "método" lo que haremos es simplemente asignar un valor a una variable para saber qué idioma usar. Que el usuario elige el primer menú, asignamos "es", que elige el segundo, asignamos "en".

Veamos el código del método buttonLang_Click tanto para Visual Basic como para C#:

VB:

''' <summary>
''' Al pulsar en el botón, mostrar el menú contextual
''' En otros casos se puede usar el evento RightTapped
''' </summary>
Private Async Sub buttonLang_Click(sender As Object, e As RoutedEventArgs)
    Dim menu = New PopupMenu
    Dim sLang As String = "es"

    menu.Commands.Add(New UICommand(culEs, Sub(command) sLang = "es"))
    menu.Commands.Add(New UICommand(culEn, Sub(command) sLang = "en"))

    Dim chosenCommand = Await menu.ShowForSelectionAsync(
                                Me.getElementRect(TryCast(sender, FrameworkElement)))

    ' The command is null if no command was invoked.
    If chosenCommand IsNot Nothing Then

        culLanguage = sLang

        'guardarConfig()

        'cambiarIdioma()

        '' Eliminar las notificaciones anteriores y usar nuevas
        '' para que se utilice el nuevo idioma indicado
        'borrarTileNotifications()

        asignarTiles()

    End If
End Sub

 

C#:

/// <summary>
/// Al pulsar en el botón, mostrar el menú contextual
/// En otros casos se puede usar el evento RightTapped
/// </summary>
private async void buttonLang_Click(object sender, RoutedEventArgs e)
{
    var menu = new PopupMenu();
    string sLang = "es";

    menu.Commands.Add(new UICommand(culEs, (command) => sLang = "es"));
    menu.Commands.Add(new UICommand(culEn, (command) => sLang = "en"));

    var chosenCommand = await menu.ShowForSelectionAsync(
                                    getElementRect((FrameworkElement)sender));


    // The command is null if no command was invoked.
    if (chosenCommand != null)
    {
        culLanguage = sLang;

        //guardarConfig();

        //cambiarIdioma();

        //' Eliminar las notificaciones anteriores y usar nuevas
        //' para que se utilice el nuevo idioma indicado
        //borrarTileNotifications();

        asignarTiles();
    }
}

 

Y una vez que tenemos todo esto, si iniciamos la aplicación y pulsamos en la pantalla con el botón derecho (o secundario) del ratón (o mouse) veremos las dos opciones mostradas por encima del botón, tal como podemos apreciar en la figura 2:

menucontextual_02

Figura 2: La aplicación en funcionamiento dentro del emulador / simulador de un Windows 8 en un tablet.

 

En otra ocasión veremos el código de guardar (y leer) la configuración y un par de detalles a tener en cuenta ya que (como estarás comprobando) casi todos los métodos que utilizamos en las aplicaciones para la tienda de Windows son asíncronos y en ocasiones hay que "esperar" a que uno de esos métodos termine antes de seguir con el siguiente.

También veremos cómo cambiar los textos según el idioma elegido y por supuesto el método ese para borrar las notificaciones que hubiese pendientes de mostrar, ya que al cambiar el idioma el texto de los días y meses se muestran distintos en español que en inglés.

Ah, y el código de asignarTiles recuerda que el de VB ya estaba publicado en Actualizar cada minuto el icono (tile) de una aplicación de Windows Store hasta la hora indicada y ahora ya está también publicado el de C#.

 

Y ya que tenemos ese método para que "automatice" las notificaciones en el icono (tile) de la aplicación, hay que modificar el código que ya tenemos, con idea de que se utilice ese nuevo código.

Esos cambios te los mostraré en la próxima ocasión, cuando te muestre el código de borrarTileNotifications.

¡Hasta entonces!

 

Más abajo tienes los enlaces al resto de artículos de esta serie.

¡Espero que te sean de utilidad!

 

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

Saber la versión de una aplicación de Windows Store

 

Pues eso… quería mostrar la versión de mi aplicación en la página de "acerca de" y al buscar en Google (aparte de ver que tiene un doodle para el 13º Baktún de los mayas) me he encontrado con un artículo/truco de Caio Proiete en el que lo muestra para C# y JavaScript, y como yo suelo usar Visual Basic como lenguaje de programación, pues… lo he convertido, que no es que sea muy complicado hacerlo (lo de convertir de C# a VB) así que… aquí lo tienes tal y como yo lo estoy usando.

 

Dim version = Windows.ApplicationModel.Package.Current.Id.Version
Dim sVersion = String.Format("{0}.{1}.{2}.{3}",
                             version.Major, version.Minor,
                             version.Build, version.Revision)

 

Espero que te sea de utilidad.

 

Nos vemos.
Guillermo