Webcast | Creando aplicaciones para Windows Store con XAML: ¿Qué necesitan saber los Desarrolladores de .Net?

El próximo 23 de Abril a las 16.00 GMT tendrá lugar el Webcast Creando aplicaciones estilo Metro con XAML: ¿Qué necesitan saber los Desarrolladores de .Net? y me complace anunciar que seré el speaker del mismo.

Este será mi segundo Webcast tras el que realicé hace ya algún tiempo junto a Alberto Díaz sobre diseño en SharePoint 2010 “¿Cómo se hizo www.SUGES.es con SharePoint 2010?

Datos del Webcast:

  • URL del webcast: Inscripción
  • Hora: 16.00 GMT
  • Duración: 1 hora
  • Público: General – Developer

 

Webcast-Windows-8

 

Espero veros a todos allí Sonrisa

Megathon Windows 2013 | Superando la edición anterior

Hola a todos, en este artículo me gustaría hacer un poco de reflexión sobre el Megathon Windows 2013 que ha superado las expectativas de todos y ha supuesto un gran éxito de participación y colaboración tanto a nivel nacional como local.

Aquí, en Tenerife, comenzamos metiendo el miedo en el cuerpo con la imagen anunciadora del evento y con el vídeo promocional.

TenerifeDev

Video Promocional Megathon Windows 2013 en Tenerife

 

Pero aquí, en Tenerife, nos hemos quedado sorprendidos viendo como participantes de diferentes características –estudiantes, emprendedores, developers con otros lenguajes, etc– han sido capaces de desarrollar una aplicación en tan sólo 2 días de trabajo, partiendo algunos de ellos desde 0 en conocimiento de la plataforma, del patrón MVVM, de Interfaces y, sobre todo, de producto estrella de este Megathon, Azure Mobile Services.

IMG_20130413_121428

El primer día, y tras la apertura del “espartano” Alberto Díaz, el hijo pródigo Josué Yeray, que volvió a casa para ayudar a su comunidad, metió el miedo a todos en el cuerpo con el patrón MVVM bien desarrollado, un concepto que para algunos fue duro de asimilar pero que después les facilitó mucho la vida cuando aprendieron a implementarlo.

IMG_20130412_175042

IMG_20130412_200502

 

En este día los asistentes hicieron mucho networking llegando a formar equipos de gente que no se conocía previamente con lo que en este sentido tuvimos el primer éxito.

El segundo día comenzamos con las charlas específicas para poder entender más las plataformas para las que desarrollar. En primer lugar, el maestro de maestros de Windows Phone dio su aportación hablando de Windows Phone. Tras él, Alberto Díaz contó las bondades sobre Windows 8 y, finalmente, David Rodríguez fue el encargado de desgranar Azure Mobile Services para que los participantes sacara provecho de las posibilidades que ofrece.

El tercer día fue el mejor de todos ya que los participantes sudaban “código” intentando afinar funcionalidades antes de la presentación de los proyectos y, para relajarlos un poco decidimos grabar el Harlem Shake con la actuación estelar de Aquiles, Leónidas, Pepe Gáfez y cerrando… la princesa Leia, todos ellos involucrados entre la multitud desorganizadas y alocada. Lo que hace uno por un smartphone…

Harlem Shake de los participantes del Megathon Windows 2013 de Tenerife

 

Para acabar, pasamos a la ronda de presentación de proyectos donde algunos equipos nos sorprendieron, no sólo por el nivel del desarrollo, sino también por la calidad de las ideas y cómo las habían ido moldeando para lograr un modelo de negocio bastante robusto gracias a Jose Fortes que se involucró con los equipos para ayudarles a definir una estrategia comercial adecuada para sus proyectos.

Los ganadores, con una aplicación que mantendré anónima hasta el fin del concurso, desarrollaron un alto porcentaje de las funcionalidades de la aplicación su aplicación y, además, la idea fue una de las mejores.

Finalmente, cerramos el evento sorteando los “goodies” que nuestros patrocinadores Nokia, Telerik y PluralSight nos enviaron. Queremos repetir y aún se oyen los gritos espartanos de nuestros participantes pidiendo otro evento igual o de similares características. ¿Para cuándo el próximo?

IMG_20130412_171632

CSS | Good manners vs hacks para navegadores específicos

Hoy me gustaría compartir una reflexión con todos ustedes acerca de las buenas formas para crear una hoja de estilos en contra punto al uso de hacks e ideas estrambóticas que pueden hacer de una hoja de estilos algo ilegible y que te dan ganas de tirar a la basura.

Uso de hacks

Los hacks en CSS son “trucos” para que determinados navegadores interpreten las hojas de estilo de forma diferente y generalmente se usaban para corregir la mala interpretación que hacían a determinadas propiedades. Se popularizaron durante la etapa en la que la versión del navegador de Microsoft era Internet Explorer 6 (IE6) que, debido a su “errónea” “especial” interpretación del modelo de caja (ancho/alto, padding y margen), hacía del diseño de páginas web una tarea dura. No era extraño ver diseñadores web que renegaban del navegador más popular que debían instalarse programas que renderizaban sus webs en N navegadores diferentes para poder ver las diferencias y así poder ir ajustando la visualización de las mismas de forma menos lenta. Una prueba que dejaba en gran evidencia estos problemas era la nota que obtenían los diferentes navegadores en “Acid Test”.

Con la llegada de Internet Explorer 7 (IE7) parecía que podía morir toda esta serie de trucos, pero aún había que usarlos en determinadas ocasiones, aunque ya se reducía a los sitios que querían seguir manteniendo el soporte para IE6 o sitios con “plantilas muy complejas”. Lo entrecomillo porque la complejidad de una plantilla depende más de una planificación del diseñador, de una mala distribución o de un mal uso de las propiedades CSS.

Finalmente, la cosa cambió mucho con la llegada de Internet Explorer 8 (IE8), momento en el que los motores de los navegadores más populares ya realizaban una interpretación de las hojas de estilo de forma muy similar y se comenzó a imponer un único modelo de caja para todos ellos.

