Etiqueta: truco

  • hay que ver… como es el Format con las llaves…

     

    Pues eso… que si usas Format, en cualquiera de sus encarnaciones: String.Format, StringBuilder.AppendFormat, Console.WriteLine, (incluso la función Format de Visual Basic) etc. y estas usando llaves para indicar parámetros (marcadores de posición que dicen en la documentación), mucho cuidado con que la cadena que estás formateando no tenga también llaves, porque entonces la hemos "pifiao".

     

    El problema

     

    Por ejemplo, si tienes esta cadena (o este código XAML):

     

    Dim xaml = <StackPanel Style="{StaticResource spImagen}">
                   <Image Source="ms-appx:///contenido/img/{0}"
                       Style="{StaticResource Imagen}"/>
               </StackPanel>
    
    

     

    Y pretendes usarlo con algo como esto:

    VB:

     

    sb.AppendFormat(xaml.ToString & "{1}",
                    sText.Replace(".tif", ".jpg"), vbCrLf)
    

     

    C#:

    Nota:

    En C# no se puede usar el contenido XML directamente, pero no es complicado adaptarlo, eso sí, es algo más laborioso que en Visual Basic, todo hay que decirlo. Pero para este caso que es poco código lo pasaremos por alto 😉

     

    string xaml = @"<StackPanel Style=""{StaticResource spImagen}"">
                        <Image Source=""ms-appx:///contenido/img/{0}""
                            Style=""{StaticResource Imagen}""/>
                    </StackPanel>";
    
    sb.AppendFormat(xaml + "\n",
                    sText.Replace(".tif", ".jpg"));
    

     

    Si usamos este código (ya sea con VB o con C#) el compilador producirá una excepción de formato no válido (FormatException) y es porque el "formateador" se encuentra con llaves (de inicio y/o de cierre) que no tienen un valor numérico asignado.

     

    FormatException

    Figura 1. El error en la aplicación de C# (es el mismo que en VB)

     

     

    La solución

     

    Pues… quitar las cadenas con las llaves que no sean "marcadores" y agregarlas en el propio AppendFormat:

     

    VB:

     

    Dim xaml = <Paragraph TextAlignment="Center">
                   <InlineUIContainer>
                       <StackPanel Style="{0}">
                           <Image Source="ms-appx:///contenido/img/{1}"
                               Style="{2}"/>
                       </StackPanel>
                   </InlineUIContainer>
               </Paragraph>
    
    sb.AppendFormat(xaml.ToString & "{3}",
                    "{StaticResource spImagen}",
                    sText.Replace(".tif", ".jpg"),
                    "{StaticResource Imagen}", vbCrLf)
    

     

    C#:

     

    string xaml = @"<StackPanel Style=""{0}"">
                        <Image Source=""ms-appx:///contenido/img/{1}""
                            Style=""{2}""/>
                    </StackPanel>";
    
    sb.AppendFormat(xaml + "\n","{StaticResource spImagen}",
                    sText.Replace(".tif", ".jpg"), "{StaticResource Imagen}");
    

     

    Es decir, ponemos para cada una de las "llaves fatídicas" una cadena con un marcador (de posición) y ponemos esas cadena (con sus llaves) en los parámetros de la función Format y asunto solucionado.

     

    Nota del Guille:

    Mira que esto mismo ya me pasó ayer (o hace un puñao de horas) y después me volvió a pasar, pero he estado un buen rato dándole vueltas al tema (se me olvidó que volví a cambiar el código XAML) y no daba con el dichoso error… eso me pasa por dos razones:

    1.- no tener buena memoria, no dormir lo suficiente, ser más cabezón que el Visual Studio

    2.- no usar suficientes Try/catch para "acorralar" el error…

    En fin… lo importante es que he dado con el error y que te lo cuento por si te pasa a ti.

     

    Si todo va bien, ya mismo te explicaré para qué estoy usando ese código (el de Visual Basic, ya que el de C# sólo lo he escrito para aquellos que les gustan los puntos y comas)…

     

    Lo dicho, espero que te pueda ser de ayuda… esa siempre es la intención 😉

     

    Nos vemos.

    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

  • Asignar el foco a un control al cargar una página web aspx (ampliación y recapitulación)

    Pues eso… que lo de "ampliación" es por no poner "revisited" o algo similar que eso ya está muy visto, jeje y lo de "recapitulación" por el chasco/descubrimiento/formas diferentes de funcionar con el que me he encontrado al hacer pruebas en VB y en C#, ya verás, ya…

    Pero en realidad esto es una aclaración o más bien un añadido (o ampliación) del otro artículo que publiqué el pasado 11 de octubre sobre este mismo tema, es decir, cómo asignar el foco a un control al cargar una página web aspx, en ese caso el código era con Visual Basic .NET pero lo importante no era el código si no lo explicado, es decir la forma de conseguir eso que indica el título.

    La aclaración es porque en ese ejemplo el código y el diseño está todo en un mismo fichero (archivo) en lugar de tener el código por un lado y el diseño por otro, que es como el Visual Studio se empeña que lo hagamos, aunque en mi caso particular me gusta más (o prefiero) tener todo junto, ya que así es más fácil de actualizar la página en concreto, al menos para mí, que todo puede ser que a tí te venga mejor compilar el código y publicarlo cada vez que hagas una modificación por pequeña que sea… ¡hay gustos para todo! 😉

     

    Vamos a lo que vamos.

    Si utilizas el código por separado del diseño de la página web entonces no es necesario asignar un valor true a la propiedad AutoEventWireup, ese valor verdadero sólo debes asignarlo si el código a usar está "incrustado" en la página web ASPX y no en un fichero separado (el cual hay que compilar y publicar en el sitio web).

     

    La forma de indicar si tienes el código "incrustado" o por separado es usando (lo que se llama "code behind"), esto lo puedes indicar al crear un nuevo fichero en el proyecto web tal como puedes ver en la figura 1 en la que he resaltado la parte que le indica a Visual Studio si quieres usar el código en un fichero separado o no (en este ejemplo SI se usa por separado).

    orden_tabulacion_fig1
    Figura 1. Indicar si queremos el código por separado o no

     

    Esto creará dos ficheros uno con la extensión .aspx (el diseño de la página web) y otro con la extensión del lenguaje usado, en este ejemplo al indicar que el código es de C# la extensión del fichero de código es: .aspx.cs.

    En este caso, el código de "enfoque" del control que queremos que sea el primero en recibir el foco lo pondremos dentro del evento Page_Load tal como podemos ver en los dos listados (el del diseñador y el código).
    (Ver el comentario de la nota).

     

    Listado 1 (el código del diseñador .aspx):

     

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default3.aspx.cs" Inherits="Default3" %>
    
    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Default3 - con el código por separado (en C#)</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
            <asp:TextBox ID="TextBox1" runat="server">uno</asp:TextBox><br />
            <asp:Label ID="Label2" runat="server" Text="Label"></asp:Label>
            <asp:TextBox ID="TextBox2" runat="server">dos</asp:TextBox><br />
            <asp:Label ID="Label3" runat="server" Text="Label"></asp:Label>
            <asp:TextBox ID="TextBox3" runat="server">tres</asp:TextBox><br />
            <asp:Button ID="Button1" runat="server" Text="Button" />    
        </div>
        </form>
    </body>
    </html>

     

    Listado 2 (el código de C# .aspx.cs):

     

    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    
    public partial class Default3 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            this.TextBox1.Focus();
        }
    

     

     

    Nota:

    Es curioso, pero al hacer la prueba con C# para "demostrar" todo esto que estoy explicando, si no uso un valor verdadero (true) en la propiedad AutoEventWireup de la página de C# no se ejecuta ese código del evento Page_Load, sin embargo (de ahí lo de curioso) en el código de Visual Basic da igual el valor que tenga esa propiedad… ¡jum!

     

     

    Conclusión:

    Que no tengo ni idea de por qué pasa esto con una página de C#… así que… sigue usando AutoEventWireup = "true" y así seguro que siempre te funciona…

    Como te he dicho antes (en la nota) en Visual Basic .NET si va bien, uses true o false en AutoEventWireup, eso sí, siempre que el código esté por separado, si el código está en la misma página, si hay que indicar el valor true.

    En cualquier caso, lo cierto es que al añadir la página de C# el valor de la propiedad AutoEventWireup tiene el valor true, mientras que en Visual Basic es false.

    Todo esto (no solo que el valor sea verdadero/falso) es otra demostración de que a los de Visual Basic nos tratan de forma diferente…

    En fin…

     

    Espero que te sea de utilidad.

     

    Nos vemos.

    Guillermo