Fantasmas en el código | De WebClient, Silverlight, UserAgents, WebApi y “gatitos que mueren”

posterEste no es un artículo de los que tengo acostumbrado escribir, este artículo es una reflexión-desahogo en la que busco opiniones y consejos de aquellos que tengan los conocimientos y/o las ganas de ilustrarme/nos sobre uno de esos fantasmas con los que me he encontrado a lo largo de mi carrera.

Bueno, al lío. Todo comenzó con la necesidad de usar Silverlight, sí, esa tecnología que parece destinada a quedar en desuso, en una aplicación web ASP.NET MVC y consumiendo datos desde un proyecto WebApi. Hasta aquí toda la mezcla puede parecer normal, ejecuto la aplicación ASP.NET MVC, accedo a la vista con el control Silverlight y… ¡todo correcto! El control muestra todos los datos correctamente y la aplicación funciona perfectamente… ¿o no?

Todo desarrollador web sabe que antes de dar por concluido el desarrollo de una aplicación debe probarla en todos los navegadores y… no señores, esta vez no es IE. En este caso es Firefox el que nos da problemas. ¡Vaya por diox! Resulta que en el navegador del zorrillo, no se me muestran datos porque la respuesta a la petición que he hecho con WebClient retorna un error porque estoy esperando un JSON y resulta que me llega un XML. Surge la pregunta de Mourinho… ¿Por qué?

Una vez puesto el mono de trabajo, abro Fiddler y veo que el parámetro de cabecera “Accept” que se envía desde Firefox es este churrasco que intenta primero obtener HTML, después xhtml, en tercer lugar XML y por último que acepte lo que venga.

image

 

Veamos qué en Chrome ni siquiera se establece ese parámetro.

image

 

Y finalmente IE que establece */*, o lo que es lo mismo, acepta todo.

image

 

Primer intento de solución

Mi primera intención fue forzar el parámetro Accept a */* pero tras ver que no tengo el método “Add” para la colección “Headers” indago un poco y encuentro que para Silverlight se establecen una serie de restricciones de los parámetros de la cabecera y que no podemos modificarlos, entre ellos “Accept” tal y como se especifica en la sección “Comentarios” de este artículo de MSDN.

Y digo yo… pero ¿y qué más da el navegador? Pues bien, resulta que la documentación de Silverlight indica que se establece por el navegador que contiene la aplicación de Silverlight, he aquí el artículo de MSDN que lo explica.

 

Segundo intento de solución

Usar HttpWebRequest pero sólo obtuve los mismos resultados y problemas.

 

Solución rápida (sólo para mi)

Podemos cambiar la configuración de Firefox para que el parámetro “Accept” admita antes JSON que XML tal y como se indica en este artículo pero… esto sólo me vale para mi, si la aplicación la va a usar más de… una persona (yo mismo) ya no mola.

Entonces… ¿qué hacer?

 

Solución aplicada

Una vez me cansé de intentar buscar la forma de establecer desde la aplicación cliente el formato en el que quería recibir los datos (JSON), pensé que puedo ir a WebApi y establecer/forzar que me envíe los datos sólo en el formato que quiero.

NOTA: No me gusta nada esta solución porque pierdo la flexibilidad de WebApi. ¿Y si mañana quiero los datos también en XML?

Gracias a la ayuda de un compañero, encontré que podía forzarlo añadiendo una única línea en el método Application_Start del Global.asax del proyecto WebApi

GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();

 

La misma solución parece un poco más elegante haciéndolo como se indica en este post de StackOverflow pero es más de lo mismo.

 

La mejor aproximación a la solución es la que se comenta en este artículo que he encontrado pero volvemos a reducirlo a lo mismo.

 

Vuestra recomendación

Como ya dije al inicio del artículo, espero vuestras recomendaciones, indicaciones y/o palos diciendo ¡eres un inútil! ¡eso no se hace así! ¿Quién es el primero?

 

Enjoy y recuerda que “cada vez que aparece un fantasma en el código, muere un gatito”

La odisea de cómo pasar de String a Decimal o Double

posterHoy me gustaría hacer un inciso para hablar de un problema recurrente, de esos como los de convertir o tratar fechas… vamos, un problema que a priori es una tontería pero que nos puede llevar unas cuántas horas de trabajo, y no es otro que… pasar de string a decimal o a double

Escenario

Tenemos una fuente de datos, llámese base de datos, xml, json,…que debemos recibir y parsear en nuestra aplicación .net que incluye un campo de tipo decimal o double y necesitamos parsearlo.