Así pues, un diseñador web vivía con cosas como esta:

   1: .mydiv {

   2:     height: 15px; /* Para todos los Browsers */

   3:     #height: 20px; /*Para Internet Explorer */

   4:     height: 20px9; /*Para Internet Explorer 8 o inferior */

   5:     *height: 20px; /*Para Internet Explorer 7 o inferior */

   6:     _height: 25px; /* Para Internet Explorer 6 */

   7: }

Pero, ¿era bueno hacer uso de estos hacks ? ¿eran realmente necesarios? ¿Influían en el posicionamiento de nuestras webs? ¿Son necesarios en la actualidad?

Ventajas de uso de los hacks

Ventajas hay, pero no todas porque hay algunas desventajas preocupantes como veremos en la influencia en el posicionamiento. No obstante, un sitio web debe tener una identidad que debe quedar claramente plasmada en todos los navegadores y en la calidad con la que se vea. Por lo tanto, si queríamos mantener una identidad de marca tal y como se diseñaba, necesitábamos imperiosamente usar algunos trucos para que el sitio web se visualizara de la misma forma en todos lo navegadores y que, de esa forma, los usuarios siempre identificaran el sitio web con la marca y la interacción fuera la misma independientemente del navegador que usaran. Pero, ¿son los hacks la solución?

Necesidad de uso de los hacks

Relacionado con el punto anterior, si queremos mantener una identidad de marca, ¿se hace necesario el uso de hacks? La respuesta es NO, un NO tan rotundo como que si no se respira se muere. Entonces, ¿cómo se debía resolver los problemas de visualización de las páginas en los diferentes navegadores? Pues es muy fácil, el uso de “Comentarios condionales” o “Bloques condicionales” como me gusta llamarlos a mi.

<!--[if IE]>

According to the conditional comment this is IE
<![endif]-->
<!--[if IE 6]>

According to the conditional comment this is IE 6
<![endif]-->
<!--[if IE 7]>

According to the conditional comment this is IE 7
<![endif]-->
<!--[if IE 8]>

According to the conditional comment this is IE 8
<![endif]-->
<!--[if IE 9]>

According to the conditional comment this is IE 9
<![endif]-->
<!--[if gte IE 8]>

According to the conditional comment this is IE 8 or higher
<![endif]-->
<!--[if lt IE 9]>

According to the conditional comment this is IE lower than 9
<![endif]-->
<!--[if lte IE 7]>

According to the conditional comment this is IE lower or equal to 7
<![endif]-->
<!--[if gt IE 6]>

According to the conditional comment this is IE greater than 6
<![endif]-->
<!--[if !IE]> -->
According to the conditional comment this is not IE
<!-- <![endif]-->

 

Con estos “bloques condicionales” podemos indicar a los navegadores que sólo deben interpretar el código específico para ellos, con lo que podemos cargar hojas de estilo específicas dependiendo del navegador, o mostrar bloques de HTML diferentes.

 

Influencia en el posicionamiento

Muchos “diseñadores” defendían que daba igual que una hoja de estilos estuviera incorrecta a los ojos del validador de CSS de W3C por el uso de estos hacks. Pero lo cierto es que Google, Bing, Ask, Yahoo,… penalizaban en mayor o menor medida los sitios web que contienen HTML o CSS inválidos por lo que, si queremos posicionarnos en los buscadores, deberíamos tener como objetivo que el código HTML de nuestro sitio y las Hojas de Estilo asociadas cumplan con los estándares. Por lo tanto, se puede afirmar que el uso de hacks en las hojas de estilo tiene una influencia relativamente grande y negativa en cuanto penaliza en el posicionamiento o SEO como se ha puesto de moda.

Actualidad

En la actualidad no es necesario el uso de hacks , aunque siempre he defendido y sigo defendiendo que nunca lo han sido. En estos momentos los navegadores renderizan las páginas web de una forma prácticamente idéntica (salvo excepciones puntuales) y que, salvo mal uso de las propiedades en las hojas de estilo, nuestras hojas de estilos serán válidas para todos los navegadores.

No obstante, si hacemos uso de CSS3, veremos cómo a menudo tenemos que colocar los prefijos –ms, -webkit, –opera,… , que son hacks a medias ya que están aceptados, para asegurarnos que todos los navegadores interpretan una propiedad específica y que se corresponde con CSS3. Esto es debido a que no todos los navegadores han adoptado las especificaciones de HTML5 / CSS3 definidas por la W3C y, aquí se produce una sorpresa que pocos conocen y que muchos querrán discutir, INTERNET EXPLORER 10 es el navegador que CUMPLE MÁS ESTÁNDARES DE HTML5 / CSS3. Esto se debe a que en Microsoft se han esforzado en que todas y cada una de las especificaciones aprobadas estén soportadas por Internet Explorer.

Complicar las cosas

Cambiemos un poco de contexto aunque quiero que mantengáis en la memoria que los hacks no deberían ser usados y que bien podrían estar en esta sección del artículo.

Como buen diseñador, me gusta analizar hojas de estilo de sitios populares, complejos o que simplemente me llaman la atención. Lo hago casi todos los días con el afán de aprender cosas nuevas, algún truquillo para realizar algo que haya visto y mantenerme siempre “entrenado” ante las posibles situaciones que me pueda encontrar en el futuro. Esto me permite encontrar casi siempre el mal uso de propiedades, redundancia, excesivas sobreescrituras, florituras innecesarias,… que me hacen pensar que o yo soy muy perfeccionista o hay demasiados diseñadores que no entienden el concepto de las hojas de estilo, que no tienen los conocimientos necesarios del ámbito tecnológico o que simplemente son desarrolladores que hacen sus pinitos de diseño.

Cualquiera que sea el caso, he llegado a encontrar un código html como este

<strong>Texto que debería ir en negrita</strong>

y después en CSS lo siguiente

strong {
    font-weight: normal;
}

Otro ejemplo es el uso de elementos flotantes a “cascoporro”, como los float son gratis… pues hala, todos son float. En este sentido me he encontrado páginas como:

  • En las que el cuerpo entero de la página es flotante y después tienen que hacer cosas complicadas para centrarlo o le ponen un margen a la izquierda fijo.
    • Un sólo elemento no debe flotar y así se centraría fácilmente
  • División de columnas en las que todos los elementos son flotantes.
    • Si son 2 columnas, debería flotar sólo uno de los elementos con un ancho fijo y el otro elemento tener un margen que deje el hueco del elemento flotante
    • Tres columnas, podrían flotar los laterales y el del centro mantenerse fijo con márgenes a ambos lados por ejemplo. También se podrían agrupar y así siempre mantener un elemento flotante y otro fijo.
  • Elementos flotantes con posiciones absolutas
    • A ver, si un elemento tiene posición absoluta, es lógico pensar que lo ubicaremos controlando las coordenadas left y top por ejemplo con lo que se anula el efecto flotante, pero el navegador puede complicarnos la vida intentando interpretarlo.

CSS-Errors

 

Puede que en algunos casos se queden propiedades residuales como resultado de la modificación del diseño durante la etapa de desarrollo, pero aún así, son demasiados casos que provocan que las hojas de estilo sean difíciles de mantener y que si algún día tenemos que modificar la visualización de algún elemento tengamos que llamar a Tom Cruise para que ruede una nueva entrega de Misión Imposible junto con nosotros. Para que todos lo vean más claro, pondré un ejemplo con código C#.

Veamos una asignación de valor a una variable dependiendo de una condición booleana.

if (condition) 
{
    myVar = valorTrue;
} 
else 
{
    myVar = valorFalse;
}

 

Este pedazo de código es tan simple que cualquiera lo entiende pero, también lo podemos hacer en una línea.

myVar = (condition) ? (valorTrue) : (valorFale);

 

Muy bonito, pero… en un método con varias líneas de código no es tan legible y, si añadimos más métodos, por ejemplo en la funcionalidad de una clase, con más instrucciones en línea como esta… empieza a ser algo complicado de ver y, por lo tanto difícil de mantener. Luego, los grandes programadores suelen recomendar que estas instrucciones sólo se usen en funciones que no tengan más líneas de código para que así sea fácilmente legible.

Por lo tanto, ¿qué podemos sacar en claro de esto? Que generalmente vale más hacer las cosas claras y limpias antes que ahorrarnos un par de líneas de código.

 

Recomendaciones para tener hojas de estilo simples y claras

Una hoja de estilos es algo simple, muy simple, basada en un concepto más simple aún y, por lo tanto, debe de ser absolutamente legible. Así que me gustaría dar algunas recomendaciones

  • Antes de empezar a escribir una hoja de estilos usa bocetos en papel o a partir del diseño original en algún programa de edición de imágenes para determinar los bloques de las páginas. De esta forma, se podrá determinar si hay elementos flotantes, cuáles son, las dimensiones de los mismos, el número de divs,…
  • En el HTML nunca debería haber modificadores de estilos dentro de los elementos a no ser que sea absolutamente necesario. Las modificaciones de estilos se deben hacer en las Hojas de Estilos CSS siempre.
  • No escatimes el uso de bloques. Los elementos se deben agrupar en bloques sin temor a pensar que se usan muchos porque al final serán más fácilmente manejables y modificables.
  • Usa clases o id’s siempre que puedas porque todos los elementos deberían ser fácilmente identificables para su posterior tratamiento en las hojas de estilo.
  • NO USES HACKS. Los hacks ya eran innecesarios en el pasado pero en la actualidad son absolutamente nefastos. Deberías pensar que si tienes que usar un hack es que estás haciendo algo mal.
  • Si quieres soportar navegadores antiguos o usas HTML5 / CSS3, incluye en tu sitio web una referencia a Modernizr o algún sistema de retrocompatibilidad similar.
  • LAS TABLAS SON SÓLO PARA TABULAR DATOS Y NO PARA DISEÑAR. Lo pongo en mayúsculas porque aún, hoy en día, todavía hay quien las usa para diseñar y eso un error muy grave que al final provoca dolores de cabeza porque las tablas no se interpretan igual en todos los navegadores y porque, como son para mostrar datos, al final resultan más complejas de manejar.
  • Ante la duda, busca siempre las especificaciones de la W3C para tratar de entender las propiedades que quieres modificar en un elemento.
  • Si se te complica un diseño, es mejor abstraerse y volver a determinar los bloques que tienes que ubicar y cómo los tienes que modificar.

Windows Phone: Depurar una app a partir del paquete .XAP en el emulador o en un dispositivo

Escenario

Necesitamos depurar una aplicación, en el emulador o en un dispositivo, de la que sólo tenemos el archivo .XAP que se genera cuando se empaqueta la aplicación y no podemos disponer del código fuente.

 

Solución

Hay varias opciones como por ejemplo subir el archivo .xap al market y distribuirlo a los testers de la aplicación que tardarán aproximadamente 24 horas en recibirla. Pero si lo queremos hacer de forma inmediata, con el SDK de Windows Phone 8 tenemos una nueva opción que nos permitirá desplegar la aplicación en el emulador o en un dispositivo físico gracias a la aplicación “Application Deployment

Application-Deployment

 

Al ejecutar la aplicación, sólo tendremos que seleccionar el dispositivo o el emulador donde realizar el despliegue, además del .XAP de la aplicación que queremos desplegar.

Application-Deployment-Device

Application-Deployment-XAP-file

 

Finalmente, pulsando el botón Deploy conseguiremos que se despliegue la aplicación que podremos localizar en la lista de aplicaciones del dispositivo o emulador, con lo que podremos testear la app sin necesidad de disponer del código fuente.

Application-Deployment-Deploy

Iberian SharePoint Conference 2013 | Primera conferencia de SharePoint en la Península Ibérica

Desde que llegué al mundo de SharePoint, siempre me he preguntado por qué no se organizaba ningún evento relacionado con SharePoint en el que los expertos pudieran relacionarse con desarrolladores, diseñadores, usuarios y clientes de las diferentes versiones de esta gran plataforma de Microsoft. De hecho, en conversaciones con algún MVP’s de SharePoint, he nombrado varias veces esta posibilidad como medio para dar a conocer este producto entre las empresas españolas ya que de momento no ha sido acogido como en el resto del mundo donde es una de las joyas de la corona en el mundo del software.