Cualquiera de nosotros lo tendría claro… hacer un parseo con cualquiera de los métodos que hay:

  • decimal.Parse(string)
  • Convert.ToDecimal(string)
  • double.Parse(string)
  • Convert.ToDouble(string)

En principio todos esperamos que esto funcione tal y como podéis comprobar ¿o no?

            string myDecimal = "0,123456789";
            Console.WriteLine("n** Cadena recibida: " + myDecimal + "n");
 
            if (!string.IsNullOrEmpty(myDecimal))
            {
                decimal attempt1 = decimal.Parse(myDecimal);
                Console.WriteLine("1) decimal.Parse(myDecimal) => " + attempt1);
 
                decimal attempt2 = Convert.ToDecimal(myDecimal);
                Console.WriteLine("2) Convert.ToDecimal(myDecimal) => " + attempt2);
 
                double attempt3 = double.Parse(myDecimal);
                Console.WriteLine("3) double.Parse(myDecimal) => " + attempt3);
 
                double attempt4 = Convert.ToDouble(myDecimal);
                Console.WriteLine("4) Convert.ToDouble(myDecimal) => " + attempt4);
            }
            else
            {
                Console.WriteLine("* Valor nulo => 0 ");
            }
            Console.ReadLine();

 

Si ejecutamos este pedazo de código, efectivamente funcionará y en cada una de las líneas mostrará el valor correcto.

image

Yujuuuuu… Pero hay algo que se nos escapa y no lo estamos viendo venir porque nos empeñamos una y otra vez en pensar que los datos siempre van a venir como en el ejemplo de turno que teníamos.

 

Problema

Hasta aquí todo ha ido muy bien, somos unos campeones de tomo y lomo y seguimos programando como si no hubiera mañana pero… se nos ha escapado que la fuente de datos no tiene por qué ser nuestra y debemos controlar que todos los datos estén bien recogidos y parseados. ¿Cómo,  no estaba hecho ya? Pues no. Hay algo muy importante que se debe tener en cuenta y es la “cultura” del origen de datos con lo que… en el ejemplo anterior, en vez de “0,123456789” puede que hayamos recibido la cadena “0.123456789” y aquí ya se joroba todo

image

 

Oh my god!! ¿Y esto por qué pasa?

La explicación es sencilla, como la cultura que tenemos definida en nuestra máquina de desarrollo define que los decimales se separan con una coma “,” y las unidades de millar con un punto “.”, al hacer el parseo, la aplicación entiende que ese punto es un separador de miles y… nos ha jorobado el día porque a veces no es tan fácil de determinar como en esta “exageración” de decimal que he puesto, pero imaginad que tenéis 137,4 por ejemplo. Si te pilla despistado te puede tener un día entero pensando por qué en el listado que le muestras al usuario salen números que no cuadran con la realidad.

 

Solución

La solución pasa por pensar que no somos las únicas personas existentes sobre el planeta tierra y que los datos no nos van a venir siempre como queremos, así que tenemos que decirle al sistema de qué cultura vienen nuestros datos. Para ello, basta con mirar la documentación que Microsoft nos aporta sobre Convert.ToDecimal(string, IFormatProvider) y de Convert.ToDouble(string, IFormatProvider) donde se nos indica que basta con establecer la cultura de origen de los datos mediante CultureInfo culture = new CultureInfo(IDENTIFICADOR_DE_CULTURA); y pasárselo al método que estemos usando del Convert. Así pues… el código nos podría quedar así:

NOTA: No os olvidéis de añadir la referencia a System.Globalization

            string myDecimal = "0.123456789";
            Console.WriteLine("n** Cadena recibida: " + myDecimal + "n");
 
            CultureInfo culture = new CultureInfo("en-US");
            if (!string.IsNullOrEmpty(myDecimal))
            {
                decimal attempt1 = decimal.Parse(myDecimal, culture);
                Console.WriteLine("1) decimal.Parse(myDecimal) => " + attempt1);
 
                decimal attempt2 = Convert.ToDecimal(myDecimal, culture);
                Console.WriteLine("2) Convert.ToDecimal(myDecimal) => " + attempt2);
 
                double attempt3 = double.Parse(myDecimal, culture);
                Console.WriteLine("3) double.Parse(myDecimal) => " + attempt3);
 
                double attempt4 = Convert.ToDouble(myDecimal, culture);
                Console.WriteLine("4) Convert.ToDouble(myDecimal) => " + attempt4);
            }
            else
            {
                Console.WriteLine("* Valor nulo => 0 ");
            }
            Console.ReadLine();

 