Este año, mi petición se hará realidad. Los grupos de usuarios de SharePoint de la Península Ibérica están organizando y planificando la 1ª Conferencia Ibérica de SharePoint qué tendrá lugar el 10 de octubre de este año en Madrid. Será el primer evento dedicado exclusivamente a SharePoint desde distintas perspectivas en el que participen los principales expertos nacionales e internacionales en la plataforma como Gustavo Vélez, Alberto Díaz, J​uan Carlos González, Edin Kapic, Mario Cortes, Joel Oleson, Michael Noel, entre otros, y en el que los asistentes tendrán la oportunidad no sólo de formarse, sino también de conocer distintas soluciones construidas sobre la plataforma.

Además, para poner la guinda al pastel, tendré la posibilidad de estar sobre la tarima junto a Teresa Cebrián de Encamina para dar una sesión sobre Diseño UX/UI en SharePoint 2013. Esto supone ponerme el listón muy alto y, tanto Teresa como yo deberemos estar a la altura de las circunstancias ya que compartimos cartel junto a grandes maestros internacionales además de los MVP’s nacionales y otros destacados arquitectos y desarrolladores expertos en SharePoint.

Challenge_accepted

 

Gracias al trabajo de General de Software y de Encamina, contamos con un sitio web de la conferencia, http://www.iberiansharepointconference.com/, construido sobre SharePoint 2013 en el que podéis encontrar toda la información sobre el evento:

Portal-Iberian-SharePoint-Conference

 

Sin duda, para toda la comunidad de SharePoint de la Península Ibérica, e incluso para la comunidad internacional, será un lugar obligado de reunión donde podrán, no sólo aprender de la mano de los expertos, sino discutir y hacer networking con los expertos de diferentes áreas.

La idea es organizar varias sesiones paralelas sobre SharePoint 2013 y plataformas relacionadas en torno a temáticas de Desarrollo (DEV), Sistemas (IT Pro) y Negocio (BIZ). En total, se realizarán un mínimo de 20 a 25 sesiones en torno a la plataforma estrella de Microsoft. Nos gustaría contar con tu participación en la conferencia como participante y también con cualquier apoyo que puedas brindar sobre todo en lo que a patrocinios necesarios para que la conferencia sea todo un éxito. Nuestro objetivo es contar con la presencia de las empresas más importantes en el mundo SharePoint a nivel nacional e internacional.

Contamos contigo para hacer realidad esta 1ª Conferencia Ibérica de SharePoint en la que todos los actores implicados, sponsors, asistentes, ponentes y organizadores, son una pieza fundamental para su éxito. Si tienes cualquier duda, cuestión o comentario sobre la conferencia no dudes en contactar a través del correo electrónico suges@hotmail.es, la cuenta de Twitter @IberianSPConf y el hastag #iberianspc de momento.

¡¡APUNTA LA FECHA!!

SharePoint 2010 y 2013 | Añadir fichero de recursos para localización regional

Escenario

Tenemos un sitio de SharePoint 2010 o 2013 y queremos localizarlo en función del idioma del usuario que visita dicho sitio. Para ello tenemos diversas opciones.

  • Localización de Página Maestra (Masterpage) y Diseños de Página (Page Layouts)
  • Localización de recursos en Interfaz de Usuario (UI) en un desarrollo
  • Localización de recursos en código manejado en un desarrollo

Aunque en realidad, se puede resumir en, localización en Interfaz de Usuario y localización en código manejado.

 

Solución

0.- Creación del fichero de recursos

El fichero, deberá tener una estructura como la indicada bajo estas líneas, además de tener la extensión .resx. Si no os fiáis mucho y dado que este ejemplo lo he sacado de SharePoint 2013, podéis ir a la carpeta

 

<?xml version="1.0" encoding="utf-8"?>
<!-- _lcid="1033" _version="15.0.4420.1017" _dal="1" -->
<!-- _LocalBinding -->
<root>
  <!-- 
    Microsoft ResX Schema 
    Version 2.0
    The primary goals of this format is to allow a simple XML format 
    that is mostly human readable. The generation and parsing of the 
    various data types are done through the TypeConverter classes 
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple 
    name/value pairs.
    Each data row contains a name, and value. The row also contains a 
    type or mimetype. Type corresponds to a .NET class that support 
    text/value conversion through the TypeConverter architecture. 
    Classes that don't support this are serialized and stored with the 
    mimetype set.
    The mimetype is used for serialized objects, and tells the 
    ResXResourceReader how to depersist the object. This is currently not 
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format 
    that the ResXResourceWriter will generate, however the reader can 
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array 
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
 
  <data name="myResource_1" xml:space="preserve">
    <value>Contact</value>
  </data>
 
  <data name="myResource_2" xml:space="preserve">
    <value>Contact</value>
  </data>
 
  <data name="myResource_3" xml:space="preserve">
    <value>Contact</value>
  </data>
 
...
...
...
 
  <data name="myResource_N" xml:space="preserve">
    <value>Contact</value>
  </data>
 
 
</root>

 

1.- Localización en Interfaz de Usuario

Se nos presentan varios posibles casos dependiendo del entorno y del alcance que queramos tener:

  • Localizar sólo los recursos en Página Maestra y Diseños de Página

En este caso, tan sólo tendremos que realizar dos acciones, crear el fichero de recursos que, básicamente es un XML, y agregarlo al directorio de recursos globales de la aplicación que se encuentra en directorio virtual de la aplicación web (Web Application) en la que queramos aplicar la localización, en la dirección C:inetpubwwwrootwssVirtualDirectoriesTU_APLICACION_WEBApp_GlobalResources. Posteriormente, en los elementos donde necesitemos las traducciones, añadir el contenido mediante una llamada al fichero de recursos. Por ejemplo:

<asp:Literal runat="server" Text="<%$Resources:nombre_del_fichero_de_recurso,nombre_del_recurso%>" />

 

  • Localizar sólo los recursos en Página Maestra y Diseños de Página para todas las aplicaciones web que se creen de ahora en adelante

El caso, es prácticamente el mismo que el del punto anterior, sólo que el fichero lo pondremos en la carpeta:

  • SharePoint 2010: C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions14CONFIGResources
  • SharePoint 2013: C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions15CONFIGResources

De esta forma, cada vez que creemos una nueva aplicación web de SharePoint, los ficheros de recursos que pongamos aquí serán copiados a la carpeta que especifiqué App_GlobalResources en el punto anterior.

  • Localizar los recursos en elementos visuales de una solución desarrollada desde Visual Studio

La idea es básicamente la misma, pero se resuelve de una forma más simple. En la solución, tendríamos que añadir la Carpeta Mapeada a los recursos globales de la aplicación, tal y como se ilustra en la imagen.

GlobalResources

Una vez añadida la carpeta, podemos crear un fichero de recursos y añadirle los elementos que necesitemos o, si ya lo habíamos creado, ponerlo ahí.

Para usarlo, sencillamente haremos lo mismo que anteriormente:

<asp:Literal runat="server" Text="<%$Resources:nombre_del_fichero_de_recurso,nombre_del_recurso%>" />
  • SharePoint 2013: Para SharePoint 2013 Apps se puede hacer de una forma un poco diferente tal y como se comenta en este artículo de la MSDN dado que SharePoint 2013 cambia el contexto de programación, dependiendo más de desarrollo web.

 

2.- Localización en código manejado

Aquí tenemos otras dos posibilidades:

  • Localizar los recursos globalmente para todos los sitios y desarrollos

Tendríamos que crear el fichero de recursos y colocarlo en la carpeta siguiente:

  • SharePoint 2010: C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions14Resources
  • SharePoint 2013: C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions15Resources

El uso de los recursos se hará de la siguiente forma:

SPUtility.GetLocalizedString("$Resources:Nombre_del_recurso", "Nombre_del_fichero_de_recursos", language);
  • Localizar los recursos desde una solución desarrollada desde Visual Studio

Al igual que en el caso visual, tendremos que añadir una Carpeta Mapeada en la solución que estemos desarrollando, en este caso será Local Resources

LocalResources

Una vez añadida la carpeta, podemos crear un fichero de recursos y añadirle los elementos que necesitemos o, si ya lo habíamos creado, ponerlo ahí.

Para usarlo, sencillamente haremos lo mismo que anteriormente:

SPUtility.GetLocalizedString("$Resources:Nombre_del_fichero_de_recursos, Nombre_del_recurso", "GloablSiteResources", language);

 

SharePoint 2013 Apps | Incluir estilos de SharePoint en una SharePoint App Auto-hosted

Ampliando mi último artículo sobre SharePoint Apps en el que trataba el escenario de incluir el entorno (chrome) de SharePoint en una SharePoint App Auto-hosted, quería compartir cómo incluir los estilos de SharePoint 2013 para que consigamos mejorar aún más la sensación de seguir dentro del sitio y, por tanto, del contexto de SharePoint.

Supongamos que queremos conseguir el siguiente efecto

sharepointstyled-beforeafter

 

 

Tal y como especifiqué en su día cuando explicaba cómo incluir el chrome de SharePoint 2013, en la cabecera HTML de la app, debíamos tener una cabecera tal que así:

   1:  <head runat="server">
   2:      <title>Santiago Porras Rodríguez - My SharePoint App with SharePoint Style</title>
   3:      <script src="http://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js" type="text/javascript">     </script>
   4:      <script src="../Scripts/jquery-1.8.2.min.js" type="text/javascript"></script>
   5:      <script type="text/javascript">
   6:          var hostweburl;
   7:   
   8:          // Load the SharePoint resources.
   9:          $(document).ready(function () {
  10:              // Get the URI decoded app web URL.
  11:              hostweburl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
  12:              // The SharePoint js files URL are in the form: web_url/_layouts/15/resource.js
  13:              var scriptbase = hostweburl + "/_layouts/15/";
  14:              // Load the js file and continue to the success handler.
  15:              $.getScript(scriptbase + "SP.UI.Controls.js", function () {
  16:                  var chromeOptions = {
  17:                      siteUrl: hostweburl,
  18:                      userName: '',
  19:                      siteTitle: "SharePoint Styled App",
  20:                      appTitle: "SharePoint Styled App",
  21:                      appIconUrl: "/Images/MyIcon.png"
  22:                  };
  23:                  var nav = new SP.UI.Controls.Navigation("chrome_ctrl_container", chromeOptions);
  24:                  nav.setVisible(true);
  25:              });
  26:          });
  27:   
  28:          // Function to retrieve a query string value. For production purposes you may want to use a library to handle the query string.
  29:          function getQueryStringParameter(paramToRetrieve) {
  30:              var params = document.URL.split("?")[1].split("&");
  31:              var strParams = "";
  32:              for (var i = 0; i < params.length; i = i + 1) {
  33:                  var singleParam = params[i].split("=");
  34:                  if (singleParam[0] == paramToRetrieve)
  35:                      return singleParam[1];
  36:              }
  37:          }
  38:      </script>
  39:  </head>

 

De esta cabecera, vamos a tener en cuenta la línea marcada en rojo donde se establece el valor de la variable hostweburl porque la vamos a necesitar para establecer la ruta.

En el body, tal y como se ilustra en el ejemplo, tenemos el siguiente contenido:

   1:          <form id="form1" runat="server">
   2:              <div>
   3:                  Awesome!!! I'm into SharePoint 2013 Chrome!!!
   4:                  <div style="margin-top: 50px; border: 1px solid #0094ff;">
   5:                      <p>Accent text </p>
   6:                      <p>Error text </p>
   7:                      <p>In a rectangle, heavily emphasized </p>
   8:                      <p>Border of an emphasized element </p>
   9:                  </div>
  10:              </div>
  11:          </form>

 