Comprobamos y… voilà funciona de nuevo

image

NOTA: Tengamos en cuenta que debemos conocer la cultura en la que recibimos los datos.

 

Solución +

Pero vamos a ir más allá… y, si seguimos con la idea en la mente de que si quien nos da los datos, nos los da con un formato, seguramente querrá verlos con el mismo formato. Esto vamos a hacerlo mediante el método ToString(IFormatProvider) donde podemos pasarle la cultura que tenemos definida.

            string myDecimal = "0.123456789";
            Console.WriteLine("n** Cadena recibida: " + myDecimal + "n");
 
            CultureInfo culture = new CultureInfo("en-US");
 
            if (!string.IsNullOrEmpty(myDecimal))
            {
                decimal attempt1 = decimal.Parse(myDecimal, culture);
                Console.WriteLine("1) decimal.Parse(myDecimal) => " + attempt1.ToString(culture));
 
                decimal attempt2 = Convert.ToDecimal(myDecimal, culture);
                Console.WriteLine("2) Convert.ToDecimal(myDecimal) => " + attempt2.ToString(culture));
 
                double attempt3 = double.Parse(myDecimal, culture);
                Console.WriteLine("3) double.Parse(myDecimal) => " + attempt3.ToString(culture));
 
                double attempt4 = Convert.ToDouble(myDecimal, culture);
                Console.WriteLine("4) Convert.ToDouble(myDecimal) => " + attempt4.ToString(culture));
            }
            else
            {
                Console.WriteLine("* Valor nulo => 0 ");
            }
            Console.ReadLine();

Y ahora el usuario podrá ver los datos tal y como los envió

image

 

Solución ++

Ahora que nos estamos recreando y podemos decir que somos unos auténticos cracks, que tenemos en cuenta la cultura de los datos y blablablabla… vamos a dar algo de mejora visual al usuario y nos vamos a crear nuestro propio formato numérico para devolver por ejemplo… esos decimales en formato Moneda y en formato Numérico (Tabla de formatos). Así pues, creemos nuestro propio NumberFormatInfo.

            string myDecimal = "12,3456.123456789";
            Console.WriteLine("n** Cadena recibida: " + myDecimal + "n");
 
            CultureInfo culture = new CultureInfo("en-US");
 
            NumberFormatInfo nfi = new CultureInfo("en-US", true).NumberFormat;
            nfi.NegativeSign = "(-)";
            nfi.CurrencyDecimalDigits = 2;
            nfi.NumberDecimalDigits = 3;
 
            if (!string.IsNullOrEmpty(myDecimal))
            {
                decimal attempt1 = decimal.Parse(myDecimal, culture);
                Console.WriteLine("1) decimal.Parse(myDecimal) => " + attempt1.ToString("c", nfi) + "  AND NEGATIVE  " + (-1*attempt1).ToString("c", nfi));
 
                decimal attempt2 = Convert.ToDecimal(myDecimal, culture);
                Console.WriteLine("2) Convert.ToDecimal(myDecimal) => " + attempt2.ToString("c", nfi) + "  AND NEGATIVE  " + (-1 * attempt2).ToString("c", nfi));
 
                double attempt3 = double.Parse(myDecimal, culture);
                Console.WriteLine("3) double.Parse(myDecimal) => " + attempt3.ToString("n", nfi) + "  AND NEGATIVE  " + (-1 * attempt3).ToString("n", nfi));
 
                double attempt4 = Convert.ToDouble(myDecimal, culture);
                Console.WriteLine("4) Convert.ToDouble(myDecimal) => " + attempt4.ToString("n", nfi) + "  AND NEGATIVE  " + (-1 * attempt4).ToString("n", nfi));
            }
            else
            {
                Console.WriteLine("* Valor nulo => 0 ");
            }
            Console.ReadLine();

 

Como podréis observar, he introducido los siguientes cambios:

  • Crear NumberFormatInfo
    • Definir el signo negativo del formato numérico
    • Definir el número de decimales para el formato numérico
    • Definir el número de decimales para el formato moneda
  • Las dos primeras escrituras en consola lo harán con el formato Moneda – “c”  usando el NumberFormatInfo que he creado
  • Las dos últimas escrituras en consola lo harán con el formato Numértico – “n” usando el NumberFormatInfo que he creado
  • Para todas las salidas muestro su negativo

 

El resultado final es este:

image

 

Conclusión

No podemos olvidarnos de los usuarios ya que, al fin y al cabo, son los que consumen nuestras aplicaciones y los que nos dan de comer, así que tengamos en cuenta desde donde obtenemos los datos y cómo los vamos a mostrar para que los usuarios siempre reciban la información de la mejor forma posible.

 

Espero que os haya gustado. Enjoy!

ASP.NET MVC | Obtener controlador actual desde una vista (Tips)

posterHoy os traigo un pequeño Tip que, a priori puede ser un poco tonto pero que a más de uno le puede salvar de horas de modificar código o de buscar la aguja en el pajar.

Escenario

Tenemos una aplicación ASP.NET MVC que tiene, dentro de las vistas, llamadas “Ajax” a acciones de la propia aplicación MVC, lo que implicará que tengamos puesto “a fuego” el controlador que queremos llamar.

Ejemplo:

$.ajax({
    url: "/MyController/MyAction",
    data: { id: myId },
    type: "GET"
}).done(function(){
    alert('DONE!!');
});
 

NOTA: Siempre hay otras formas de hacerlo más elegante.

 

Problema

Qué ocurriría si:

  • Cambiamos la acción a otro controlador
  • Cambiamos el nombre del controlador
  • Usamos la gran herramienta de todo developer “Copy/Paste” y ponemos este método en otras vistas de otros controladores

Bueno, para todas las opciones, lo que ocurre es que tendremos que estar atentos de cambiar el controlador en la “url” que estamos usando en la llamada “ajax” y esto a veces se nos puede pasar por alto y tenernos un buen rato buscando la solución al problema de que de repente no nos funcione nuestra llamada.

 

Solución

Puede haber muchas soluciones, unas más elegantes que otras como por ejemplo he visto el tener un fichero de recursos con las acciones y sus “url” correctas lo que nos permitiría tener centralizadas todas las posibles llamadas que hagamos y tan sólo tendríamos que pasarle desde el controller las acciones que va a llamar la vista. Pero no es eso lo que yo voy a exponer, sino que en este caso quiero explicar cómo hacer referencia al controlador actual desde la propia vista, con lo que nos ahorraríamos los problemas de un cambio en el controlador.

¿Y cómo conseguimos saber el controlador actual? Pues con una sencilla línea de código.

@HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString()

 

Con esto, nuestra llamada Ajax podría quedar de la siguiente forma:

$.ajax({
    url: '/@HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString()/MyAction',
    data: { id: myId },
    type: "GET"
}).done(function(){
    alert('DONE!!');
});

 

NOTA: A algunos os podrá extrañar ver el código “razor” dentro de un bloque JavaScript… ¿funcionará? La respuesta es que sí, puesto que razor es capaz de identificar casi siempre el código de que le corresponde después de una @, lo que nos permite hacer cosas tan “fascinantes” y productivas como esta. Algo similar se expone en este artículo. Pero tened cuidado con no abusar de incluir código razor dentro del código JavaScript que al final puede ser peor el remedio que la enfermedad.

 

Update

Como bien apunta Javier Torrecilla, por buenas prácticas debería utilizar Url.Action para referir la url, con lo que para ser estrictamente correctos, deberíamos hacerlo de la siguiente forma.

$.ajax({
    url: '@Url.Action("MyAction", HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString())',
    data: { id: myId },
    type: "GET"
}).done(function(){
    alert('DONE!!');
});

 

Update 2

Un nuevo apunte de la mano de maese Eduard Tomàs que nos indica “intentar evitar la referencia binaria a HttpContext.Current siempre que se pueda y acceder a los route values a través de PageContext” con @PageContext.Page.Request.RequestContext.RouteData.Values["controller"].ToString() quedándonos de la siguiente forma nuestro código.

 

$.ajax({
    url: '@Url.Action("MyAction", PageContext.Page.Request.RequestContext.RouteData.Values["controller"].ToString())',
    data: { id: myId },
    type: "GET"
}).done(function(){
    alert('DONE!!');
});

Webcast. Charla con los expertos: Todo lo que siempre quisiste saber sobre SharePoint, pero no te atreviste a preguntar

Como extra a un comienzo de año lleno de expectativas, eventos, participaciones con la comunidad, etc. esta tarde tendré el gran placer de formar parte del equipo que estará presente en el Webcast “Charla con los expertos: Todo lo que siempre quisiste saber sobre SharePoint, pero no te atreviste a preguntar” junto a los grandes maestros hispanohablantes de todo el mundo. ¿A qué esperas para preguntar?