En primer lugar, debemos incluir la hoja de estilos de SharePoint 2013, para lo cuál añadimos el siguiente código JavaScript en la cabecera (head) de la página :

   1:          loadCSS(hostweburl + "/_layouts/15/defaultcss.ashx");
   2:   
   3:          function loadCSS(url) {
   4:              var fileref = document.createElement("link")
   5:              fileref.setAttribute("rel", "stylesheet")
   6:              fileref.setAttribute("type", "text/css")
   7:              fileref.setAttribute("href", url)
   8:          }

Como podéis ver, hacemos uso de la variable hostweburl que os comenté que habría que tener en cuenta para poder alcanzar nuestro objetivo.

Después de haber incluido el código JavaScript, tan sólo tendremos que seleccionar cuál es el estilo que se adapta a nuestras pretensiones de entre los que nos indica Microsoft en este enlace y aplicarlo a los elementos del contenido, con lo que finalmente nos queda el siguiente cuerpo de la página:

   1:          <form id="form1" runat="server">
   2:              <div>
   3:                  Awesome!!! I'm into SharePoint 2013 Chrome!!!
   4:                  <div style="margin-top: 50px; border: 1px solid #0094ff;">
   5:                      <p class="ms-accentText">Accent text </p>
   6:                      <p class="ms-error">Error text </p>
   7:                      <p class="ms-emphasis">In a rectangle, heavily emphasized </p>
   8:                      <p class="ms-emphasisBorder">Border of an emphasized element </p>
   9:                  </div>
  10:              </div>
  11:          </form>

 

Y…. ¡todo listo! Llegados a este punto ya podremos aplicar los estilos de SharePoint 2013 a una SharePoint App Auto-hosted.

sharepointstyled-after

 

NOTA: Si ya tenemos incluido el «chrome» de SharePoint, se supone que ya está cargada la hoja de estilos con lo que no debería hacer falta incluirla a mano. Así que, en el artículo anterior, todo esto debería funcionar sin necesidad de cargar el fichero con los estilos.

Microsoft Active Professional (MAP) 2013, un desconocido reconocimiento que me ha llegado

Ayer me llegó por sorpresa un correo de Microsoft en el que se me indicaba que he sido nominado como Microsoft Active Professional 2013, abreviado como MAP (Me llaman Map, Bing Map… jejeje). He tenido que buscarlo porque era la primera vez que oía hablar de este reconocimiento y… ¡vaya sorpresa! Me ha alagado saber que me lo otorgan como distinción entre el 5% de los mejores profesionales. Como siempre digo, tengo mucho que aprender y hay muchísimas personas que deberían recibir reconocimientos antes que yo porque son auténticos cracks, pero lo son en la sombra porque por diferentes circunstancias no disponen de visibilidad en la comunidad. A todos ellos, les diré que se animen que hay un mundo de oportunidades y momentos de gran valor.

A Microsoft, gracias por este reconocimiento que me anima a seguir compartiendo los conocimientos y la experiencia que voy adquiriendo día a día con mi trabajo o con mis estudios e investigaciones.

A mis compañeros, en especial a mi maestro Jedi Alberto Díaz gracias por mostrarme el camino (y por los latigazos con el latigus 3000 =oP).

A todos, deciros que intentaré estar a la altura del reconocimiento en la medida de los posible.

MAP

SharePoint 2013 | Aplicar entorno (Chrome) de SharePoint en una SharePoint App

Escenario

Cuando programamos una SharePoint App “autohosted” tenemos que tener en cuenta que al instalarla se pierde el entorno de SharePoint porque lo que se hace es una especie de redirección desde SharePoint al sitio donde se ubica la App que publicamos. Por lo tanto, nuestra aplicación se verá como una web independiente de SharePoint y puede que no nos interese que sea así, sino que la aplicación esté dentro del entorno y del contexto de SharePoint 2013 manteniendo cabecera y menú.

 

Solución

Una vez hayamos creado el proyecto con la SharePoint App, tendremos una página default.aspx que por defecto está establecida como la página inicial del proyecto y que no contiene nada, salvo el nombre del sitio de SharePoint al que está asociada y que se inyecta desde el Code Behind.

SharePoint-App-Default-PageSharePoint-App-HostSharePoint-App-Project

 

Para lograr mi objetivo tenía que buscar la forma de insertar los scripts de SharePoint 2013 dentro de la página de mi aplicación y… buscando un poco en Bing (jejeje….) di con esta página de la MSDN que, aunque está un poco desactualizada, explican bien el concepto de lo que quiero y también incluyen el código que necesito para lograrlo. Pero como no es exactamente igual que como pone ahí porque hay algunas cosas que no son necesarias, quería explicarlo a mi manera.