Los expertos que estarán disponibles para resolver vuestras dudas serán:

  • Gustavo Vélez
  • Juan Carlos González
  • Fabián Imaz
  • Alberto Díaz
  • David Martos
  • Miguel Tabera
  • Mario Cortes
  • Edin Kapic
  • Gerardo Reyes
  • Ricardo Muñoz
  • Juan Manuel Herrera
  • Juan Andrés Valenzuela
  • Haarón González
  • Juan Pablo Pussacq
  • Andrés Rojas
  • Vielka Rojas
  • Adrian Díaz
  • David Sánchez
  • Santiago Porras

 

Datos del Webcast

Inicio: jueves, 13 de febrero de 2014 16:00

Zona horaria: (GMT+01:00) Madrid, París

Duración: 1 horas 30 minutos

Enlace: enlace.

 

 

comunidades

JavaScript | Cómo Imprimir página web usando acción del navegador

En este artículo os voy a mostrar un tip muy sencillo.

Escenario

Se puede dar el caso en el que algún día debáis poner una acción en algún sitio web que tenga como requisito poder imprimir el contenido sin necesidad de generar un documento formateado por ejemplo en PDF.

 

Solución

Para proveer esta característica, lo mejor es recurrir a JavaScript dado que es una acción de cliente y dado que los datos están ya en el navegador, no necesitamos de enviar nada al servidor. Para realizar esta acción, el código que debemos escribir no podría ser más sencillo. Voy a ilustrarlo con un ejemplo haciendo uso de un «link» que ejecute código JavaScript «unobstrusive».

   1:  <html lang="en">
   2:      <head>
   3:          <title></title>
   4:          <script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script>
   5:      </head>
   6:      <body>
   7:          <a href="#" id="btnPrint">Imprimir</a>
   8:          <script type="text/javascript">
   9:              $("#btnPrint").click(function () {
  10:                  window.print();
  11:              });
  12:          </script>
  13:      </body>
  14:  </html>

 

Como podéis ver, hago uso de jQuery para asignar una función anónima como evento «click» y dentro de esa función hago una llamada al método window.print() de JavaScript. Con esto, cuando un usuario seleccione el enlace, se lanzará la acción de imprimir del navegador que se esté usando.

JavaScript-Print

 

Enjoy it 🙂

Disponibles los materiales del Webcast SharePoint y los dispositivos móviles (W8, WP8, iOS, Android)

Ya están disponibles los materiales del Webcast de SUGES que ayer tuve el placer de compartir junto a Adrían Díaz recién nombrado MVP de SharePoint Server. En esta ocasión os hablamos de SharePoint y las aplicaciones para móviles, lo modelos de desarrollo de que disponemos, comparamos aplicaciones nativas, HTML e híbridas, etc. Creo que dejamos un debate muy interesante sobre qué se puede esperar de una aplicación móvil conectada a SharePoint realizando además algunos ejemplos sencillos de cómo podemos realizarlo.

Para descargaros los materiales, sólo tenéis que ir a esta página de SUGES

download

 

Enjoy it! Sonrisa

Webcast | SharePoint y los dispositivos móviles (W8, WP8, iOS, Android)

Hoy 23 de enero a las 18:00 hora (GMT+1) tendré el placer de estar junto a Adrían Díaz en un nuevo webcast de SUGES para hablar sobre SharePoint y los dispositivos móviles explicando. Para tal fin, mostraremos cómo podemos consumir los datos de la forma más sencilla desde dispositivos Windows 8, Windows Phone, iOS y Android y de qué herramientas disponemos para ello.

 

SharePoint-Dispositivos-Moviles

Datos del evento:

Organizador: SUGES

Formato: Online

Fecha: 23/01/2014

Hora: 18.00 (GMT+1)

Enlace: https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032574966&Culture=es-ES&community=0

 

No os lo perdáis.

Windows Phone | Diferentes plantillas de elementos en LongListSelector con DataTemplateSelector

Escenario

Puede darse el caso en el que tengamos que aplicar diferentes plantillas a los elementos de una misma lista, en este artículo un LongListSelector. Para ello, tenemos varios métodos como por ejemplo usar Converters e ir aplicando distintos valores de propiedades según convenga (Lo explicaré en un próximo artículo), pero esto tiene un inconveniente y es que tendríamos que crear tantos converters como propiedades distintas queramos cambiar.

TemplateSelector

 

Solución

Bueno, ¿y cuál es la solución? Pues no es otra que hacer un uso un poco más avanzado de las herramientas de las que disponemos y crear un DataTemplateSelector que no es más que un “control” que nos permite crear un selector. Así pues, comencemos a ver cómo irlo creando paso a paso.