En primer lugar, tenemos diferentes opciones, casi iguales, para incluir el Chrome de SharePoint 2013 dentro de nuestra App. Aquí exponen dos de ellos y yo me decanté por el más sencillo que es el segundo, denominado declarativo en el artículo.

   1:  <head runat="server">
   2:      <title>Santiago Porras Rodríguez - My SharePoint App with Chrome control</title>
   3:      <script src="http://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js" type="text/javascript">     </script>
   4:      <script src="../Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
   5:      <script type="text/javascript">
   6:          var hostweburl;
   7:   
   8:          // Load the SharePoint resources.
   9:          $(document).ready(function () {
  10:              // Get the URI decoded app web URL.
  11:              hostweburl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
  12:              // The SharePoint js files URL are in the form: web_url/_layouts/15/resource.js
  13:              var scriptbase = hostweburl + "/_layouts/15/";
  14:              // Load the js file and continue to the success handler.
  15:              $.getScript(scriptbase + "SP.UI.Controls.js")
  16:          });
  17:   
  18:          // Function to retrieve a query string value. For production purposes you may want to use a library to handle the query string.
  19:          function getQueryStringParameter(paramToRetrieve) {
  20:              var params = document.URL.split("?")[1].split("&");
  21:              var strParams = "";
  22:              for (var i = 0; i < params.length; i = i + 1) {
  23:                  var singleParam = params[i].split("=");
  24:                  if (singleParam[0] == paramToRetrieve)
  25:                      return singleParam[1];
  26:              }
  27:          }
  28:      </script>
  29:  </head>
  30:  <body>
  31:      <!-- Chrome control placeholder options are declared inline.  -->
  32:      <div id="chrome_ctrl_container"
  33:          data-ms-control="SP.UI.Controls.Navigation"
  34:          data-ms-options='{  
  35:                  "appHelpPageUrl" : "Help.html",
  36:                  "appIconUrl" : "/Images/MyIcon.png",
  37:                  "appTitle" : "My SharePoint App with Chrome control",
  38:                  "settingsLinks" : [
  39:                      {
  40:                          "linkUrl" : "Account.html",
  41:                          "displayName" : "Account settings"
  42:                      },
  43:                      {
  44:                          "linkUrl" : "Contact.html",
  45:                          "displayName" : "Contact us"
  46:                      }
  47:                  ]
  48:               }'>
  49:      </div>
  50:   
  51:      <!-- The chrome control also makes the SharePoint Website style sheet available to your page. -->
  52:      <h1 class="ms-accentText">Main content</h1>
  53:      <h2 class="ms-accentText">The chrome control</h2>
  54:      <div id="MainContent">
  55:          <form id="form1" runat="server">
  56:              <div>
  57:                  Awesome!!! I'm into SharePoint 2013 Chrome!!!
  58:              </div>
  59:          </form>
  60:      </div>
  61:  </body>

 

El funcionamiento es el siguiente:

  • Se carga la librería Javascript de Ajax desde el CDN de Microsoft.
  • Se carga jQuery. Yo lo he cargado localmente al proyecto pero podemos sustituirlo por el que se encuentra en el CDN de Microsoft.
  • En el bloque de Javascript que viene a continuación se encuentran dos partes bien diferenciadas:
    • Cuando se haya cargado la página, se establece la dirección del Host de SharePoint 2013 (hostweburl) y se intenta cargar la librería Javascript SP.UI.Controls.js que es la que nos interesa para nuestro caso.
    • La función getQueryStringParameter que es llamada desde el punto anterior, nos sirve para obtener la dirección del servidor de SharePoint.
  • En el código HTML tenemos que realizar varios cambios:
    • Crear un divisor al que asignaremos un id, por ejemplo “chrome_ctrl-container” y en el que tenemos que incrustar las propiedades que están establecidas en el ejemplo.
    • Crear un divisor con id “MainContent” en el que incluimos el contenido de nuestra App (más que nada para tenerlo organizado, cosa que os puede salvar de muchos problemas)
    • Además, como opcional, podemos añadir Título y subtítulo en la página tal y como se muestra en el ejemplo.

Si ahora ejecutamos nuevamente la aplicación, el resultado es bien diferente, más agradable y, como estamos trabajando en SharePoint, mucho más lógico mantener el entorno (Chrome). Me he permitido el lujo de añadir un icono a la aplicación para darle algo de vida.

 

No obstante, hay otra forma muy similar de incluir el Chrome de SharePoint en nuestra App y que consiste en modificar levemente esta solución poniendo las propiedades del container dentro del bloque javascript. Esta opción que es la que finalmente he implementado en mi proyecto, nos permite separar más la lógica seguida para incluir el Chrome de SharePoint.

   1:  <head runat="server">
   2:      <title>Santiago Porras Rodríguez - My SharePoint App with Chrome control</title>
   3:      <script src="http://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js" type="text/javascript">     </script>
   4:      <script src="../Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
   5:      <script type="text/javascript">
   6:          var hostweburl;
   7:   
   8:          // Load the SharePoint resources.
   9:          $(document).ready(function () {
  10:              // Get the URI decoded app web URL.
  11:              hostweburl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
  12:              // The SharePoint js files URL are in the form: web_url/_layouts/15/resource.js
  13:              var scriptbase = hostweburl + "/_layouts/15/";
  14:              // Load the js file and continue to the success handler.
  15:              $.getScript(scriptbase + "SP.UI.Controls.js", function () {
  16:                  var chromeOptions = {
  17:                      siteUrl: hostweburl,
  18:                      userName: '',
  19:                      siteTitle: "Tracking map site",
  20:                      appTitle: "Tracking map",
  21:                      appIconUrl: "/Images/siteLogo.png"
  22:                  };
  23:                  var nav = new SP.UI.Controls.Navigation("chrome_ctrl_container", chromeOptions);
  24:                  nav.setVisible(true);
  25:              });
  26:          });
  27:   
  28:          // Function to retrieve a query string value. For production purposes you may want to use a library to handle the query string.
  29:          function getQueryStringParameter(paramToRetrieve) {
  30:              var params = document.URL.split("?")[1].split("&");
  31:              var strParams = "";
  32:              for (var i = 0; i < params.length; i = i + 1) {
  33:                  var singleParam = params[i].split("=");
  34:                  if (singleParam[0] == paramToRetrieve)
  35:                      return singleParam[1];
  36:              }
  37:          }
  38:      </script>
  39:  </head>
  40:  <body>
  41:      <!-- Chrome control placeholder options are declared inline.  -->
  42:      <div id="chrome_ctrl_container"></div>
  43:   
  44:      <!-- The chrome control also makes the SharePoint Website style sheet available to your page. -->
  45:      <h1 class="ms-accentText">Main content</h1>
  46:      <h2 class="ms-accentText">The chrome control</h2>
  47:      <div id="MainContent">
  48:          <form id="form1" runat="server">
  49:              <div>
  50:                  Awesome!!! I'm into SharePoint 2013 Chrome!!!
  51:              </div>
  52:          </form>
  53:      </div>
  54:  </body>

El resultado final es el mismo que en la opción anterior como podréis comprobar

 

Finalmente, quería recordaros que para quitar la palabra “Dev” que me aparece a mi y que se corresponde con el nombre del sitio de SharePoint al que está vinculado la aplicación, tendréis que ir al archivo de Code Behind de la página, es decir a default.aspx.cs donde deberéis eliminar la línea siguiente:

   1: Response.Write(clientContext.Web.Title);

 