NOTA: En WPF y en Windows 8 se puede usar sin necesidad de este Workaround

 

Los datos

Bueno, para empezar debemos definir los datos con los que vamos a trabajar para poder determinar cuál va a ser el elemento diferenciador entre ellos. En este caso, he usado la lista de Speakers de las “Christmas Windows Phone Sessions” y les he añadido una propiedad “Type” que define el área a la que pertenecen entre Business, Developer y Designer. Así pues, tenemos a Alejandro Campos Magencio en el área Business, a mi como Designer y el resto como Developer. Y lo que quiero es diferenciarlos por el color de fondo de su nombre tal y como se muestra en la imagen de “resultado”.

El modelo que he creado es el siguiente:

public class SpeakerViewModel

{

    public int Id { get; set; }

    public string Type { get; set; }

    public string Title { get; set; }

    public string ProfilePhoto { get; set; }

}

 

Todos los Speakers los estoy recogiendo en una ObservableCollection<SpeakerViewModel> que tengo en mi ViewModel principal para, de esta forma, fácilmente pasárselo a la página de detalles (en la que no vamos a entrar ahora)

public void LoadSpeakers()

{

    this.Speakers.Add(new SpeakerViewModel() { Id = 1, Title = "Alejandro Campos Magencio", Type = "Business", ProfilePhoto = "/Assets/Speakers/Alejandro.jpg" });

    this.Speakers.Add(new SpeakerViewModel() { Id = 2, Title = "Josué Yeray", Type = "Developer", ProfilePhoto = "/Assets/Speakers/JosueYeray.jpg" });

    this.Speakers.Add(new SpeakerViewModel() { Id = 3, Title = "Roberto Luis Bisbé", Type = "Developer", ProfilePhoto = "/Assets/Speakers/RobertoLuisBisbe.jpg" });

    this.Speakers.Add(new SpeakerViewModel() { Id = 4, Title = "Javier Suárez Ruiz", Type = "Developer", ProfilePhoto = "/Assets/Speakers/JavierSuarez.jpg" });

    this.Speakers.Add(new SpeakerViewModel() { Id = 5, Title = "Rafa Serna", Type = "Developer", ProfilePhoto = "/Assets/Speakers/RafaSerna.jpg" });

    this.Speakers.Add(new SpeakerViewModel() { Id = 6, Title = "Santiago Porras", Type = "Designer", ProfilePhoto = "/Assets/Speakers/SantiagoPorras.jpg" });

 

}

 

Además de estos datos, que son los que se muestran al ejecutar la aplicación, como estamos diseñando cómo se verán los elementos querremos verlos en tiempo de edición, así que nos crearemos los Mockups dentro de un fichero XAML que referenciaremos en nuestra poniendo en la cabecera su referencia.

Referencia en la vista

d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"

 

Datos de ejemplo

<vm:MainViewModel.Speakers>

    <vm:SpeakerViewModel Id="1" Title="Alejandro Campos Magencio"   Type="Business" ProfilePhoto="/Assets/Speakers/Alejandro.jpg"/>

    <vm:SpeakerViewModel Id="2" Title="Josué Yeray"   Type="Developer" ProfilePhoto="/Assets/Speakers/JosueYeray.jpg"/>

    <vm:SpeakerViewModel Id="3" Title="Roberto Luis Bisbé" Type="Developer" ProfilePhoto="/Assets/Speakers/RobertoLuisBisbe.jpg"/>

    <vm:SpeakerViewModel Id="4" Title="Javier Suárez Ruiz"  Type="Developer" ProfilePhoto="/Assets/Speakers/JavierSuarez.jpg"/>

    <vm:SpeakerViewModel Id="5" Title="Rafa Serna"  Type="Developer" ProfilePhoto="/Assets/Speakers/RafaSerna.jpg"/>

    <vm:SpeakerViewModel Id="6" Title="Santiago Porras"   Type="Designer" ProfilePhoto="/Assets/Speakers/SantiagoPorras.jpg"/>

</vm:MainViewModel.Speakers>

NOTA: Si hemos usado una plantilla con datos al crear la aplicación (Windows Phone Databound App, Windows Phone Pivot App o Windows Phone Panorama App) dispondremos de un archivo “SampleData/MainViewModelSampleData” que ya está asociado a la vista MainPage.xaml y que podremos usar para realizar esta acción.

 

Una vez hayamos hecho esto y teniendo un LongListSelector enlazado a los datos, podremos ver que nos aparecen elementos en la Vista Diseño, con lo que podremos darle color a los elementos.

 

Implementar la clase DataTemplateSelector

Ahora que ya tenemos los datos, debemos crear la base del selector de plantillas que debe heredar de “ContentControl” que se encuentra dentro del espacio de nombres “System.Windows.Controls”.

public abstract class DataTemplateSelector : ContentControl

{

    public virtual DataTemplate SelectTemplate(object item, DependencyObject container)

    {

        return null;

    }

 

    protected override void OnContentChanged(object oldContent, object newContent)

    {

        base.OnContentChanged(oldContent, newContent);

 

        ContentTemplate = SelectTemplate(newContent, this);

    }

}

Como se puede apreciar, hay dos métodos virtuales que debemos implementar al heredar de ContentControl

  • SelectTemplate: Selecciona una plantilla (De momento lo dejamos como null porque lo implementaremos más adelante en otra clase).
  • OnContentChanged: Define la plantilla que se muestra al cambiar el contenido, cosa que sucede cuando se rellenan los datos con el Binding. Esta es la que usaremos para nuestro ejemplo.

 

Implementar la clase que gestione los elementos, ItemDataTemplateSelector

Continuamos con el trabajo creando la clase que realmente va a realizar el cambio de plantilla atendiendo al campo “Type” del modelo SpeakerViewModel.

Esta clase, que he llamado ItemDataTemplateSelector, hereda de DataTemplateSelector y contiene, una propiedad por cada diferente plantilla y la sobreescritura del método SelectTemplate que atiende al campo “Type” para definir la plantilla que se ha de mostrar.

public class ItemDataTemplateSelector : DataTemplateSelector

{

    public DataTemplate Designer

    {

        get;

        set;

    }

 

    public DataTemplate Developer

    {

        get;

        set;

    }

 

    public DataTemplate Business

    {

        get;

        set;

    }

 

    public override DataTemplate SelectTemplate(object item, DependencyObject container)

    {

        SpeakerViewModel speaker = item as SpeakerViewModel;

        if (speaker != null)

        {

            if (speaker.Type == "Designer")

                return Designer;

            else if (speaker.Type == "Developer")

                return Developer;

            else 

                return Business;

        }

 

        return base.SelectTemplate(item, container);

    }

}

 

Usar las diferentes plantillas gestionadas por ItemDataTemplateSelector

Ya sólo nos queda crear las plantillas e indicarle al LongListSelector que haga uso de ellas.

En primer lugar, tenemos que definir el espacio de nombres en el que hemos incluido el selector de plantillas ItemDataTemplateSelector que, en mi caso, he puesto en una carpeta Helpers. Así pues, en la cabecera de la vista donde tengamos el LongListSelector, debemos añadir la siguiente línea:

xmlns:local="clr-namespace:Sample_2.Helpers"

Nótese que “local” puede ser sustituido por lo que queráis, pero tenéis que tenerlo en cuenta más adelante.

El siguiente paso, es crear las plantillas dentro del LongListSelector, haciendo referencia a las propiedades correspondientes a cada una de las plantillas que definimos en ItemDataTemplateSelector, como por ejemplo “local:ItemDataTemplateSelector.Developer”.

<phone:LongListSelector Grid.Row="1" ItemsSource="{Binding Speakers}" LayoutMode="Grid" GridCellSize="180,180">

    <phone:LongListSelector.ItemTemplate>

        <DataTemplate>

            <local:ItemDataTemplateSelector Content="{Binding}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">

                <local:ItemDataTemplateSelector.Developer>

                    <DataTemplate>

                        <Grid Height="180" Margin="5" Width="180" Background="#FFE00109">

                            <Border BorderBrush="Black" Height="50" VerticalAlignment="Bottom" Background="#FFE00109">

                                <TextBlock Text="{Binding Title}" Style="{StaticResource PhoneTextExtraLargeStyle}" Foreground="White" FontSize="24" VerticalAlignment="Center"/>

                            </Border>

                        </Grid>

                    </DataTemplate>

                </local:ItemDataTemplateSelector.Developer>

 

                <local:ItemDataTemplateSelector.Designer>

                    <DataTemplate>

                        <Grid Height="180" Margin="5" Width="180" Background="#FF009045">

                            <Border BorderBrush="Black" Height="50" VerticalAlignment="Bottom" Background="#FF009045">

                                <TextBlock Text="{Binding Title}" Style="{StaticResource PhoneTextExtraLargeStyle}" Foreground="White" FontSize="24" VerticalAlignment="Center"/>

                            </Border>

                        </Grid>

                    </DataTemplate>

                </local:ItemDataTemplateSelector.Designer>

                <local:ItemDataTemplateSelector.Business>

                    <DataTemplate>

                        <Grid Height="180" Margin="5" Width="180" Background="#FF7b057e">

                            <Border BorderBrush="Black" Height="50" VerticalAlignment="Bottom" Background="#FF7b057e">

                                <TextBlock Text="{Binding Title}" Style="{StaticResource PhoneTextExtraLargeStyle}" Foreground="White" FontSize="24" VerticalAlignment="Center"/>

                            </Border>

                        </Grid>

                    </DataTemplate>

                </local:ItemDataTemplateSelector.Business>

            </local:ItemDataTemplateSelector>

        </DataTemplate>

    </phone:LongListSelector.ItemTemplate>