También podéis eliminar el resto si no vais a usar el contexto de SharePoint.

LightSwitch | Envío de Notificaciones Push (Push Notifications) a Windows Phone desde LightSwitch

En mi anterior artículo, hice una introducción de cómo construir una aplicación de LightSwitch para SharePoint en la que el escenario se centraba en poder enviar notificaciones push a usuarios de Windows Phone cada vez que se insertara un elemento en una lista de SharePoint. En este caso compartiré el código para realizar estos envíos.

Escenario

Teniendo una aplicación de LightSwitch en SharePoint que graba alertas en una lista, pretendemos que tras insertar el registro (o actualizarlo) se envíe una notificación push a los dispositivos Windows Phone registrados en una lista.

Solución

Si seguisteis el anterior artículo,  veríais que en el código de la aplicación, concretamente en el Helper “AlertsHelper”, programé un método AddAlert que se encargaba de registrar la alerta en SharePoint y, además, tenía una llamada a la función SendPushNotification. Este es el momento de desvelar el código de dicho método por si algún día necesitáis hacer lo mismo.

   1: public static void SendPushNotification(Alert alert, ClientContext siteContext)

   2: {

   3:     var alertId = "ID";

   4:     List subscribersList = siteContext.Web.Lists.GetByTitle("SubscribersList");

   5:     siteContext.Load(subscribersList);

   6:     siteContext.ExecuteQuery();

   7:  

   8:     CamlQuery query = new CamlQuery();

   9:     query.ViewXml = "";

  10:     var subscriberCollection = subscribersList.GetItems(query);

  11:     siteContext.Load(subscriberCollection);

  12:     siteContext.ExecuteQuery();

  13:  

  14:     foreach (var subscriber in subscriberCollection)

  15:     {

  16:         try

  17:         {

  18:             var subscriptionUri = subscriber["ChannelUri"].ToString();

  19:  

  20:             // Create the toast message.

  21:             string toastMessage = "<?xml version="1.0" encoding="utf-8"?>" +

  22:                                     "<wp:Notification xmlns:wp="WPNotification">" +

  23:                                         "<wp:Toast>" +

  24:                                             "<wp:Text1>" + alert.Title + "</wp:Text1>" +

  25:                                             "<wp:Text2>" + alert.Body + "</wp:Text2>" +

  26:                                             "<wp:Param>/MainPage.xaml?AlertId=" + alertId + "</wp:Param>" +

  27:                                         "</wp:Toast> " +

  28:                                     "</wp:Notification>";

  29:  

  30:             SendMessage(toastMessage, new Uri(subscriptionUri), "toast", "2");

  31:  

  32:             string tileMessage = "<?xml version="1.0" encoding="utf-8"?>" +

  33:                                     "<wp:Notification xmlns:wp="WPNotification">" +

  34:                                         "<wp:Tile>" +

  35:                                               "<wp:Count>1</wp:Count>" +

  36:                                             "<wp:BackTitle >" + alert.Title + "</wp:BackTitle >" +

  37:                                             "<wp:BackContent>" + alert.Body + "</wp:BackContent>" +

  38:                                         "</wp:Tile> " +

  39:                                     "</wp:Notification>";

  40:  

  41:  

  42:             SendMessage(tileMessage, new Uri(subscriptionUri), "token", "1");

  43:  

  44:         }

  45:         catch (Exception)

  46:         {

  47:         }

  48:     }

  49: }

  50:  

  51: private static void SendMessage(string message, Uri subscriptionUri, string target, string notificationClass)

  52: {

  53:     HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri);

  54:     sendNotificationRequest.Method = "POST";

  55:  

  56:     // Set the notification payload to send.

  57:     byte[] notificationMessage = Encoding.Default.GetBytes(message);

  58:  

  59:     // Set the web request content length.

  60:     sendNotificationRequest.ContentLength = notificationMessage.Length;

  61:     sendNotificationRequest.ContentType = "text/xml";

  62:     sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", target);

  63:     sendNotificationRequest.Headers.Add("X-NotificationClass", notificationClass);

  64:  

  65:     using (Stream requestStream = sendNotificationRequest.GetRequestStream())

  66:     {

  67:         requestStream.Write(notificationMessage, 0, notificationMessage.Length);

  68:     }

  69:  

  70:     // Send the notification and get the response.

  71:     HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse();

  72:     string notificationStatus = response.Headers["X-NotificationStatus"];

  73:     string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];

  74:     string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];

  75: }

 

Explicación

En primer lugar, tenemos el método SendPushNotification que recibe como parámetros la alerta que queremos enviar a los dispositivos con Windows Phone y el contexto del sitio de SharePoint que nos servirá para obtener los dispositivos a los que tenemos que hacer el envío de la alerta y que están almacenados en una lista de SharePoint.

  1. Como podéis observar, obtengo la lista de suscriptores por medio del contexto de sharepoint que le he pasado al método y, posteriormente ejecuto una consulta con CAML Query (sin filtro) para obtener todos los registros de la lista.
  2. Una vez obtengo todos los registros, he de recorrer la colección de suscriptores creando un Toast Notification y, además, he añadido también un Tile Notification para que se vea si la aplicación está anclada al inicio. Ambos tipos de notificación son creados por medio del XML que los genera.
  3. Haciendo uso de la URI correspondiente al canal del dispositivo y que obtengo del registro del suscriptor, hago una llamada al método SendMessage que es el encargado de abrir la petición y efectuar el envío. Esta llamada se realiza para cada uno de los tipos de notificación que he creado.
  4. El método SendMessage abre la petición HTTP con la URI del canal del dispositivo, codifica el XML de la alerta en un array de Bytes, establece la cabecera HTTP en la que se especifica el tipo de notificación y su nivel de prioridad y realiza el envío de la alerta.
  5. Por último, abro una conexión con la respuesta HTTP y, aunque no la estoy usando, podría comprobar el resultado del envío de la notificación. Lo he puesto para que comprobéis que se puede saber si la notificación ha sido enviada correctamente.