</phone:LongListSelector>

Llegados a este punto, ya tenemos todo preparado para ejecutar el proyecto y ver el resultado.

TemplateSelector

 

Estructura final del proyecto

La estructura final del proyecto, debería contener algo como lo que muestro a continuación, con las dos clases que hemos creado para la selección de la plantilla, los ViewModel necesarios, los datos de ejemplo y la vista principal MainPage.xaml.

Solution

 

Código

Lo pondré en un próximo artículo con los recursos del Webcast en el que hablé de esto, siendo la solución que colgaré, más avanzada y con un aspecto algo diferente…

result-enhanced

 

Enjoy it!

Windows Phone | Disponibles las nuevas versiones de los emuladores (Update 2 y Update 3)

En este nuevo año, Microsoft ha liberado actualizaciones de diferentes productos y, para los desarrolladores de Windows Phone no podía faltarnos la actualización de los emuladores.

  • Desde el 2 de enero está disponible el Update 2 : Preparado para ambientes que sólo tengan Visual Studio 2013 RTM
  • Desde el 6 de enero está disponible el Update 3 : Corresponden a Windows Phone Update 3 e incluso incluye un emulador con la resolución 1080p

Windows-Phone-Update-Emulators

En Verde UPDATE 2

En magenta UPDATE 3

NOTA: Estas actualizaciones requieren Visual Studio 2012 Update 3 o (b) RTM de Visual Studio 2013, con el SDK 8.0 de Windows Phone instalado.

¿Es importante aplicar estas actualizaciones? Desde mi humilde opinión, SÍ, para poder ofrecer aplicaciones adaptadas a las nuevas actualizaciones de Windows Phone y poder aportar a los usuarios la mejor experiencia posible. Además, como ya he comentado en otras ocasiones, cada actualización incluye una serie de mejoras y fixes absolutamente necesarios para que nuestras Apps aporten una calidad excelente.

Todas las descargas están disponibles en este enlace http://developer.windowsphone.com/en-us/downloadsdk

Windows Azure | Disponible una nueva versión de Windows Azure Storage Client Library

El pasado 4 de enero, Microsoft liberó la nueva versión de la librería de Windows Azure Storage (3.0.2) y, cómo no, ésta está disponible en nuget.

Para instalarla, sólo tenemos que abrir el Package Manager Console y ejecutar el siguiente comando de PowerShell.

PM> Install-Package WindowsAzure.Storage

Install-Windows-Azure-Storage_thumb5

 

Esta acción actualizará la librería de Windows Azure Storage, no así el resto de componentes del SDK de Windows Azure.

Y os preguntaréis, ¿es una buena práctica actualizar sólo una librería cuando el resto se mantiene en la misma versión? La respuesta, al menos en este caso de la plataforma en la nube de Microsoft, es que sí, dado que cada versión incluye fixes y nuevas funcionalidades para adaptarse a la constante actualización, mejora y adición de componentes de Windows Azure. Veamos qué incluye esta actualización:

  - All (WP): Fixed an issue causing ArgumentOutOfRangeException in many APIs.

  - Queues: Fixed an issue causing NullReferenceException when trying to re-create an existing queue.

  - Tables: Fixed an issue with TableServiceContext causing NullReferenceException when the error response cannot be parsed.

  - Tables (RT): Do not allow users to set JsonFullMetadata format on RequestOptions in RT, because JSON is not supported on the RT library yet.

 

Como se puede observar, han incluído 4 fixes que nos ayudarán a crear soluciones más robustas. Aquí podéis ver todas las notas de las versiones de la librería Windows Azure Storage.

Espero que os actualicéis. Enjoy it! Sonrisa