<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Juanma Santoyo &#187; .net</title>
	<atom:link href="http://www.juanmasantoyo.es/index.php/category/net/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.juanmasantoyo.es</link>
	<description>En ocasiones me llaman friki</description>
	<lastBuildDate>Thu, 19 Jan 2012 06:33:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>Introducción a Entity framework Code First</title>
		<link>http://www.juanmasantoyo.es/index.php/2011/10/22/introduccion-a-entity-framework-code-first/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2011/10/22/introduccion-a-entity-framework-code-first/#comments</comments>
		<pubDate>Sat, 22 Oct 2011 19:17:07 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[efcf]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=648</guid>
		<description><![CDATA[En general, para usar Entity Framework; debemos tomar como punto de partida una base de datos existente. Por lo tanto, sin base de datos no podemos crear el modelo de persistencia. Pero todo eso ha cambiado ya que desde hace &#8230; <a href="http://www.juanmasantoyo.es/index.php/2011/10/22/introduccion-a-entity-framework-code-first/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>En general, para usar Entity Framework; debemos tomar como punto de partida una base de datos existente. Por lo tanto, sin base de datos no podemos crear el modelo de persistencia.</p>
<p>Pero todo eso ha cambiado ya que desde hace unos pocos meses disponemos de <em>Entity framework Code First</em>. <em>EFCF</em> nos permite crear el modelo de base de datos tomando como punto de partida una serie de clases. Estas clases, serán nuestro modelo de persistencia, y también serán el punto de partida desde el cual <em>EFCF</em> creará el esquema de la base de datos.<br />
<span id="more-648"></span></p>
<h3>Convención sobre configuración</h3>
<p>Una de las grandes cosas de <em>EFCF</em> es que sigue una pauta de &#8220;Convención sobre configuración&#8221;. Esto es, que <strong>nosotros no debemos configurar todas las cosas que programamos en nuestro modelo de persistencia, si no que <em>EFCF</em> lo hará por nosotros</strong> siempre que sigamos las convenciones de <em>EFCF</em>. Por lo tanto, cuando programemos nuestro modelo de persistencia deberemos conocer y aplicar dichas convenciones.</p>
<p>Las convenciones de las que hablo en realidad son muy pocas y muy coherentes. Ahora puede parecer que son un inconveniente por tener que conocerlas todas, pero nada más lejos de la realidad: veremos cómo gracias a esta política el desarrollo de un modelo de persistencia se vuelve un proceso simple y muy rápido.</p>
<p>Por supuesto, todas las convenciones son configurables. Si quieres complicarte la vida, tú mismo con tu mecanismo…</p>
<p>Por ejemplo, supongamos que estamos creando la clase de persistencia para la tabla <em>Producto</em>. Lo primero que deberíamos hacer, es crear un campo que sea la clave primaria. ¿Cómo determinamos cuál es ese campo?. Fácil: Por convención, la clave primaria será aquel campo numérico llamado <em>Id</em>. Cabe destacar que nada en <em>EFCF</em> es case-sensitive (aunque el modelo de persistencia sí lo es).</p>
<h3>Ejemplo práctico</h3>
<p>Vamos a hacer un pequeño ejemplo para ilustrar el funcionamiento de <em>EFCF</em>. Crearemos un pequeño modelo de persistencia para dos tablas: <em>Producto</em> y <em>Categoría</em>.</p>
<p>Ya podemos imaginar el resto de la historia: ambas tablas tendrán un identificador y un nombre. Además, la tabla producto estará relacionada con la tabla categoría; de forma que un producto tenga una categoría asociada y desde una categoría se pueda acceder a la colección de todos los productos asociados.</p>
<h4>Instalando Entity framework Code First</h4>
<p>Deberemos descargar e instalar <em>EFCF</em> para poder dotar a Visual Studio de las nuevas características. <a title="Entity framework Code First" href="http://www.microsoft.com/download/en/details.aspx?displaylang=en&amp;id=26660">Descarga Entity Framework Code First aquí</a>.</p>
<p>El orden de instalación es:</p>
<ol>
<li>EF_JUNE_2011_CTP.msi</li>
<li>DS_JUNE_2011_CTP.msi</li>
<li>EFTools.msi</li>
</ol>
<h4>Las entidades</h4>
<p>Crearemos un proyecto de tipo <em>Librería de clases</em> donde programaremos las clases que representarán las entidades. Atención aquí: debemos crear el proyecto bajo el framework <em>Microsoft Entity Framework June 2011 CTP</em>. El proyecto se llamará <em>EFCFTest.Entities</em>.</p>
<p><img class="aligncenter size-full wp-image-671" title="crear entities" src="http://www.juanmasantoyo.es/wp-content/uploads/2011/10/crear-entities.png" alt="crear entities" width="800" height="450" /></p>
<p>Crearemos una clase llamada <em>Categoria.cs</em>. En esta clase crearemos dos campos: uno será el identificador y el otro el nombre de la categoría.</p>
<pre class="brush:csharp">namespace EFCFTest.Entities
{
    public class Categoria
    {
        public long Id {get; set;}
        public string Nombre { get; set; }
    }
}</pre>
<p>Como veis, el campo <em>Id</em> es numérico. Un campo que se llame <em>Id</em> y sea numérico se convierte por convención en la clave primaria de la entidad. Además, es autoincrementable.</p>
<p>Vamos a crear ahora la clase <em>Producto.cs</em>.</p>
<pre class="brush:csharp">namespace EFCFTest.Entities
{
    public class Producto
    {
        public long Id { get; set; }
        public string Nombre { get; set; }
        public int Stock { get; set; }
    }
}</pre>
<p>Esta clase se parece mucho a la anterior, pero le he añadido un campo de Stock.</p>
<p>Solo nos queda un pequeño pero importantísimo detalle: Tenemos que programar la relación entre las entidades. Lo lógico aquí es que un producto se clasifique en una categoría y que una categoría tenga asociada una colección de productos. Pues no se hable más, vamos allá:</p>
<p>Empecemos por la clase <em>Producto</em>. Por convención, para asociar una entidad <em>Categoría</em> a la clase producto lo haremos añadiendo estos campos a la clase <em>Producto</em>.</p>
<pre class="brush:csharp">public long CategoriaId { get; set; }
public virtual Categoria Categoria { get; set; }</pre>
<p>Voy a explicar primero la segunda de estas dos propiedades: cumple dos características muy importantes:</p>
<ul>
<li>Es del tipo <em>Categoria</em>. Esto es así porque su valor será la entidad categoría con en la que está clasificado el producto.</li>
<li>Es virtual. Esto es importante, ya que determina que es una propiedad de navegación.</li>
</ul>
<p>La primera propiedad también cumple dos características:</p>
<ul>
<li>Es del mismo tipo que la clave primaria de <em>Categoria</em>. Esto tiene que ser así porque almacenará dicho valor.</li>
<li>El nombre de la propiedad es la concatenación de el nombre de la propiedad de navegación <em>Categoria</em> y el nombre de la clave primaria en la entidad <em>Categoria</em>, que es <em>Id</em>.</li>
</ul>
<p>Ahora vamos a crear la relación desde la clase <em>Categoria</em>. Aquí lo que queremos es una colección de productos, por lo que añadiremos la siguiente propiedad:</p>
<pre class="brush:csharp">public virtual ICollection&lt;Producto&gt; Productos { get; set; }</pre>
<p>Los aspectos a destacar en esta propiedad son:</p>
<ul>
<li>Al igual que la propiedad <em>Categoria</em> de la entidad <em>Producto</em>, es una propiedad de navegación y por lo tanto debe ser virtual.</li>
<li>Lógicamente, el tipo de la colección es <em>Producto</em>.</li>
</ul>
<p>Las colecciones merecen un tratamiento un poco especial, ya que por defecto las colecciones son referencias vacías. Podemos modificar el objeto para que las colecciones vengan siempre inicializadas. Es sencillo, sólo deberemos añadir un constructor en nuestra clase. Realmente, en el constructor podríamos inicializar cualquier valor que queramos:</p>
<pre class="brush:csharp">public Categoria()
{
	Productos = new List();
}</pre>
<p>Con esto ya hemos acabado con las entidades. Como veis, el principio de convención sobre configuración nos obliga a hacer las cosas de una determinada manera, pero las convenciones son sencillas de aprender y nos ahorrarán mucho tiempo.</p>
<h4>El contexto.</h4>
<p>El contexto es el entorno que nos permitirá trabajar con las Entidades. Se trata de un objeto que usaremos para acceder a la capa de persistencia.</p>
<p>Vamos a crear un nuevo proyecto tipo <em>Librería de clases</em> donde programaremos el contexto de nuestra aplicación. Al igual que el anterior, este proyecto debe crearse bajo el framework <em>Microsoft Entity Framework June 2011 CTP</em>.</p>
<p><img class="aligncenter size-full wp-image-670" title="crear context" src="http://www.juanmasantoyo.es/wp-content/uploads/2011/10/crear-context.png" alt="crear context" width="800" height="450" /></p>
<p>Este nuevo proyecto, extenderá de clases propias de <em>EFCF</em>, por lo que deberemos añadir una referencia a su DLL. Si has instalado <em>EFCF</em> tal y como indica este artículo, deberías encontrar la DLL en <em>C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.2\System.Data.Entity.dll</em>. Esto no tiene porqué ser así en tu entorno, ya que hay otras formas de instalar <em>EFCF</em>. También habrá que añadir una referencia al proyecto <em>EFCFTest.Entities</em> para poder hacer uso de las entidades que hemos creado previamente.</p>
<p>De momento, sólo vamos a añadir una clase. Quizás parezca absurdo crear un proyecto que va a contener una única clase, pero es más una cuestión de concepto y de escalabilidad: El contexto y las entidades no son exactamente lo mismo, por lo que no deberían estar juntos. El día de mañana, podríamos necesitar más contextos, o quizás necesitemos añadir lógica adicional a nuestro contexto.</p>
<p>La clase se llamará <em>EFCFTestContext</em>. Este nombre es importante, ya que por convención, es el nombre que recibirá el esquema de la base de datos que cree el contexto (el contexto es el encargado de crear la base de datos si no existe).</p>
<p>La clase es sencilla, sólo necesitará el siguiente código:</p>
<pre class="brush:csharp">using System.Data.Entity;
using VoyAlCine.Entities;
namespace EFCFTest.Context
{
    public class EFCFTestContext : DbContext
    {
        public DbSet&lt;Categoria&gt; Categorias { get; set; }
        public DbSet&lt;Producto&gt; Productos { get; set; }
    }
}</pre>
<p>Como se puede ver, también he indicado los using que vamos a necesitar para importar las referencias que acabamos de añadir.</p>
<p>Las cosas a destacar de éste código son:</p>
<ul>
<li>La clase contexto hereda de DbContext. Esta es una clase de la DLL de <em>EFCF</em>.</li>
<li>Crearemos un DbSet por cada entidad que hayamos creado. La clase DbSet también es parte de <em>EFCF</em>.</li>
</ul>
<p>Sólo queda una cosa por decir sobre la clase contexto: sería lógico querer cambiar el nombre del esquema de la base de datos. Esto es sencillo, solo debemos añadir un constructor que sobre escriba la llamada al constructor de DbContext. Como parámetro, le pasaremos el nombre que deseamos darle a la base de datos:</p>
<pre class="brush:csharp">public EFCFTestContext() : base("EFCFTest")
{
}</pre>
<h3>Creamos una aplicación de prueba</h3>
<p>Por último, vamos a crear una aplicación que nos sirva para realizar algunas pruebas sobre nuestro nuevo modelo de persistencia.</p>
<p>Vamos a crear una aplicación de consola llamada <em>EFCFTest.ConsoleApp</em>. No hay que olvidar que debemos seleccionar el framework <em>Microsoft Entity Framework June 2011 CTP</em>.</p>
<p>Lo primero que haremos será agregar a las referencias los proyectos de entidades y contexto. También debemos añadir una referencia a la DLL <em>System.Data.Entity.dll</em>, tal y como hicimos en el proyecto de contexto.</p>
<p><img class="aligncenter size-full wp-image-669" title="crear consoleapp" src="http://www.juanmasantoyo.es/wp-content/uploads/2011/10/crear-consoleapp.png" alt="crear consoleapp" width="800" height="450" /></p>
<p>Otra cosa importante que debemos hacer, es añadir la cadena de conexión a la base de datos. Esta cadena de conexión se debe añadir al <em>app.config</em> del proyecto, en el nodo <em>configuration/connectionStrings</em>. La cadena de conexión deberá ser algo como:</p>
<pre class="brush:plain">Data Source=&lt;Servidor de la base de datos&gt;;Initial catalog=&lt;em&gt;EFCF&lt;/em&gt;Test;Integrated Security=True;MultipleActiveResultSets=True</pre>
<p>Atención al parámetro <em>Data Source</em>. Este debe ser el servidor donde esté la base de datos. He puesto las marcas <em>&lt;</em> y <em>&gt;</em> por legibilidad, pero debéis quitarlas cuando pongáis el servidor.</p>
<p>Al final, vuestro app.config debe quedar más o menos así:</p>
<pre class="brush:xml">&lt;configuration&gt;
  &lt;!-- … --&gt;
  &lt;connectionStrings&gt;
    &lt;add name="VoyAlCineContext"
         connectionString="Data Source=&lt;Servidor de la base de datos&gt;;Initial catalog=&lt;em&gt;EFCF&lt;/em&gt;Test;Integrated Security=True;MultipleActiveResultSets=True"
         providerName="System.Data.SqlClient"/&gt;
  &lt;/connectionStrings&gt;
&lt;/configuration&gt;</pre>
<p>El parámetro <em>providerName</em> es muy importante.</p>
<h4>Crear registros</h4>
<p>Vamos a crear algunos registros. Lo primero que debemos hacer es crear una instancia del contexto al cual queramos acceder:</p>
<pre class="brush:csharp">EFCFTestContext context = new &lt;em&gt;EFCF&lt;/em&gt;TestContext();

Vamos a crear una categoría y le vamos a asociar dos productos:

Categoria Quesos = new Categoria();
Quesos.Nombre = "Quesos";

Quesos.Productos.Add(new Producto()
{
  Nombre = "Queso Edam"
  , Stock = 10
});

Quesos.Productos.Add(new Producto()
{
  Nombre = "Queso Havarti"
  , Stock = 5
});

context.Categorias.Add(quesos);

context.SaveChanges();</pre>
<p>Lo que he hecho es lo siguiente: He creado un objeto <em>Categoria</em>, le he inicializado la propiedad <em>Nombre</em>, y después le he incluido en la colección de productos dos objetos <em>Producto</em>. Por último, lo más importante: he añadido la categoría al contexto, y he persistido los cambios con el método <em>SaveChanges</em>. Este último paso es esencial, ya que es lo que guarda en base de datos todos los objetos que hemos añadido al contexto.</p>
<p>Para tener un conjunto de datos más decente, voy a crear un par de categorías más, con sus respectivos productos. Haré algunas modificaciones sobre el código anterior que no voy a reflejar en este artículo por cuestiones de legibilidad. Como siempre, al final encontraréis una versión descargable de este ejemplo donde podréis consultar todo el código fuente.</p>
<p>Si ahora ejecutamos nuestra aplicación de consola, veremos cómo se crea nuestra base de datos y se inicializa con unos cuantos datos. Vamos a añadir un poco de código más al programa. Este código sólo servirá para monitorizar lo que está ocurriendo, y para evitar que la aplicación se cierre automáticamente (todas las aplicaciones de consola se cierran al acabar, a no ser que nosotros programemos que no sea así).</p>
<p>El código que añadiremos será:</p>
<ol>
<li>Después de añadir una categoría al contexto añadiremos ésta línea:
<pre class="brush:csharp">Console.WriteLine("Se ha creado la categoría {0}", Quesos.Nombre);</pre>
</li>
<li>Al final de nuestro programa, forzaremos que la aplicación espere la introducción de la tecla enter, de forma que no se cerrará hasta que se pulse enter:
<pre class="brush:csharp">Console.WriteLine("Operación finalizada. Pulsa ENTER para salir...");
Console.ReadLine();</pre>
</li>
</ol>
<p>Por último, antes de ejecutar la aplicación. Hay que tener en cuenta que se deba establecer la aplicación de consola como el proyecto de inicio:</p>
<p style="text-align: center;"><img class="aligncenter size-full wp-image-690" title="establecer como proyecto de inicio" src="http://www.juanmasantoyo.es/wp-content/uploads/2011/10/establecer-proyecto-inicio.png" alt="establecer como proyecto de inicio" width="590" height="400" /></p>
<p>&nbsp;</p>
<p>Lo que ocurra en nuestra pantalla debería parecerse a esto:</p>
<p><img class="aligncenter size-full wp-image-673" title="resultados creacion" src="http://www.juanmasantoyo.es/wp-content/uploads/2011/10/resultados-creacion.png" alt="resultados creacion" width="677" height="342" /></p>
<p>Seguramente habréis notado un tiempo de inactividad excesivo al ejecutar la aplicación por primera vez. Esto se debe a que la base de datos no existía y se ha creado. Si accedéis a la base de datos con un gestor como el Microsoft SQL Management Studio o similar, podréis ver cómo se ha creado la base de datos:</p>
<p><img class="aligncenter size-full wp-image-681" title="bdd creada" src="http://www.juanmasantoyo.es/wp-content/uploads/2011/10/bdd-creada.png" alt="bdd creada" width="302" height="360" /></p>
<p>Por supuesto, también se habrán creado los registros que hemos programado.</p>
<h4>Seleccionar registros</h4>
<p>Ahora vamos a ver cómo podemos seleccionar registros de la base de datos.</p>
<p>Lo primero que haremos será comentar el código anterior, ya que no queremos que se vuelvan a crear más registros. Sólo dejaremos sin comentar las líneas que crean el objeto Context y las que pausan la ejecución del programa. Comentaremos incluso la llamada al método SaveChanges, ya que no queremos modificar el contenido de la base de datos.</p>
<p>Hay varias maneras de acceder al contenido. Por ejemplo, desde el objeto Context podríamos obtener un listado con todas las categorías:</p>
<pre class="brush:csharp">Console.WriteLine("nTodas las categorías:");
List&lt;Categoria&gt; Categorias = Context.Categorias.ToList&lt;Categoria&gt;();
foreach (Categoria Categoria in Categorias)
{
    Console.WriteLine("{0}t{1}", Categoria.Id, Categoria.Nombre);
}</pre>
<p>Podríamos usar <em>linq</em>, personalmente es el método que más me gusta:</p>
<pre class="brush:csharp">var ProductosQueso = from p in Context.Productos
where p.Nombre.ToLower().Contains("queso")
      select p;

Console.WriteLine("nProductos cuyo nombre contiene la subcadena "queso":");
foreach (Producto Producto in ProductosQueso)
{
    Console.WriteLine("t{0}.", Producto.Nombre);
}</pre>
<p><em>Linq</em> nos permitiría incluso hacer joins entre tablas:</p>
<pre class="brush:csharp">ProductosQueso = from p in Context.Productos
join c in Context.Categorias on p.Categoria equals c
where c.Nombre.ToLower() == "Quesos"
select p;

Console.WriteLine("nProductos en la categoría "Quesos":");
foreach (Producto Producto in ProductosQueso)
{
    Console.WriteLine("t{0}.", Producto.Nombre);
}</pre>
<p>También podríamos usar las <em>lambda expressions</em> de <em>linq</em>. Hacen el uso de <em>linq</em> más simple, pero no son tan potentes como usar <em>linq</em> directamente, y complican la lectura del código:</p>
<pre class="brush:csharp">List&lt;Producto&gt; ProductosFrutosSecos = Context.Productos.Where(
p =&gt; p.Categoria == Context.Categorias.FirstOrDefault(c =&gt; c.Nombre.ToLower() == "frutos secos"))
.ToList&lt;Producto&gt;();

Console.WriteLine("nProductos en la categoría "Frutos secos":");
foreach (Producto Producto in ProductosFrutosSecos)
{
    Console.WriteLine("t{0}.", Producto.Nombre);
}</pre>
<p>Por último, podríamos acceder a los elementos según el valor de su clave primaria. Es el método más simple de todos ya que permite seleccionar contenidos sin el uso de <em>linq</em>, pero sólo podríamos usarlo sabiendo la clave primaria del elemento que necesitamos, y no permite seleccionar conjuntos (solo elementos aislados):</p>
<pre class="brush:csharp">Categoria CategoriaRefrescos = Context.Categorias.Find(2);
Console.WriteLine("nProductos en la categoría "Refrescos":");
foreach (Producto Producto in CategoriaRefrescos.Productos)
{
    Console.WriteLine("t{0}.", Producto.Nombre);
}</pre>
<p><img class="aligncenter size-full wp-image-674" title="resultados listado" src="http://www.juanmasantoyo.es/wp-content/uploads/2011/10/resultados-listado.png" alt="resultados listado" width="677" height="366" /></p>
<pre class="brush:csharp"></pre>
<h4>Borrar registros</h4>
<p>Borrar los registros existentes no puede ser más sencillo:</p>
<pre class="brush:csharp">Categoria CategoriaFrutosSecos = (from c in Context.Categorias
    where c.Nombre.ToLower() == "frutos secos"
    select c).First&lt;Categoria&gt;();

Context.Categorias.Remove(CategoriaFrutosSecos);

Console.WriteLine("nSe ha borrado la categoría {0}", (CategoriaFrutosSecos).Nombre);</pre>
<p>Cómo veis, tan solo hay que localizar el registro a borrar y usar el método Remove.</p>
<h4>Descarga el ejemplo</h4>
<p>Como intento hacer siempre, os dejo la <a href="http://www.juanmasantoyo.es/wp-content/uploads/2011/10/EFCFTest.Entities.zip">descarga del ejemplo</a>.</p>
<h3>¡Hemos terminado!</h3>
<p>En éste artículo hemos explicado cómo crear una base de datos y su modelo de persistencia con <em>Entity framework Code First</em> y también hemos añadido varios ejemplos de cómo usarla. Ha sido un artículo muy completo que espero que os resulte útil.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2011/10/22/introduccion-a-entity-framework-code-first/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Instalar el entorno de desarrollo de Surface 2</title>
		<link>http://www.juanmasantoyo.es/index.php/2011/08/01/instalar-el-entorno-de-desarrollo-de-surface-2/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2011/08/01/instalar-el-entorno-de-desarrollo-de-surface-2/#comments</comments>
		<pubDate>Mon, 01 Aug 2011 15:34:04 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[surface]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=637</guid>
		<description><![CDATA[Instalar el entorno de desarrollo para la Surface 2 es relatívamente sencillo, ya que el proceso es similar a la instalación del entorno para Surface 1. De hecho es más sencillo si cabe, ya que en ésta ocasión la instalación &#8230; <a href="http://www.juanmasantoyo.es/index.php/2011/08/01/instalar-el-entorno-de-desarrollo-de-surface-2/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Instalar el entorno de desarrollo para la Surface 2 es relatívamente sencillo, ya que el proceso es similar a la instalación del entorno para Surface 1. De hecho es más sencillo si cabe, ya que en ésta ocasión la instalación es totalmente compatible con Windows 7 y Visual Studio 2010, por lo que nos ahorraremos problemas.</p>
<p>Paso a comentar qué hace falta instalar con sus respectivos enlaces de descarga.<br />
<span id="more-637"></span> Visual Studio 2010</p>
<p>Obviamente, lo primero es contar con un buen entorno de programación. Los que aún no tengáis un Visual Studio 2010 en vuestras manos, sabed que se pueden obtener licencias gratuitas desde el programa para estudiantes <a title="Dreamspark" href="https://www.dreamspark.com/default.aspx">Deamspark</a>. Si no sois estudiantes, podéis haceros con una versión de <a title="Visual C# 2010 Express" href="http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-csharp-express">Visual C# 2010 Express</a>, que no tiene tantas funcionalidades como un Visual Studio; pero es gratiíto y sigue siendo un bien IDE.</p>
<p>Mi recomendación es que os hagáis con las versiones en inglés.</p>
<h3>XNA Framework redistributable 4.0</h3>
<p>Al igual que el SDK de Surface 1 requería del XNA Framework redistributable 2.0, para el SDK de Surface 2 necesitaremos el <a title="XNA Framework redistributable 4.0" href="http://www.microsoft.com/download/en/details.aspx?id=20914">XNA Framework redistributable 4.0</a>. Se puede descargar fácilmente desde el centro de descargas de Microsoft.</p>
<h3>SDK de Surface 2</h3>
<p>Ahora ya tenemos todo lo que necesitamos para instalar el <a title="Surface 2 SDK" href="http://www.microsoft.com/download/en/details.aspx?displaylang=en&amp;id=26716">SDK de Surface 2</a>. También se puede bajar desde el centro de descargas de Microsoft.</p>
<h3>Empezar a programar con el nuevo SDK</h3>
<p>Para empezar a tocar, podéis echarle un ojo a la <a title="Documentación de Surface 2 SDK en MSDN" href="http://msdn.microsoft.com/en-us/library/ff727815(Surface.20).aspx">nueva documentación de MSDN</a>.</p>
<p>¡A Programar!. Hay que hacer una mención especial a la nueva interfaz metro, y las nuevas herramientas de desarrollo. Ahora el emulador está totalmente integrado en Windows 7 y es mucho más agradable para los desarrolladores. Sobre todo si contamos con una pantalla táctil.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2011/08/01/instalar-el-entorno-de-desarrollo-de-surface-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Crear eventos propios en aplicaciones de Surface</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/10/02/crear-eventos-propios-en-aplicaciones-de-surface/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/10/02/crear-eventos-propios-en-aplicaciones-de-surface/#comments</comments>
		<pubDate>Sat, 02 Oct 2010 08:34:41 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[surface]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=535</guid>
		<description><![CDATA[En las aplicaciones de Surface, los eventos son básicos. La mayoría de las veces, nos bastará con los eventos que nos proporciona el SDK, pero en ocasiones necesitamos algo más específico, o incluso reaccionar a una combinación de eventos. Necesitamos &#8230; <a href="http://www.juanmasantoyo.es/index.php/2010/10/02/crear-eventos-propios-en-aplicaciones-de-surface/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>En las aplicaciones de Surface, los eventos son básicos.</p>
<p>La mayoría de las veces, nos bastará con los eventos que nos proporciona el SDK, pero en ocasiones necesitamos algo más específico, o incluso reaccionar a una combinación de eventos. Necesitamos eventos propios: poder disparar el evento cuando queramos, pasar la información que queramos; y que otra clase que esté escuchando el evento realice una acción.</p>
<p>De eso hablaré en este artículo. Comentaré un pequeño ejemplo donde veremos cómo podemos implementar un evento de doble tap (lo llamaremos <em>DoubleTap</em>). Este evento no viene implementado en el SDK de Surface, y como análogo al por todos conocido &#8220;Doble Click&#8221; de los PC, puede resultar muy útil.</p>
<p><span id="more-535"></span></p>
<h3>Implementación de un evento DoubleTap.</h3>
<p>Vamos a organizarnos un poco: ¿cómo vamos a implementar el <em>DoubleTap</em>?. Pues bien, en esencia un <em>DoubleTap</em> no es más que dos <em>ContactDown </em>en un periodo corto de tiempo. El <em>ContactDown </em>es un evento que sí que viene implementado en el SDK, así que sólo necesitamos almacenar el instante en el que se realizó el primer <em>ContactDown </em>para comprobar si el tiempo entre dos <em>ContactDown </em>es lo suficientemente corto. De serlo, se dispararía el <em>DoubleTap</em>. Es decir, que necesitamos:</p>
<ul>
<li>Un manejador para el evento <em>ContactDown</em>.</li>
<li>Una propiedad global de tipo <em>DateTime </em>que almacenará el momento del último <em>ContactDown</em>.</li>
</ul>
<p>Todo esto lo programaríamos dentro del objeto al que queremos dotar de la capacidad de lanzar <em>DoubleTap</em>. En nuestro caso, será un control de usuario (lo normal).</p>
<h3>Programamos el control de usuario.</h3>
<p>Gráficamente lo haremos fácil: un rectángulo. Este es el <em>XAML</em>:</p>
<pre class="brush:xml">&lt;s:SurfaceUserControl x:Class="ProgramarEventosPropios.DoubleTapUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"&gt;
    &lt;Grid&gt;
        &lt;Rectangle Fill="Red" Width="100" Height="100" /&gt;
    &lt;/Grid&gt;
&lt;/s:SurfaceUserControl&gt;</pre>
<p>Y ahora, el<em> C#</em>. Aquí es donde programaremos el evento <em>DoubleTap</em>, pero primero, vayamos a lo principal: gestionar el evento <em>ContactDown</em> y el tiempo entre dos <em>ContactDown</em>:</p>
<pre class="brush:csharp">DateTime LastContactDown = DateTime.MinValue;
TimeSpan MaxMilis = TimeSpan.FromMilliseconds(700);

protected override void OnContactDown(ContactEventArgs e)
{
    base.OnContactDown(e);

    if (this.LastContactDown == DateTime.MinValue)
    {
        this.LastContactDown = DateTime.Now;
    }
    else
    {
        TimeSpan time = DateTime.Now - this.LastContactDown;

         if (time &lt;= this.MaxMilis)
         {
             RaiseDoubleTapEvent(time);
         }

         this.LastContactDown = DateTime.MinValue;
    }
}</pre>
<p>Varias cosas a comentar de este código:</p>
<ol>
<li>La variable <em>MaxMilis </em>sólo define el tiempo máximo entre <em>ContactDown</em>. Únicamente cuando el tiempo es inferior o igual, se considera un <em>DoubleTap</em>.</li>
<li>Por otra parte, mirad cómo asigno el evento <em>ContactDown</em>. Lo normal es asignar un manejador, pero en este caso hago una sobreescritura del método <em>OnContactDown</em>. Los eventos también se pueden asignar así, aunque sinceramente, yo prefiero añadir un manejador en el <em>XAML </em>(en esta ocasión lo hago con sobreescritura a modo ilustrativo).</li>
<li>Por último, fijaos que en caso de cumplirse el <em>DoubleTap</em>, ejecuto el método <em>RaiseDoubleTapEvent </em>con el parámetro <em>time</em>. El método lo veremos ahora, se trata del disparador del evento. Los disparadores no son necesarios, pero sí muy recomendables. El parámetro time lo paso por que es la información que quiero enviar con el evento (en un objeto <em>EventArgs</em>). De esta forma, los manejadores del evento <em>DoubleTap </em>pueden saber cuánto tiempo pasó entre los dos <em>ContactDown </em>(no es una información demasiado útil, pero para ilustrar como funcionan los objetos <em>EventArgs </em>nos viene de perlas).</li>
</ol>
<p>Y ahora empieza lo bueno: programamos el evento.</p>
<p>En realidad, hay que programar varias cosas:</p>
<ol>
<li>El evento.</li>
<li>Las gestión de los manejadores que quieran escuchar el evento.</li>
<li>Un disparador para el evento.</li>
</ol>
<h4>Programamos el evento.</h4>
<p>En realidad lo único complejo es el registro del evento. Y no es que sea complejo, es sólo que es muy configurable (y requiere muchos parámetros).</p>
<pre class="brush:csharp">public delegate void DoubleTapEventHandler(object sender, DoubleTapEventArgs e);

public static readonly RoutedEvent DoubleTapEvent = EventManager.RegisterRoutedEvent(
	"DoubleTap",
	RoutingStrategy.Direct,
	typeof(DoubleTapEventHandler),
	typeof(DoubleTapUserControl));</pre>
<p>Lo primero, definimos el manejador del evento. Le damos nombre, y; sobre todo; determinamos que los argumentos vienen como un objeto <em>DoubleTapEventArgs</em>. Este objeto lo crearemos luego, y tiene la particularidad de tener una propiedad para almacenar el tiempo entre los dos <em>ContactDown </em>¿recordáis que al disparador le pasábamos un parámetro <em>time</em>?.</p>
<p>En cuanto al registro del evento, le pasamos cuatro parámetros:</p>
<ol>
<li>El nombre del evento.</li>
<li>La estrategia del evento. Puede ser <em>Direct</em>, <em>Tunnel </em>o <em>Bubble</em>. Sirve para determinar la dirección de propagación. En este caso, <em>Direct </em>no se propaga.</li>
<li>El tipo del manejador. No es casualidad que coincida con lo declarado en la línea 1.</li>
<li>El tipo del objeto que dispara el evento. En este caso, el nombre de nuestro control de usuario.</li>
</ol>
<h4>La gestión de manejadores.</h4>
<p>Esto se parece a los típicos <em>getters </em>/ <em>setters</em> de .Net. Se trata simplemente de gestionar lo que ocurre cuando se añaden o se quitan manejadores de los eventos.</p>
<pre class="brush:csharp">public event DoubleTapEventHandler DoubleTap
{
    add
    {
        AddHandler(DoubleTapEvent, value);
    }
    remove
    {
        RemoveHandler(DoubleTapEvent, value);
    }
}</pre>
<h4>El disparador.</h4>
<p>Este método se encarga de lanzar el evento. Un disparador debería hacer tres cosas:</p>
<ol>
<li>Recibir por parámetro los valores que hay que enviar como argumentos del evento.</li>
<li>Montar un objeto <em>EventArgs</em> que contenga los valores a enviar.</li>
<li>Lanzar el evento.</li>
</ol>
<p>Es muy sencillo:</p>
<pre class="brush:csharp">private void RaiseDoubleTapEvent(TimeSpan TimeBetweenTaps)
{
    DoubleTapEventArgs Args = new DoubleTapEventArgs(TimeBetweenTaps);
    Args.RoutedEvent = DoubleTapUserControl.DoubleTapEvent;

    RaiseEvent(Args);
}</pre>
<p>Y este sería el objeto <em>DoubleTapEventArgs</em>. La idea es que es una extensión del objeto <em>RoutedEventArgs</em>.</p>
<pre class="brush:csharp">public class DoubleTapEventArgs : RoutedEventArgs
{
	TimeSpan timeBetweenTaps = TimeSpan.MinValue;
	public TimeSpan TimeBetweenTaps
	{
	  get { return timeBetweenTaps; }
	}

	public DoubleTapEventArgs(TimeSpan TimeBetweenTaps) : base()
	{
		this.timeBetweenTaps = TimeBetweenTaps;
	}
}</pre>
<p>Con esto ya hemos programado un control de usuario capaz de detectar y disparar un evento <em>DoubleTap</em>. ¿Cómo lo usamos?. Vamos a añadir este objeto a la ventana inicial de la aplicación y lo vemos. En realidad, como si fuese un evento normal y corriente.</p>
<p>Este es el <em>XAML </em>de la ventana:</p>
<pre class="brush:xml">&lt;s:SurfaceWindow x:Class="ProgramarEventosPropios.SurfaceWindow1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"
    xmlns:self="clr-namespace:ProgramarEventosPropios"
    Title="ProgramarEventosPropios"
&gt;
    &lt;s:SurfaceWindow.Resources&gt;
        &lt;ImageBrush x:Key="WindowBackground" Stretch="None" Opacity="0.6" ImageSource="pack://application:,,,/Resources/WindowBackground.jpg"/&gt;
    &lt;/s:SurfaceWindow.Resources&gt;

    &lt;Grid Background="{StaticResource WindowBackground}" &gt;
        &lt;self:DoubleTapUserControl x:Name="DoubleTapUC" DoubleTap="DoubleTapUC_DoubleTap" /&gt;
    &lt;/Grid&gt;</pre>
<p>Y desde el C# programamos el manejador del evento:</p>
<pre class="brush:csharp">void DoubleTapUC_DoubleTap(object sender, DoubleTapEventArgs e)
{
    Debug.WriteLine("Double Tap. Tiempo: {0}", e.TimeBetweenTaps.TotalMilliseconds);
}</pre>
<h3>Descarga del ejemplo.</h3>
<p>Y como no podía ser de otra forma, os dejo aquí la descarga a un pequeño proyecto que es el que programé para redactar este artículo.</p>
<p><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/10/CrearEventosPropios.zip">Crear eventos propios.</a></p>
<h3>¡Hemos acabado!.</h3>
<p>Con esto ya tenemos un evento propio funcionando. Este tipo de eventos nos serán muy útiles en nuestras aplicaciones, y nos solucionarán la vida en muchas ocasiones. No está de más decir que realmente este tipo de eventos se pueden implementar en cualquier aplicación WPF, no sólo en las aplicaciones de Surface.</p>
<p>Espero que hayáis encontrado útil este artículo.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/10/02/crear-eventos-propios-en-aplicaciones-de-surface/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Arquitectura de capas en aplicaciones para Microsoft Surface</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/09/30/arquitectura-de-capas-en-aplicaciones-para-microsoft-surface/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/09/30/arquitectura-de-capas-en-aplicaciones-para-microsoft-surface/#comments</comments>
		<pubDate>Thu, 30 Sep 2010 19:10:46 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[surface]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=521</guid>
		<description><![CDATA[En la primera aplicación que hice para Surface, cometí un gran error de arquitectura que bajo mi punto de vista estropeó un poco lo que yo considero que fue un buen trabajo. Ahora que estoy trabajando en otra aplicación para &#8230; <a href="http://www.juanmasantoyo.es/index.php/2010/09/30/arquitectura-de-capas-en-aplicaciones-para-microsoft-surface/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>En <a href="http://www.juanmasantoyo.es/index.php/2010/07/06/video-de-rent-a-car-surface-by-bizzit/">la primera aplicación que hice para Surface</a>, cometí un gran error de arquitectura que bajo mi punto de vista estropeó un poco lo que yo considero que fue un buen trabajo. Ahora que estoy trabajando en otra aplicación para Surface, me propuse desde el primer día no repetirlo. A la solución que he encontrado la he llamado arquitectura de capas. No estoy inventando nada, pues en la programación gráfica dividir la interfaz  en capas es muy común. Este artículo realmente sólo propone una forma de  hacerlo en una aplicación WPF orientada a Surface.<br />
<span id="more-521"></span></p>
<h3>Mi gran error.</h3>
<p>Me dí cuenta de que era realmente complicado gestionar las cosas que había en el <em>ScatterView</em>. Si veis el vídeo, comprobaréis que básicamente estoy hablando de tres tipos de elementos: los controles de usuario, el coche, y las imágenes del interior del coche. La solución por la que opté en aquel momento se basaba en usar métodos para introducir y eliminar cada elemento en el <em>ScatterView</em>, y registrar la existencia de cada uno en tres listas globales (una lista para cada tipo de elemento). De esa forma ponía y quitaba los elementos de cada tipo.</p>
<p>Era una mala solución, por que no permitía una gran modularización del código y sobrecargaba la clase principal del programa de funcionalidades que realmente no eran de su competencia. Todo se complicaba mucho más si tenemos en cuenta que no pude encapsular el <em>ScatterView </em>en un control de usuario como me hubiese gustado y lo tenía todo en la ventana inicial de la aplicación. En pocas palabras, aunque creo que algunos aspectos de la aplicación quedaron bastante bien, la ventana inicial era sucia y desordenada. El tiempo y la falta de experiencia me impidieron mejorar ese aspecto. Se podría decir que esa aplicación tiene un serio problema de arquitectura.</p>
<h3>La solución: Arquitectura de capas.</h3>
<p>Sin embargo, cuando inicié el desarrollo de la aplicación que tengo ahora entre manos, decidí desde el primer momento plantear las cosas de otro modo. No quería repetir el mismo error, sobre todo por que esta aplicación es aún más compleja en lo que respecta a los elementos libres por la pantalla y un error de esas características hubiese tenido un impacto negativo en la aplicación mucho más devastador.</p>
<p>El principal error que cometí inicialmente fue pensar que un <em>ScatterView </em>no puede posicionarse sobre otro. En realidad, sí es posible, y ambos <em>ScatterViews </em>son totalmente funcionales. Esto nos permite organizar los elementos en varios <em>ScatterViews</em>, uno sobre otro; como si fuesen capas.</p>
<p>Básicamente, para implementar esta arquitectura sólo necesitaremos:</p>
<ol>
<li>Diferentes controles de usuario que serán las capas.</li>
<li>Una clase estática o Singleton que se encargará de gestionar las interacciones entre capas.</li>
</ol>
<p>Cada capa se preocupa únicamente de sus competencias, mientras que para cualquier tipo de interacción con los elementos de las otras capas se usarán funcionalidades implementadas en la clase estática.</p>
<h3>Un ejemplo sencillo.</h3>
<p>No voy a complicarlo, ya que realmente sólo nos interesa ver cómo funcionaría esta arquitectura.</p>
<p>El ejemplo en cuestión será el siguiente: vamos a hacer dos capas en forma de controles de usuario. En cada capa vamos a poner un <em>ScatterView </em>y un <em>ScatterViewItem</em>.</p>
<p>La idea es que cuando un <em>ScatterViewItem </em>se mueva, el otro se posicionará en una posición reflejada. Es decir, reflejará las coordenadas X e Y del <em>ScatterViewItem </em>que hemos movido.</p>
<p>Obviamente, la comunicación entre controles de usuario que necesitamos se implementará con una clase estática.</p>
<h4>Las capas.</h4>
<p>Vamos a programar las dos capas. Serán dos controles prácticamente idénticos, sólo cambiaremos un par de características del <em>ScatterViewItem </em>(para que no se confundan). Los <em>ScatterViewItems </em>tendrán colores diferentes y ubicaciones diferentes (obviamente). Uno estará situado en el punto (100, 100) y el otro en su punto simétrico (924, 668). Para simplificar el asunto, haremos que los <em>ScatterViewItems </em>no se puedan rotar ni escalar.</p>
<p>Los controles de usuario se llamarán LayerA y LayerB, este es su <em>XAML</em>:</p>
<h5>LayerA.</h5>
<pre class="brush:xml">&lt;s:SurfaceUserControl x:Class="ArquitecturaDeCapas.LayerA"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"&gt;
    &lt;Grid&gt;
        &lt;s:ScatterView&gt;
            &lt;s:ScatterViewItem x:Name="Svi"
                Orientation="0" Center="100,100"
                Width="50" Height="50"
                CanScale="False" CanRotate="False"
                ScatterManipulationDelta="ScatterViewItem_ScatterManipulationDelta"
            &gt;
                &lt;Rectangle Fill="Yellow" /&gt;
            &lt;/s:ScatterViewItem&gt;
        &lt;/s:ScatterView&gt;
    &lt;/Grid&gt;
&lt;/s:SurfaceUserControl&gt;</pre>
<h5>LayerB.</h5>
<pre class="brush:xml">&lt;s:SurfaceUserControl x:Class="ArquitecturaDeCapas.LayerB"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"&gt;
    &lt;Grid&gt;
        &lt;s:ScatterView&gt;
            &lt;s:ScatterViewItem x:Name="Svi"
                Orientation="0" Center="924,668"
                Width="50" Height="50"
                CanScale="False" CanRotate="False"
                ScatterManipulationDelta="ScatterViewItem_ScatterManipulationDelta"
            &gt;
                &lt;Rectangle Fill="Red" /&gt;
            &lt;/s:ScatterViewItem&gt;
        &lt;/s:ScatterView&gt;
    &lt;/Grid&gt;
&lt;/s:SurfaceUserControl&gt;</pre>
<p>Como veis en ambos controles, los <em>ScatterViewItems </em>tienen asignado un manejador para el evento &#8220;<em>ScatterManipilationDelta</em>&#8220;. Este evento se lanza cada vez que una manipulación provoca un cambio en el <em>ScatterViewItem</em>, así que lo usaremos para notificar a la clase estática que el <em>ScatterViewItem</em> tiene una nueva posición (y la clase estática lo notificará a su vez a la otra capa).</p>
<p>Vamos pues a ver ese manejador. En ambas capas será igual, ya que será el método de la clase estática quien diferencie entre una capa o otra (esto no tendría por qué ser así).</p>
<h5>El manejador de <em>ScatterManipilationDelta.</em></h5>
<pre class="brush:csharp">private void ScatterViewItem_ScatterManipulationDelta(object sender, ScatterManipulationDeltaEventArgs e)
{
    ScatterViewItem svi = (ScatterViewItem) sender;
    Layers.NotifyNewPosition(this, svi.Center);
}</pre>
<p>Fijaos en el método <em>Layers.NotifyNewPosition</em>. Este método será el método que comunicará ambas capas. La idea es que haga dos cosas:</p>
<ol>
<li>Identifica la capa que llama al método.</li>
<li>Notifica a la otra capa la nueva posición respecto a la que debe reflejar su <em>ScatterViewItem</em>.</li>
</ol>
<p>Para identificar la capa que llama al método, vemos que se envía el parámetro <em>this</em>. Para saber la nueva posición del <em>ScatterViewItem</em>, envía el parámetro <em>svi.Center</em>.</p>
<p>Por otra parte, la clase estática informará a la otra capa de la nueva posición mediante un método público en la capa que deba ser notificada. En este caso, como la posición se refleja; también se harán los cálculos necesarios. El método público de cada capa, sería una cosa así:</p>
<pre class="brush:csharp">
public void ReflexSviPosition(Point Position)
{
    this.Svi.Center = new Point(1024 - Position.X, 768 - Position.Y);
}
</pre>
<p>Ahora que ya sabemos como son las capas, vamos a ver que forma tiene esa clase estática que nos comunicará ambas capas.</p>
<h5>Una clase estática para comunicar las distintas capas.</h5>
<p>A la capa en cuestión la llamaremos Layers. Como he dicho, puede ser estática o Singletone. Aunque seamos francos, realmente el único requisito que debe cumplir esta clase es que tiene que ser accesible por todas las capas que queramos relacionar.</p>
<pre class="brush:csharp">public static class Layers
{
	private static LayerA layerA = null;
	public static LayerA LayerA
	{
		get { return layerA; }
		set { layerA = value; }
	}

	private static LayerB layerB = null;
	public static LayerB LayerB
	{
		get { return Layers.layerB; }
		set { Layers.layerB = value; }
	}

	public static void NotifyNewPosition(SurfaceUserControl Layer, Point Position)
	{
		if(Layer.GetType() == typeof(LayerA))
		{
			layerB.ReflexSviPosition(Position);
		}
		else if(Layer.GetType() == typeof(LayerB))
		{
			layerA.ReflexSviPosition(Position);
		}
	}
}</pre>
<p>Podéis ver el método &#8220;<em>NotifyNewPosition</em>&#8221; y cómo hace la distinción de capas.</p>
<p>Otro aspecto muy importante de esta clase, son las propiedades <em>LayerA </em>y <em>LayerB</em>. No son más que referencias a las capas, para poder acceder posteriormente a ellas. ¿Dónde se inician estas propiedades? Pues allí donde estén las propias capas. En este caso es la ventana inicial de mi programa, que se llama Main.xaml.</p>
<h5>El contenedor de las capas.</h5>
<p>Este sería el xaml:</p>
<pre class="brush:xml">&lt;s:SurfaceWindow x:Class="ArquitecturaDeCapas.Main"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"
    xmlns:self="clr-namespace:ArquitecturaDeCapas"

    Title="ArquitecturaDeCapas"
&gt;
    &lt;s:SurfaceWindow.Resources&gt;
        &lt;ImageBrush x:Key="WindowBackground" Stretch="None" Opacity="0.6" ImageSource="pack://application:,,,/Resources/MainBackground.jpg"/&gt;
    &lt;/s:SurfaceWindow.Resources&gt;

    &lt;Grid Background="{StaticResource WindowBackground}"&gt;
        &lt;self:LayerA x:Name="LayerA" /&gt;
        &lt;self:LayerB x:Name="LayerB" /&gt;
    &lt;/Grid&gt;
&lt;/s:SurfaceWindow&gt;</pre>
<p>Y también se necesita un poco de <em>C#</em> para guardar en la clase <em>Layers</em> las referencias a cada capa. En mi caso, he añadido estas dos líneas de código al final del constructor de <em>Main</em>.</p>
<pre class="brush:csharp">Layers.LayerA = this.LayerA;
Layers.LayerB = this.LayerB;</pre>
<p>De esta clase <em>Main </em>destacar tres cosas:</p>
<ol>
<li>Añadimos el namespace <em>self</em>, para poder incluír controles de usuario de nuestro proyecto.</li>
<li>Añadimos las dos capas.</li>
<li>Guardamos en <em>Layers </em>las referencias a nuestras capas. Yo lo he hecho al final del constructor, pero se podría hacer en el evento <em>Loaded</em>, por ejemplo.</li>
</ol>
<h3>Hemos acabado.</h3>
<p>Pues no hay mucho más que decir, ya tenemos dos <em>ScatterViews </em>sincronizados. Rápido, sencillo, y para toda la familia; espero que os ayude.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/09/30/arquitectura-de-capas-en-aplicaciones-para-microsoft-surface/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Detección y gestión de contactos en una Surface</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/09/22/deteccion-y-gestion-de-contactos-en-una-surface/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/09/22/deteccion-y-gestion-de-contactos-en-una-surface/#comments</comments>
		<pubDate>Wed, 22 Sep 2010 17:55:16 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[surface]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=508</guid>
		<description><![CDATA[A estas alturas de la película, uno ya debería tener claro que la base de las entradas de usuario en una Surface son los contactos. Da igual si tocamos la pantalla con un dedo o con toda la mano, con &#8230; <a href="http://www.juanmasantoyo.es/index.php/2010/09/22/deteccion-y-gestion-de-contactos-en-una-surface/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>A estas alturas de la película, uno ya debería tener claro que la base de las entradas de usuario en una Surface son los contactos.</p>
<p>Da igual si tocamos la pantalla con un dedo o con toda la mano, con un trozo de madera; o si hemos colocado un elemento etiquetado: Todo se reduce a detectar un contacto y gestionarlo.</p>
<p>Por lo tanto, se podría decir que hay varios tipos de contactos. En este artículo vamos a ver qué tipos de contactos hay, cómo podemos detectarlos, y cómo podemos obtener información relevante sobre los mismos. No es un asunto complicado, pero sí es bastante amplio. A modo de resumen, en el artículo vamos a tratar los siguientes puntos:</p>
<ol>
<li>Tipos de contactos.</li>
<li>Reconocimiento de contactos.</li>
<li>Diferenciación de contactos.</li>
<li>Reconocer el mismo contacto en diferentes eventos.</li>
<li>Conocer los diferentes contactos que existen sobre un elemento.</li>
</ol>
<p><span id="more-508"></span></p>
<h3>Tipos de contactos.</h3>
<p>Básicamente, podemos considerar tres tipos diferentes de contactos:</p>
<h4>Dedos.</h4>
<p>La mayoría de contactos son dedos, ya que para el usuario, lo más natural y intuitivo casi siempre será hacer algo con el dedo sobre la pantalla.</p>
<h4>Etiquetas.</h4>
<p>Una de las características más interesantes de una Surface es la detección de etiquetas. Las etiquetas son unos gráficos que al entrar en contacto con la pantalla son reconocidos como tales y tienen un valor numérico determinado.</p>
<p>Existen dos tipos de etiquetas: Las <em>Byte Tags</em> y las <em>Identity Tags</em>. La principal diferencia entre ambas es que las <em>Byte Tags</em> pueden tomar 2<sup>8</sup> valores distintos, y los <em>Identity Tags</em> pueden tomar 2<sup>128</sup> valores diferentes (separados en dos campos de 2<sup>64</sup> y 2<sup>64</sup>).</p>
<div id="attachment_510" class="wp-caption aligncenter" style="width: 81px"><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/09/ByteTagC6.jpg"><img class="size-full wp-image-510" title="Byte Tag" src="http://www.juanmasantoyo.es/wp-content/uploads/2010/09/ByteTagC6.jpg" alt="Byte Tag" width="71" height="71" /></a><p class="wp-caption-text">Un Byte Tag</p></div>
<div id="attachment_511" class="wp-caption aligncenter" style="width: 118px"><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/09/IdentityTag.png"><img class="size-full wp-image-511" title="Identity Tag" src="http://www.juanmasantoyo.es/wp-content/uploads/2010/09/IdentityTag.png" alt="Identity Tag" width="108" height="109" /></a><p class="wp-caption-text">Un Identity Tag</p></div>
<h4>Objetos.</h4>
<p>En realidad, la Surface reacciona ante cualquier contacto sobre la pantalla, sea lo que sea. Puede ser el dorso de la mano, un trozo de madera, etc. Por lo tanto, catalogaremos como un &#8220;objeto&#8221; cualquier contacto que no sea ni un dedo ni una etiqueta.</p>
<h3>Reconocer contactos sobre un elemento.</h3>
<p>Reconocer los contactos sobre un elemento es relativamente sencillo. El propio SDK nos aporta eventos como &#8220;<em>ContactDown</em>&#8220;, &#8220;<em>ContactUp</em>&#8221; o &#8220;<em>ContactTapGesture</em>&#8221; que nos realizaran dicha tarea. De hecho, &#8220;<em>ContactDown</em>&#8221; nos servirá en la mayoría de las ocasiones.</p>
<p>Lo que sí requiere más conocimiento del tema es cómo diferenciar los diferentes tipos de contactos, y cómo obtener información relevante sobre cada uno.</p>
<p>Veamos un ejemplo sencillo de &#8220;<em>ContactDown</em>&#8220;. Por ejemplo, sobre un control de usuario que tengamos en pantalla.</p>
<p>Añadiremos la gestión del evento en el <em>XAML</em> del Control de usuario:</p>
<pre class="brush:xml">&lt;s:SurfaceUserControl x:Class="DeteccionDeContactos.MyUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"

    ContactDown="SurfaceUserControl_ContactDown"
    Width="100" Height="100"
&gt;
    &lt;Grid Background="Red"&gt;

    &lt;/Grid&gt;
&lt;/s:SurfaceUserControl&gt;</pre>
<p>Y esto nos generará en el código <em>C#</em> un método como este:</p>
<pre class="brush:csharp">private void SurfaceUserControl_ContactDown(object sender, ContactEventArgs e)
{
}</pre>
<p>Podéis ver que por parámetro nos llegan dos objetos. Uno es el objeto <em>sender</em>, que no es más que el control de usuario que ha recogido el evento &#8220;<em>ContactDown</em>&#8220;. Por otra parte, nos llega el objeto <em>e</em>. Es un objeto del tipo &#8220;<em>ContactEventArgs</em>&#8220;, y contiene mucha información que nos puede interesar.</p>
<h3>Obtener información útil sobre los contactos.</h3>
<p>Antes de nada, vamos a ver cómo podemos saber qué tipo de contacto es. En realidad es sencillo, ya que las propiedades <em>e.Contact.IsContactRecognized</em> y <em>e.Contact.IsTagRecognized </em>nos lo revelarán:</p>
<pre class="brush:csharp">private void SurfaceUserControl_ContactDown(object sender, ContactEventArgs e)
{
    if (e.Contact.IsFingerRecognized)
    {
        Debug.WriteLine("Es un dedo.");
    }
    else if (e.Contact.IsTagRecognized)
    {
        Debug.WriteLine("Es un elemento etiquetado.");
    }
    else
    {
        Debug.WriteLine("Es un objeto.");
    }
}</pre>
<p>Ahora bien: ¿Más cosas que nos puedan interesar? Pues dependerá del tipo de contacto, obviamente. En general, algo que tienen en común todos los contactos es una posición en pantalla y una orientación (recordemos que una de las más geniales características de una Surface es poder detectar la orientación de un contacto). Nos lo dirán los métodos <em>e.Contact.GetPosition</em> y <em>e.Contact.GetOrientation</em>. Ambos métodos reciben por parámetro un elemento respecto al que orientarse. Lo más común es orientarse respecto al objeto que capturó el método, así que el parámetro <em>sender</em> que mencioné antes nos viene de perlas. El objeto <em>sender</em> en principio viene tipado como un objeto genérico, así que habrá que hacerle un casting. No debería ser un problema si tenemos claro quien lanzó el evento.</p>
<pre class="brush:csharp">private void SurfaceUserControl_ContactDown(object sender, ContactEventArgs e)
{
    MyUserControl uc = (MyUserControl) sender;

    Point position = e.GetPosition(uc);
    Double orientation = e.Contact.GetOrientation(uc);

    Debug.WriteLine("El contacto se ha producido en la posición: {0}, {1}.", position.X, position.Y);
    Debug.WriteLine("La orientación del contacto es de {0} grados.", orientation);

    if (e.Contact.IsFingerRecognized)
    {
        Debug.WriteLine("Es un dedo.");
    }
    else if (e.Contact.IsTagRecognized)
    {
        Debug.WriteLine("Es un elemento etiquetado.");
    }
    else
    {
        Debug.WriteLine("Es un objeto.");
    }
}</pre>
<p>Bueno, ya sabemos dónde se produjo el contacto, y sabemos también su orientación. Si el contacto fuese un dedo, no necesitamos saber más. Pero ¿Y si es un objeto etiquetado? Hay dos parámetros importantísimos que nos interesará conocer: el tipo de etiqueta y su valor. Conviene anticipar que si el contacto es de tipo <em>Identity</em>, el valor se separa en dos campos: la serie y el valor. La serie son los primeros 2<sup>64</sup> bits, el valor son los últimos 2<sup>64</sup> bits. Debemos considerar el objeto <em>e.Contacts.Tag</em> y sus propiedades.</p>
<pre class="brush:csharp">private void SurfaceUserControl_ContactDown(object sender, ContactEventArgs e)
{
    MyUserControl uc = (MyUserControl) sender;

    Point position = e.GetPosition(uc);
    Double orientation = e.Contact.GetOrientation(uc);

    Debug.WriteLine("El contacto se ha producido en la posición: {0}, {1}.", position.X, position.Y);
    Debug.WriteLine("La orientación del contacto es de {0} grados.", orientation);

    if (e.Contact.IsFingerRecognized)
    {
        Debug.WriteLine("Es un dedo.");
    }
    else if (e.Contact.IsTagRecognized)
    {
        Debug.WriteLine("Es un elemento etiquetado.");

        switch (e.Contact.Tag.Type)
        {
            case TagType.Byte:

                Byte byteTagValue = e.Contact.Tag.Byte.Value;

                Debug.WriteLine("Es un Byte Tag. Su valor es {0}.", byteTagValue);
                break;

            case TagType.Identity:

                long serie = e.Contact.Tag.Identity.Series;
                long identityTagValue = e.Contact.Tag.Identity.Value;

                Debug.WriteLine("Es un Identity Tag. Su serie es: {0}. Su valor es {1}.", serie, identityTagValue);
                break;
        }
    }
    else
    {
        Debug.WriteLine("Es un objeto.");
    }
}</pre>
<p>Por último, si el contacto es un objeto cualquiera, nos interesará saber su forma y el área que ocupa. El propio SDK nos ayuda creando un objeto <em>Ellipse</em> que tiene la forma aproximada del contacto. Nosotros podemos saber dónde está el centro del elipse, cuál es su tamaño y incluso el área exacta del objeto que toca la pantalla. Debemos observar los métodos <em>e.Contact.GetEllipse</em>,  <em>e.Contact.GetCenterPosition</em>, y la propiedad <em>e.Contact.PhysicalArea</em>. Los métodos para obtener los valores de la elipse también reciben por parámetro el objeto respecto al que deben calcularse. Igual que antes, lo normal sería usar el parámetro <em>sender</em>.</p>
<pre class="brush:csharp">private void SurfaceUserControl_ContactDown(object sender, ContactEventArgs e)
{
    MyUserControl uc = (MyUserControl) sender;

    Point position = e.GetPosition(uc);
    Double orientation = e.Contact.GetOrientation(uc);

    Debug.WriteLine("El contacto se ha producido en la posición: {0}, {1}.", position.X, position.Y);
    Debug.WriteLine("La orientación del contacto es de {0} grados.", orientation);

    if (e.Contact.IsFingerRecognized)
    {
        Debug.WriteLine("Es un dedo.");
    }
    else if (e.Contact.IsTagRecognized)
    {
        Debug.WriteLine("Es un elemento etiquetado.");

        switch (e.Contact.Tag.Type)
        {
            case TagType.Byte:

                Byte byteTagValue = e.Contact.Tag.Byte.Value;

                Debug.WriteLine("Es un Byte Tag. Su valor es {0}.", byteTagValue);
                break;

            case TagType.Identity:

                long serie = e.Contact.Tag.Identity.Series;
                long identityTagValue = e.Contact.Tag.Identity.Value;

                Debug.WriteLine("Es un Identity Tag. Su serie es: {0}. Su valor es {1}.", serie, identityTagValue);
                break;
        }
    }
    else
    {
        Double area = e.Contact.PhysicalArea;
        Ellipse ellipse = e.Contact.GetEllipse(uc);
        Point ellipseCenter = e.Contact.GetCenterPosition(uc);

        Debug.WriteLine("Es un objeto.");
        Debug.WriteLine("La elipse aproximada que contiene el contacto es ancho: {0}, alto: {1}", ellipse.Width, ellipse.Height);
        Debug.WriteLine("El punto central de la elipse es: {0}, {1}", ellipseCenter.X, ellipseCenter.Y);
    }
}</pre>
<h3>Reconocer el mismo contacto en diferentes eventos.</h3>
<p>Los contactos en realidad disparan más de un evento. Por ejemplo, un mismo contacto primero genera un &#8220;<em>ContactDown</em>&#8221; y por último generará un &#8220;<em>ContactUp</em>&#8220;. Entonces, ¿Cómo saber que son el mismo?. Para ello usaremos la propiedad<em> e.Contact.Id</em>. El mismo contacto, en diferentes eventos; conservará el mismo Id.</p>
<h3>Conocer los diferentes contactos sobre un elemento.</h3>
<p>Y para acabar, algo que también nos puede ser de utilidad es conocer los diferentes contactos que hay sobre un elemento. Para ello, podemos usar la propiedad <em>ContactsOver</em> que nos proporcionan los controles de usuario. Esto es una lista de objetos <em>Contact</em>, por lo que a cada elemento se le podrían aplicar todas las propiedades y métodos vistos en este artículo.</p>
<h3>Hemos acabado.</h3>
<p>Bueno, acabamos de pegar un buen repaso a los contactos de Surface. Dejo la descarga a un ejemplo que desarrollé para escribir este artículo.</p>
<p><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/09/DeteccionDeContactos.zip">Deteccion de contactos (VS 2010)</a></p>
<p>El contenido de este artículo debería ser suficiente para que cualquier hombre de bien desarrolle aplicaciones bastante decentes para Microsoft Surface, pero el que tenga dudas o necesite más información sobre algún aspecto que no dude en comentarlo por aquí. Yo mismo sigo aprendiendo sobre el tema, así que cualquier ayuda que os pueda prestar también me ayuda a mí.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/09/22/deteccion-y-gestion-de-contactos-en-una-surface/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Navegador basado en Chromium para integrar en aplicaciones WPF</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/09/03/navegador-basado-en-chromium-para-integrar-en-aplicaciones-wpf/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/09/03/navegador-basado-en-chromium-para-integrar-en-aplicaciones-wpf/#comments</comments>
		<pubDate>Fri, 03 Sep 2010 18:32:53 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[surface]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=504</guid>
		<description><![CDATA[Desde hace semanas, intento encontrar la manera de identificar un usuario en Facebook desde una aplicación de Surface (WPF)&#8230; sin mucho éxito por el momento (de conseguir algo, será seguro una gran guarrería, que por supuesto publicaré en el blog). &#8230; <a href="http://www.juanmasantoyo.es/index.php/2010/09/03/navegador-basado-en-chromium-para-integrar-en-aplicaciones-wpf/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Desde hace semanas, intento encontrar la manera de identificar un usuario en Facebook desde una aplicación de Surface (WPF)&#8230; sin mucho éxito por el momento (de conseguir algo, será seguro una gran guarrería, que por supuesto publicaré en el blog).</p>
<p>No obstante, hice algunas preguntas por el blog de desarrolladores de Surface en MSDN USA, y es ahí donde me recomendaron este navegador para integrar en aplicaciones WPF:</p>
<p><a href="http://chriscavanagh.wordpress.com/2009/08/25/a-real-wpf-webbrowser/">http://chriscavanagh.wordpress.com/2009/08/25/a-real-wpf-webbrowser/</a></p>
<p>El proyecto es muy interesante: han aprovechado que el código de Chromium es libre para hacer una adaptación del mismo en WPF. Para que quede más claro, estoy hablando de un control de usuario 100% funcional, que puede ser integrado en aplicaciones WPF y que es una alternativa al control WebBrowser (el problema de este es que no es más que el Internet Explorer integrado en la aplicación).</p>
<p>En realidad este navegador no es exáctamente lo que necesito para mi aplicación y no lo he probado, pero como después de semanas de investigación esto es lo más interesante que me he encontrado, quería comentarlo.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/09/03/navegador-basado-en-chromium-para-integrar-en-aplicaciones-wpf/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introducción al componente ScatterView de Surface (2: ScatterViews con C#)</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/08/03/introduccion-al-componente-scatterview-de-surface-parte-2/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/08/03/introduccion-al-componente-scatterview-de-surface-parte-2/#comments</comments>
		<pubDate>Tue, 03 Aug 2010 05:27:06 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[surface]]></category>
		<category><![CDATA[scatterview]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=352</guid>
		<description><![CDATA[Ya hemos visto como podemos usar los ScatterView a base de XAML, pero muchas veces vamos a tener la necesidad de tratarlos desde C#. En este artículo, veremos cómo podemos trabajar con ScatterViews de forma dinámica. Preparamos el proyecto Antes &#8230; <a href="http://www.juanmasantoyo.es/index.php/2010/08/03/introduccion-al-componente-scatterview-de-surface-parte-2/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Ya hemos visto como podemos <a href="http://www.juanmasantoyo.es/index.php/2010/04/08/introduccion-al-componente-scatterview-de-surface-parte-1/">usar los <em>ScatterView</em> a base de <em>XAML</em></a>, pero muchas veces vamos a tener la necesidad de tratarlos desde <em>C#</em>.</p>
<p>En este artículo, veremos cómo podemos trabajar con ScatterViews de forma dinámica.<br />
<span id="more-352"></span></p>
<h3>Preparamos el proyecto</h3>
<p>Antes de iniciar el Visual Studio, debemos haber iniciado el emulador de Surface que viene con el <em>SDK</em>. Si no lo has hecho así, cierra el Visual Studio y inícialo todo en el orden correcto.</p>
<p>Usaremos el proyecto <em>HelloScatterView</em> que creamos en el primer artículo, y crearemos una nueva ventana <em>XAML</em> llamada &#8220;<em>CSharpSample</em>&#8220;. Tendremos que modificar el archivo <em>App.xml</em> (atrubito <em>StartupUri</em>) para que inicie la aplicación en la nueva ventana.</p>
<h3>ScatterViews simples con C#</h3>
<p>Vamos a considerar un <em>XAML</em> muy básico, sin <em>ScatterViewItems</em>. Sí que vamos a añadir tres botones que nos proporcionarán un mínimo de funcionalidad.</p>
<pre class="brush:xml">
	&lt;s:SurfaceWindow
		x:Class=&quot;HelloScatterView.SurfaceWindow1&quot;
		xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
		xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
		xmlns:s=&quot;http://schemas.microsoft.com/surface/2008&quot;
		Title=&quot;HelloScatterView&quot;&gt;

		&lt;s:SurfaceWindow.Resources&gt;
			&lt;ImageBrush x:Key=&quot;WindowBackground&quot;
					  Stretch=&quot;None&quot; Opacity=&quot;0.6&quot;
					  ImageSource=&quot;pack://application:,,,/Resources/WindowBackground.jpg&quot;/&gt;
		&lt;/s:SurfaceWindow.Resources&gt;

		&lt;DockPanel&gt;
			&lt;UniformGrid Background=&quot;Black&quot; Height=&quot;50&quot; DockPanel.Dock=&quot;Top&quot; Columns=&quot;3&quot;&gt;
				&lt;s:SurfaceButton Content=&quot;Add&quot; Click=&quot;Add&quot; Foreground=&quot;White&quot;/&gt;
				&lt;s:SurfaceButton Content=&quot;Remove&quot; Click=&quot;Remove&quot; Foreground=&quot;White&quot;/&gt;
				&lt;s:SurfaceButton Content=&quot;Clear&quot; Click=&quot;Clear&quot; Foreground=&quot;White&quot;/&gt;
			&lt;/UniformGrid&gt;
			&lt;s:ScatterView x:Name=&quot;Scatter&quot;/&gt;
		&lt;/DockPanel&gt;
	&lt;/s:SurfaceWindow&gt;
</pre>
<p>Por ahora esto es todo el <em>XAML</em> que vamos a usar. La parte importante del código, la hacemos desde <em>C#</em>, en el archivo <em>CSharpSample.xaml.cs</em>.</p>
<p>Antes de continuar vamos a dejar claro que hará cada botón:</p>
<ul>
<li>El botón Add añadirá un <em>ScatterViewItem</em> con una de las imágenes (se seguirá un orden cíclico).</li>
<li>El botón Remove quitará el último <em>ScatterViewItem</em> añadido.</li>
<li>El botón Clear quitará todos los <em>ScatterViewItems</em>.</li>
</ul>
<h4>Inicializamos</h4>
<p>Creamos en el <em>.cs</em> una propiedad global que será una lista con los nombres de las imágenes, y otra propiedad que será un <em>String</em> con la ruta del directorio que las contiene, de forma que al concatenar el directorio con nombre de imagen tengamos la ruta completa. Daremos los correspondientes valores a ambas propiedades.</p>
<pre class="brush:csharp">
namespace HelloScatterView
{
    public partial class CSharpSample : SurfaceWindow
    {
        string folder = string.Empty;
        List&lt;string&gt; images = new List&lt;string&gt;();

        public CSharpSample()
        {
            this.folder = &quot;Resources/Img/&quot;;

            images.Add(&quot;fuente_tres_chorros.jpg&quot;);
            images.Add(&quot;kiyoumizu_dera.jpg&quot;);
            images.Add(&quot;laputa_robot.jpg&quot;);

            InitializeComponent();

            // Add handlers for Application activation events
            AddActivationHandlers();
        }
	}
}
</pre>
<h4>Eventos de los botones</h4>
<p>El evento click del botón añadir creará un ScatterViewItem, le asignará la imagen y lo añadirá al contenedor ScatterView. Para saber que imagen añadir, se basará en el total de imágenes añadidas.</p>
<pre class="brush:csharp">
private void Add(object sender, RoutedEventArgs e)
{
	// Calculamos el indice de la imagen a mostrar en función de la cantidad de elementos en el ScatterView
	int index = this.Scatter.Items.Count % this.images.Count;

	// Creamos el ScatterViewItem que vamos a añadir
	BitmapImage bmp = new BitmapImage(new Uri(this.folder + this.images[index], UriKind.Relative));
	Image img = new Image();
	img.Source = bmp;

	ScatterViewItem svi = new ScatterViewItem();
	svi.Content = img;

	Scatter.Items.Add(svi);
}
</pre>
<p>El evento click del botón eliminar, accederá a la última imagen añadida en el contenedor ScatterView y la eliminará:</p>
<pre class="brush:csharp">
private void Remove(object sender, RoutedEventArgs e)
{
	// Averiguamos el índice del último elemento añadido
	int index = this.Scatter.Items.Count - 1;

	if (index &gt;= 0)
	{
		this.Scatter.Items.Remove(this.Scatter.Items[index]);
	}
}
</pre>
<p>El evento click del botón borrar limpiará complétamente el contenedor ScatterView:</p>
<pre class="brush:csharp">
private void Clear(object sender, RoutedEventArgs e)
{
	this.Scatter.Items.Clear();
}
</pre>
<h3>Añadir ScatterViews con Data Binding</h3>
<p>Pero vamos a aprovechar las características de <em>.Net</em> para añadir los ScatterViewItems de una forma más potente. En realidad, podemos definir un origen de datos de forma que <em>.Net</em> añadirá todas las imágenes que contenga al contenedor ScatterView.</p>
<p>Dicho de otra forma, en pocas líneas de código podemos conseguir añadir al ScatterView todas las imágenes que haya, por ejemplo; en un directorio determinado. Vamos a hacer un ajuste en el constructor de CSharpSample y en el nodo s:ScatterView del XAML:</p>
<h4>En el C#</h4>
<pre class="brush:csharp">
public CSharpSample()
{
	this.folder = &quot;Resources/Img/&quot;;

	images.Add(&quot;fuente_tres_chorros.jpg&quot;);
	images.Add(&quot;kiyoumizu_dera.jpg&quot;);
	images.Add(&quot;laputa_robot.jpg&quot;);

	InitializeComponent();

	// Add handlers for Application activation events
	AddActivationHandlers();

	String[] files = System.IO.Directory.GetFiles(&quot;Resources/Img&quot;, &quot;*.jpg&quot;);
	this.Scatter.ItemsSource = files;
}
</pre>
<p>Lo importante son las últimas lineas: obtienen la lista de imágenes en un directorio y la establecen como el origen de datos del ScatterView.</p>
<h4>En el XAML#</h4>
<pre class="brush:xml">
&lt;s:ScatterView x:Name=&quot;Scatter&quot;&gt;
	&lt;s:ScatterView.ItemTemplate&gt;
		&lt;DataTemplate&gt;
			&lt;s:ScatterViewItem&gt;
				&lt;Image Source=&quot;{Binding}&quot; /&gt;
			&lt;/s:ScatterViewItem&gt;
		&lt;/DataTemplate&gt;
	&lt;/s:ScatterView.ItemTemplate&gt;
&lt;/s:ScatterView&gt;
</pre>
<p>Lo importante es el nodo ItemTemplate: hemos establecido una plantilla que es la que usarán los elementos en el origen de datos.</p>
<h3>Es todo por hoy</h3>
<p>Ahora ya sabemos crear ScatterVirews desde <em>XAML</em> y desde C#, pero esto es sólo el principio. Como comenté en el primer artículo, un ScatterViewItem es mucho más que una imagen; y eso es lo que veremos en el próximo artículo sobre ScatterViews.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/08/03/introduccion-al-componente-scatterview-de-surface-parte-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Animar transformaciones en WPF con el método BeginAnimation</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/07/06/animar-transformaciones-en-wpf-con-el-metodo-beginanimation/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/07/06/animar-transformaciones-en-wpf-con-el-metodo-beginanimation/#comments</comments>
		<pubDate>Tue, 06 Jul 2010 22:17:31 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[surface]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=475</guid>
		<description><![CDATA[Me preguntaron hace poco cómo animar elementos en Microsoft Surface. En esencia se haría como en cualquier entorno basado en WPF. Hay varias formas de animar, pero quizás una de las más sencilla y potente sea la animación de transformaciones &#8230; <a href="http://www.juanmasantoyo.es/index.php/2010/07/06/animar-transformaciones-en-wpf-con-el-metodo-beginanimation/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Me preguntaron hace poco cómo animar elementos en Microsoft Surface. En esencia se haría como en cualquier entorno basado en WPF.</p>
<p>Hay varias formas de animar, pero quizás una de las más sencilla y potente sea la animación de transformaciones con el método <em>BeginAnimation</em>.<br />
<span id="more-475"></span></p>
<h3>Las transformaciones.</h3>
<p>En WPF existen varios tipos de transformaciones:</p>
<ul>
<li><em><a href="http://msdn.microsoft.com/es-es/library/system.windows.media.rotatetransform.aspx">RotateTransform</a></em>. Cambia la orientación de un elemento.</li>
<li><em><a href="http://msdn.microsoft.com/es-es/library/system.windows.media.scaletransform.aspx">ScaleTransform</a></em>. Cambia el tamaño de un elemento.</li>
<li><em><a href="http://msdn.microsoft.com/es-es/library/system.windows.media.translatetransform.aspx">TranslateTransform</a></em>. Cambia la posición de un elemento.</li>
<li><em><a href="http://msdn.microsoft.com/es-es/library/system.windows.media.transformgroup.aspx">TransformGroup</a></em>. Es un tipo de transformación especial, ya que consiste en una combinación de las tres anteriores.</li>
</ul>
<h3>Animar transformaciones.</h3>
<p>La idea es simple: Una animación consiste en modificar progresivamente el valor de una propiedad durante un periodo de tiempo. En principio, se puede animar cualquier propiedad de dependencia del API de WPF.</p>
<p>Concretamente, las transformaciones usan propiedades de dependencia para modificar el aspecto del elemento en el que se aplican; así que si aplicamos una animación sobre una propiedad de una transformación de cualquier tipo, el resultado será una animación sobre el elemento.</p>
<p>El código para aplicar una transformación es sencillo. Se puede aplicar desde XAML o desde C#, pero en lo personal prefiero hacerlo en XAML:</p>
<pre class="brush:xml">	&lt;Image x:Name="MyImage" Source="images/myimage.png" Width="100" Height="100"&gt;
		&lt;Image.RenderTransform&gt;
			&lt;TransformGroup&gt;
				&lt;TranslateTransform x:Name="Translacion" X="85" /&gt;
				&lt;RotateTransform x:Name="Rotacion" Angle="0" CenterX="15" CenterY="15" /&gt;
			&lt;/TransformGroup&gt;
		&lt;/Image.RenderTransform&gt;
	&lt;/Image&gt;</pre>
<p>Fijaos que al usar el nodo <em>&#8220;TransformGroup&#8221;</em> podemos aplicar más de una transformación. De no usarlo sólo podríamos aplicar una, ya que el nodo <em>&#8220;Image.RenderTransform&#8221;</em> sólo admite un nodo hijo. Notad también que las transformaciones se aplican desde la última a la primera. No es lo mismo mover y rotar, que rotar y mover.</p>
<h3>Las animaciones y el método BeginAnimation.</h3>
<p>A nivel práctico, una animación es un objeto con una serie de propiedades configuradas, como por ejemplo la duración de la animación, la aceleración, el valor inicial y final de la propiedad, etc. Dependiendo del tipo de dato que queramos animar, el objeto de animación cambia. No es lo mismo animar un color (<em>ColorAnimation</em>) que una orientación (<em>DoubleAnimation</em>). Se pueden ver todos los tipos de animación posible en la especificación de la clase madre: <em><a href="http://msdn.microsoft.com/es-es/library/system.windows.media.animation.animationtimeline.aspx">AnimationTimeLine</a></em>.</p>
<p>Para aplicar una animación sobre una transformación, contamos con un potente aliado: el método <em>BeginAnimation</em>, presente en todas las transformaciones. Al método <em>BeginAnimation</em> se le pasan dos parámetros: la propiedad de dependencia a animar, y la animación. La animación deberemos construirla en base a lo que queremos animar, la propiedad de dependencia es una propiedad estática de la clase a la que pertenece el elemento que queremos animar. Por ejemplo, si queremos animar el ángulo de un <em>RotateTransform</em>, tendremos una propiedad estática &#8220;<em>RotateTransform.AngleProperty</em>&#8220;. Más fácil con un poco de código C#:</p>
<pre class="brush:csharp">	DoubleAnimation animacion = new DoubleAnimation();

	animacion.To = 180;
	animacion.Duration = new Duration(TimeSpan.FromMilliseconds(1000));

	Rotacion.BeginAnimation(RotateTransform.AngleProperty, animacion);</pre>
<h3>Es todo por el momento.</h3>
<p>Y en realidad no hay mucho más que decir sobre animaciones. Sólo destacar que, como imagináis, el objeto animación tiene eventos que informarán del estado del proceso, y que no son bloqueantes (es decir, mientras una animación se ejecuta, el flujo del programa no se detiene, por lo que necesitan una gestión asíncrona). También destacar que la animación se iniciará en el instante en que se ejecute el método <em>BeginAnimation</em>.</p>
<p>Si queréis aprender sobre animaciones más complejas, os recomiendo que miréis el objeto <em><a href="http://msdn.microsoft.com/en-us/library/system.windows.media.animation.storyboard.aspx">StoryBoard</a></em>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/07/06/animar-transformaciones-en-wpf-con-el-metodo-beginanimation/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Charla sobre Microsoft Surface</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/05/28/charla-sobre-microsoft-surface/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/05/28/charla-sobre-microsoft-surface/#comments</comments>
		<pubDate>Fri, 28 May 2010 15:15:12 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[surface]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=455</guid>
		<description><![CDATA[Ayer dí una charla sobre Microsoft Surface en el centro de Innovación de Microsoft de Palma para el grupo de desarrolladores de .Net en Baleares. Hablamos un poco de todo lo que implica la Surface y el desarrollo de aplicaciones &#8230; <a href="http://www.juanmasantoyo.es/index.php/2010/05/28/charla-sobre-microsoft-surface/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Ayer dí una charla sobre Microsoft Surface en el centro de Innovación de Microsoft de Palma para el grupo de desarrolladores de .Net en Baleares.</p>
<p>Hablamos un poco de todo lo que implica la Surface y el desarrollo de aplicaciones para este dispositivo. Fue una experiencia muy gratificante.</p>
<p>Dejo la descarga del Power Point que redacté para la ocasión, por si os interesa.</p>
<p><a href='http://www.juanmasantoyo.es/wp-content/uploads/2010/05/Microsoft-Surface.zip'>Microsoft Surface</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/05/28/charla-sobre-microsoft-surface/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>ScatterViewItem invisibles en Microsoft Surface</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/05/18/scatterviewitem-invisibles-en-microsoft-surface/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/05/18/scatterviewitem-invisibles-en-microsoft-surface/#comments</comments>
		<pubDate>Tue, 18 May 2010 06:00:57 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[surface]]></category>
		<category><![CDATA[scatterviewitem invisible]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=449</guid>
		<description><![CDATA[Cuando empecé a desarrollar para Microsoft Surface, me di cuenta de tres cosas: El control ScatterViewItem es la base de toda aplicación. La mayoría de las veces, no queremos que el ScatterViewItem tenga la apariencia tan horrible que tiene por &#8230; <a href="http://www.juanmasantoyo.es/index.php/2010/05/18/scatterviewitem-invisibles-en-microsoft-surface/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Cuando empecé a desarrollar para Microsoft Surface, me di cuenta de tres cosas:</p>
<ol>
<li>El control ScatterViewItem es la base de toda aplicación.</li>
<li>La mayoría de las veces, no queremos que el ScatterViewItem tenga la apariencia tan horrible que tiene por defecto, es más, nos interesará que sea &#8220;invisible&#8221;.</li>
<li>El control ScatterViewItem no proporciona ningun sistema simple para volverse invisible.</li>
</ol>
<p>Así que despues de trastear un poco con Microsoft Expression Blend, conseguí montar un pequeño estilo que podemos aplicar a nuestros ScatterViewItem para que estos sean invisibles. Esto es, inapreciables para el usuario. Por lo tanto, el contenido se verá perfectamente, pero las sombras, reflejos, destellos y demás tonterías, no.<br />
<span id="more-449"></span><br />
Este es el estilo en cuestión:</p>
<pre class="brush:xml">
&lt;Style x:Key=&quot;InvisibleScatterViewItem&quot; TargetType=&quot;{x:Type s:ScatterViewItem}&quot;&gt;
    &lt;Setter Property=&quot;Template&quot;&gt;
        &lt;Setter.Value&gt;
            &lt;ControlTemplate TargetType=&quot;{x:Type s:ScatterViewItem}&quot;&gt;
                &lt;ContentPresenter /&gt;
            &lt;/ControlTemplate&gt;
        &lt;/Setter.Value&gt;
    &lt;/Setter&gt;
&lt;/Style&gt;
</pre>
<p>Espero que os sirva tanto como a mí.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/05/18/scatterviewitem-invisibles-en-microsoft-surface/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Introducción al componente ScatterView de Surface (1: ScatterViews con XAML)</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/04/08/introduccion-al-componente-scatterview-de-surface-parte-1/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/04/08/introduccion-al-componente-scatterview-de-surface-parte-1/#comments</comments>
		<pubDate>Thu, 08 Apr 2010 12:30:16 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[surface]]></category>
		<category><![CDATA[scatterview]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=332</guid>
		<description><![CDATA[El multitouch es probablemente una de las características más atractivas de Surface. Esto se debe a que plantea una serie de posibilidades al usuario que convierte su experiencia en algo mucho más intuitivo y sencillo. El componente ScatterView es quizás &#8230; <a href="http://www.juanmasantoyo.es/index.php/2010/04/08/introduccion-al-componente-scatterview-de-surface-parte-1/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>El multitouch es probablemente una de las características más atractivas de Surface. Esto se debe a que plantea una serie de posibilidades al usuario que convierte su experiencia en algo mucho más intuitivo y sencillo.</p>
<p>El componente <em>ScatterView</em> es quizás uno de los mejores ejemplos de esto. Este es el primero de varios artículos en el que veremos cómo se usan y qué posiblides nos ofrecen.<br />
<span id="more-332"></span></p>
<h3>Sobre el componente <em>ScatterView</em></h3>
<p>Un <em>ScatterView</em> representa un elemento que reacciona a los contactos sobre Surface y que se puede mover, rotar o redimensionar.</p>
<p>El ejemplo más clásico sería un puñado de fotografías. Cada una de esas fotografías puede ser un <em>ScatterView</em>.</p>
<h3>Preparamos el proyecto</h3>
<p>Antes de iniciar el Visual Studio, debemos haber iniciado el emulador de Surface que viene con el SDK. Si no lo has hecho así, cierra el Visual Studio y inícialo todo en el orden correcto.</p>
<h4>Creamos el proyecto</h4>
<p>Nuestro proyecto va a ser una aplicación para Surface basada en WPF. Lo llamaremos HelloScatterView.</p>
<p>Al crear nuestro proyecto se nos ha creado una ventana por defecto: SurfaceWindow1. La renombraremos, y le pondremos el nombre de &#8220;<strong>XamlSample</strong>&#8220;.</p>
<h4>Añadimos unas imágenes</h4>
<p>Para poder trabajar con los <strong>ScatterViews</strong>, necesitaremos algunas imágenes.</p>
<p>Dentro de nuestro proyecto Surface tenemos una carpeta llamada Resources. Crearemos otra carpeta en su interior llamada Img y ubicaremos ahí nuestras imágenes.</p>
<h3>Añadir <em>ScatterViews</em> desde XAML</h3>
<h4><em>ScatterView</em> simple con XAML</h4>
<p>Comenzaremos usando los <em>ScatterViews</em> desde XAML. En realidad añadir un <em>ScatterView</em> es muy simple:</p>
<pre class="brush:xml">
&lt;s:ScatterView&gt;
    &lt;Image Source=&quot;Resources/Img/fuente_tres_chorros.jpg&quot;/&gt;
&lt;/s:ScatterView&gt;
</pre>
<p><img src="http://www.juanmasantoyo.es/wp-content/uploads/2010/02/01-300x225.jpg" alt="" title="Ejemplo de ScatterView" width="300" height="225" class="aligncenter size-medium wp-image-333" /></p>
<p>Este es el código que añade un <em>ScatterView</em>. Fijaos en un detalle: añadir un <em>ScatterView</em> no es sólo añadir el nodo <em>ScatterView</em>, también es añadir el recurso que se manejará, en este caso la imagen. Y es que un <em>ScatterView</em> no es una imagen, sino la capacidad que tiene un elemento (en este caso una imagen) de ser rotado, redimensionado y movido.</p>
<p>El XAML completo de la ventana es este:</p>
<pre class="brush:xml">
&lt;s:SurfaceWindow
    x:Class=&quot;HelloScatterView.SurfaceWindow1&quot;
    xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
    xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
    xmlns:s=&quot;http://schemas.microsoft.com/surface/2008&quot;
    Title=&quot;HelloScatterView&quot;&gt;
    &lt;s:SurfaceWindow.Resources&gt;
        &lt;ImageBrush x:Key=&quot;WindowBackground&quot;
                  Stretch=&quot;None&quot; Opacity=&quot;0.6&quot;
                  ImageSource=&quot;pack://application:,,,/Resources/WindowBackground.jpg&quot;/&gt;
    &lt;/s:SurfaceWindow.Resources&gt;
    &lt;s:ScatterView&gt;
        &lt;Image Source=&quot;Resources/Img/fuente_tres_chorros.jpg&quot;/&gt;
    &lt;/s:ScatterView&gt;
&lt;/s:SurfaceWindow&gt;
</pre>
<p>Si ahora compilamos nuestro proyecto, podremos jugar un poco con nuestra foto sobre el emulador de Surface. Incluso, si conectas dos ratones por USB puedes simular multitouch.</p>
<p>Vamos a hacerlo un poco más espectacular añadiendo varias imágenes:</p>
<pre class="brush:xml">
&lt;s:ScatterView&gt;
    &lt;Image Source=&quot;Resources/Img/fuente_tres_chorros.jpg&quot;/&gt;
    &lt;Image Source=&quot;Resources/Img/kiyoumizu_dera.jpg&quot;/&gt;
    &lt;Image Source=&quot;Resources/Img/laputa_robot.jpg&quot;/&gt;
    &lt;Image Source=&quot;Resources/Img/fuente_tres_chorros.jpg&quot;/&gt;
    &lt;Image Source=&quot;Resources/Img/kiyoumizu_dera.jpg&quot;/&gt;
    &lt;Image Source=&quot;Resources/Img/laputa_robot.jpg&quot;/&gt;
&lt;/s:ScatterView&gt;
</pre>
<p><img src="http://www.juanmasantoyo.es/wp-content/uploads/2010/02/02-300x225.jpg" alt="" title="Ejemplo de varios ScatterViews" width="300" height="225" class="aligncenter size-medium wp-image-334" /></p>
<h4><em>Scatterviews</em> complejos con XAML</h4>
<p>Probablemente ya te estés preguntando lo siguiente: ¿cómo determinar los parámetros por defecto de nuestro <em>ScatterView</em>? Vamos a ello.</p>
<p>La clave está en que la estructura que hemos usado ahora no acaba de ser la mejor. En realidad, nos está faltando crear un <em>ScatterViewItem</em>, que hasta el momento se estaba creando de forma automática para cada elemento en el <em>ScatterView</em>.</p>
<pre class="brush:xml">
&lt;s:ScatterView&gt;
	&lt;s:ScatterViewItem&gt;
		&lt;Image Source=&quot;Resources/Img/fuente_tres_chorros.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;
&lt;/s:ScatterView&gt;
</pre>
<p>En el nodo <em>ScatterViewItem</em> podemos establecer muchas propiedades que afectarán al comportamiento de nuestro <em>ScatterView</em>.</p>
<p>Algunas de esas propiedades son:</p>
<ul>
<li><strong>MinWidth</strong>, <strong>MaxWidth</strong>, <strong>MinHeight</strong>, <strong>MaxHeight</strong>: Máximos y mínimos de anchura y altura.</li>
<li><strong>CanMove</strong>, <strong>CanRotate</strong>, <strong>CanScale</strong>: Activan o desactivan las capacidades de mover, rotar y redimensionar.</li>
<li><strong>Width</strong>, <strong>Height</strong>, <strong>Center</strong>, <strong>Orientation</strong>: Valores iniciales para el ancho, el alto, la posición, y la rotación.</li>
<li><strong>BorderBrush</strong>, <strong>BorderThikness</strong>: Color y grosor del borde.</li>
</ul>
<p>Un ejemplo sencillo:</p>
<pre class="brush:xml">
&lt;s:ScatterView&gt;
	&lt;s:ScatterViewItem Center=&quot;512,384&quot; Orientation=&quot;0&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/fuente_tres_chorros.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;

	&lt;s:ScatterViewItem Center=&quot;512,384&quot; Orientation=&quot;60&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/kiyoumizu_dera.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;

	&lt;s:ScatterViewItem Center=&quot;512,384&quot; Orientation=&quot;180&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/laputa_robot.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;

	&lt;s:ScatterViewItem Center=&quot;512,384&quot; Orientation=&quot;240&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/fuente_tres_chorros.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;

	&lt;s:ScatterViewItem Center=&quot;512,384&quot; Orientation=&quot;300&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/kiyoumizu_dera.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;

	&lt;s:ScatterViewItem Center=&quot;512,384&quot; Orientation=&quot;360&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/laputa_robot.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;
&lt;/s:ScatterView&gt;
</pre>
<p>Aunque de hecho, podemos centralizar la mayoría de estas propiedades en un único nodo, como un estilo:</p>
<pre class="brush:xml">
&lt;s:ScatterView&gt;
	&lt;s:ScatterView.ItemContainerStyle&gt;
		&lt;Style TargetType=&quot;{x:Type s:ScatterViewItem}&quot;&gt;
			&lt;Setter Property=&quot;Center&quot; Value=&quot;512,384&quot;/&gt;
			&lt;Setter Property=&quot;BorderBrush&quot; Value=&quot;Black&quot;/&gt;
			&lt;Setter Property=&quot;BorderThickness&quot; Value=&quot;3&quot;/&gt;
		&lt;/Style&gt;
	&lt;/s:ScatterView.ItemContainerStyle&gt;

	&lt;s:ScatterViewItem Orientation=&quot;0&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/fuente_tres_chorros.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;

	&lt;s:ScatterViewItem Orientation=&quot;60&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/kiyoumizu_dera.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;

	&lt;s:ScatterViewItem Orientation=&quot;180&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/laputa_robot.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;

	&lt;s:ScatterViewItem Orientation=&quot;240&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/fuente_tres_chorros.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;

	&lt;s:ScatterViewItem Orientation=&quot;300&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/kiyoumizu_dera.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;

	&lt;s:ScatterViewItem Orientation=&quot;360&quot;&gt;
		&lt;Image Source=&quot;Resources/Img/laputa_robot.jpg&quot;/&gt;
	&lt;/s:ScatterViewItem&gt;
&lt;/s:ScatterView&gt;
</pre>
<p><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/02/03.jpg"><img src="http://www.juanmasantoyo.es/wp-content/uploads/2010/02/03-300x225.jpg" alt="" title="Definiendo las propiedades por defecto de los ScatterViews" width="300" height="225" class="aligncenter size-medium wp-image-335" /></a></p>
<h3>Es todo por hoy</h3>
<p>El componente <em>ScatterView</em> es sencillo de usar y proporciona una característica muy interesante a nuestras aplicaciones.</p>
<p>Cuando se trabaja para Surface, hay que tener muy en cuenta que los diversos usuarios se colocan alrededor de la mesa, por lo que no existe el derecho ni el revés. Los ScatterViews nos solucionarán este problema más de una vez.</p>
<p>En la siguiente parte del artículo, veremos cómo trabajar con ScatterViews desde C#.</p>
<h3>Editado 7/5/2010:</h3>
<p>Me comentaron que los códigos fuente en XAML tenían mal la sintaxis, y efectívamente; cuando he pasado a revisarlo así era. Ni idea de que pudo pasar&#8230; Bueno, ya están arreglados.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/04/08/introduccion-al-componente-scatterview-de-surface-parte-1/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Enrutamiento Seo Friendly con asp.Net</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/04/06/enrutamiento-seo-friendly-con-asp-net/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/04/06/enrutamiento-seo-friendly-con-asp-net/#comments</comments>
		<pubDate>Tue, 06 Apr 2010 21:55:48 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[enrutamiento seo]]></category>
		<category><![CDATA[friendly url]]></category>
		<category><![CDATA[seo friendly]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=423</guid>
		<description><![CDATA[En los últimos años se ha ido estableciendo en los sitios web una buena práctica: las Seo Friendly URL. Friendly de amistoso, por que hace que nuestras rutas sean fáciles de indexar por los buscadores (los parámetros GET nunca les &#8230; <a href="http://www.juanmasantoyo.es/index.php/2010/04/06/enrutamiento-seo-friendly-con-asp-net/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>En los últimos años se ha ido estableciendo en los sitios web una buena práctica: las <em>Seo Friendly URL</em>. Friendly de amistoso, por que hace que nuestras rutas sean fáciles de indexar por los buscadores (los parámetros <em>GET</em> nunca les han gustado demasiado) y, mucho más importante; fáciles de comprender por los usuarios.</p>
<p>Es de sentido común que la URL de una página tenga que tener cierta relación con lo que contiene. A nadie le dice nada una URL de tipo http://www.dominio.com?seccion=5, pero la cosa es más interesante si es algo del tipo http://www.dominio.com/contacto. La propia URL de este artículo es un buen ejemplo.</p>
<p>En este artículo veremos un pequeño ejemplo de como podemos crear Friendly URLs en <em>asp.Net</em><br />
<span id="more-423"></span></p>
<h3>¿En que consisten técnicamente las <em>Friendly URL</em>?</h3>
<p>La idea es interceptar las peticiones por URL que se realizan a nuestro sitio web antes de que se sirva ningún contenido, de forma que podamos analizar la ruta y servir un archivo u otro en consecuencia. También podemos considerar que parte de la URL es un parámetro de entrada.</p>
<p>Por ejemplo, la URL de este artículo podría ser una como <em>http://www.dominio.com/articulos/enrutamiento-friendly-url/</em>, donde la parte &#8220;enrutamiento-friendly-url&#8221; es la que determina qué artículo se está mostrando.</p>
<h3>¿Cómo montamos el nuevo sistema de direccionamiento?</h3>
<p>Cada tecnología de servidor tiene su forma de hacerlo. En este artículo veremos un ejemplo sobre <em>asp.Net</em>.</p>
<p>La clave es el archivo <em>Global.asax</em>. Este es un archivo que proporciona manejadores para una serie de eventos producidos por la aplicación. A nosotros nos interesará el evento <em>Application_BeginRequest</em>: es ahí donde podemos interceptar las peticiones antes de que se sirva ningún contenido, y donde analizaremos las URL para reprogramar el direccionamiento a donde nos interese. Obviamente, el archivo <em>Global.asax</em> tiene más utilidades que el enrutamiento, pero de momento no nos interesan.</p>
<h3>Un ejemplo</h3>
<p>Vamos a programar un pequeño ejemplo que nos serviría para gestionar los direccionamientos de una red social.</p>
<p>Haremos una dirección para las secciones principales, y una dirección que será dinámica para cada usuario miembro de la red social.</p>
<h4>Diseñamos el nuevo enrutamiento</h4>
<p>Como siempre, antes de empezar a hacer nada; necesitamos tener claro qué queremos hacer. Vamos a considerar todas las rutas que tendremos.</p>
<ol>
<li>
		<strong><em>/inicio/</em></strong><br />
		Todos los sitios web, las redes sociales también; necesitan una página de inicio.
	</li>
<li>
		<strong><em>/buscar/</em></strong><br />
		Toda red social necesita un buscador.
	</li>
<li>
		<strong><em>/usuarios/</em></strong><br />
		Lo más importante de una red social son los usuarios. Esta es la página genérica de usuarios, que podría ser por ejemplo un formulario de Login.
	</li>
<li>
		<strong><em>/usuarios/&lt;nombre de usuario&gt;/</em></strong><br />
		Todos los usuarios tienen un perfil con su nombre, su foto, las cosas que hacen los domingos&#8230; Fijaos en que ya incluímos un parámetro, el nombre del usuario.
	</li>
<li>
		<strong><em>/usuarios/&lt;nombre de usuario&gt;/contacto/</em></strong><br />
		La gracia de una red social es poder contactar con los usuarios. Esta página proporcionaría funcionalidad a una red de mensajería interna. Tambien tenemos el parámetro de nombre de usuario.
	</li>
</ol>
<h4>Creamos el proyecto</h4>
<p>Vamos a crear un proyecto web normal y corrente desde el <em>Visual Studio</em>.</p>
<p>Crearemos cuatro <em>.aspx</em>, uno para cada una de nuestras rutas, excepto para <em>/inicio/</em>, que será el <em>Default.aspx</em> que se ha creado por defecto. Tambien crearemos una clase <em>.cs</em> para simular el origen de datos de los usuarios.</p>
<ul>
<li><em>Buscar.aspx</em></li>
<li><em>Usuarios.aspx</em></li>
<li><em>UsuariosPerfil.aspx</em></li>
<li><em>UsuariosContacto.aspx</em></li>
<li><em>Datos/Usuarios.cs</em></li>
</ul>
<h4>Algo de código para los <em>.aspx</em></h4>
<p>Vamos a añadir algo de código en los <em>.aspx</em>. Para diferenciarlos, más que nada; y para probar unos links.</p>
<h5><em>Default.aspx</em></h5>
<pre class="brush:xml">
	&lt;head runat=&quot;server&quot;&gt;
		&lt;title&gt;Inicio&lt;/title&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;form id=&quot;form1&quot; runat=&quot;server&quot;&gt;
		&lt;div&gt;
			&lt;h1&gt;Inicio&lt;/h1&gt;

			&lt;ul&gt;
				&lt;li&gt;&lt;asp:LinkButton runat=&quot;server&quot; ID=&quot;lbBuscar&quot; PostBackUrl=&quot;~/buscar/&quot;&gt;Buscar&lt;/asp:LinkButton&gt;&lt;/li&gt;
				&lt;li&gt;&lt;asp:LinkButton runat=&quot;server&quot; ID=&quot;lbUsuarios&quot; PostBackUrl=&quot;~/usuarios/&quot;&gt;Usuarios&lt;/asp:LinkButton&gt;&lt;/li&gt;
			&lt;/ul&gt;
		&lt;/div&gt;
		&lt;/form&gt;
	&lt;/body&gt;
</pre>
<h5><em>Buscar.aspx</em></h5>
<pre class="brush:xml">
	&lt;head runat=&quot;server&quot;&gt;
		&lt;title&gt;Buscar&lt;/title&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;form id=&quot;form1&quot; runat=&quot;server&quot;&gt;
		&lt;div&gt;
			&lt;h1&gt;Buscar&lt;/h1&gt;

			&lt;ul&gt;
				&lt;li&gt;&lt;asp:LinkButton ID=&quot;lbInicio&quot; runat=&quot;server&quot; PostBackUrl=&quot;~/inicio/&quot;&gt;Inicio&lt;/asp:LinkButton&gt;&lt;/li&gt;
				&lt;li&gt;&lt;asp:LinkButton ID=&quot;lbUsuarios&quot; runat=&quot;server&quot; PostBackUrl=&quot;~/usuarios/&quot;&gt;Usuarios&lt;/asp:LinkButton&gt;&lt;/li&gt;
			&lt;/ul&gt;
		&lt;/div&gt;
		&lt;/form&gt;
	&lt;/body&gt;
</pre>
<h5><em>Usuarios.aspx</em></h5>
<pre class="brush:xml">
	&lt;head runat=&quot;server&quot;&gt;
		&lt;title&gt;Usuarios&lt;/title&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;form id=&quot;form1&quot; runat=&quot;server&quot;&gt;
		&lt;div&gt;
			&lt;h1&gt;Usuarios&lt;/h1&gt;

			&lt;ul&gt;
				&lt;li&gt;&lt;asp:LinkButton ID=&quot;lbInicio&quot; runat=&quot;server&quot; PostBackUrl=&quot;~/inicio/&quot;&gt;Inicio&lt;/asp:LinkButton&gt;&lt;/li&gt;
				&lt;li&gt;&lt;asp:LinkButton ID=&quot;lbBuscar&quot; runat=&quot;server&quot; PostBackUrl=&quot;~/buscar/&quot;&gt;Buscar&lt;/asp:LinkButton&gt;&lt;/li&gt;
			&lt;/ul&gt;
		&lt;/div&gt;
		&lt;/form&gt;
	&lt;/body&gt;
</pre>
<h5><em>UsuariosPerfil.aspx</em></h5>
<pre class="brush:xml">
	&lt;head runat=&quot;server&quot;&gt;
		&lt;title&gt;Perfil de &lt;/title&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;form id=&quot;form1&quot; runat=&quot;server&quot;&gt;
		&lt;div&gt;
			&lt;h1 runat=&quot;server&quot; id=&quot;hTitle&quot;&gt;&lt;/h1&gt;

			&lt;ul&gt;
				&lt;li&gt;&lt;asp:LinkButton ID=&quot;lbInicio&quot; runat=&quot;server&quot; PostBackUrl=&quot;~/inicio/&quot;&gt;Inicio&lt;/asp:LinkButton&gt;&lt;/li&gt;
				&lt;li&gt;&lt;asp:LinkButton ID=&quot;lbUsuariosContacto&quot; runat=&quot;server&quot;&gt;Perfíl de &lt;/asp:LinkButton&gt;&lt;/li&gt;
			&lt;/ul&gt;
		&lt;/div&gt;
		&lt;/form&gt;
	&lt;/body&gt;
</pre>
<h5><em>UsuariosContacto.aspx</em></h5>
<pre class="brush:xml">
	&lt;head runat=&quot;server&quot;&gt;
		&lt;title&gt;Contactar con &lt;/title&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;form id=&quot;form1&quot; runat=&quot;server&quot;&gt;
		&lt;div&gt;
			&lt;h1 runat=&quot;server&quot; id=&quot;hTitle&quot;&gt;Contactar con &lt;/h1&gt;

			&lt;ul&gt;
				&lt;li&gt;&lt;asp:LinkButton ID=&quot;lbinicio&quot; runat=&quot;server&quot; PostBackUrl=&quot;~/inicio/&quot;&gt;Inicio&lt;/asp:LinkButton&gt;&lt;/li&gt;
				&lt;li&gt;&lt;asp:LinkButton ID=&quot;lbUsuariosPerfil&quot; runat=&quot;server&quot;&gt;Perfíl de &lt;%= getUserName() %&gt;&lt;/asp:LinkButton&gt;&lt;/li&gt;
			&lt;/ul&gt;
		&lt;/div&gt;
		&lt;/form&gt;
	&lt;/body&gt;
</pre>
<p>Debemos considerar, además, un poco de código en los <em>.cs</em>:</p>
<h5><em>Datos/Usuarios.cs</em></h5>
<p>Este será nuestro origen de datos. Normalmente será una tabla en base de datos, un xml, o algo por el estilo; pero para nuestro ejemplo nos basta una clase que extienda de List. Le hemos programado además dos nuevos métodos: uno para convertir un valor de la lista al formato que usamos en las URL, y otro para encontrar valores. Destacar que el algoritmo de búsqueda podría ser bastante mejor, pero para el caso ya nos sirve.</p>
<pre class="brush:csharp">
	using System;
	using System.Collections.Generic;

	namespace JuanmaSantoyoSeoFriendly.Datos
	{
		public class Usuarios : List&lt;string&gt;
		{
			public Usuarios()
			{
				this.Add("John Travolta");
				this.Add("Samuel L Jackson");
				this.Add("Bruce Willis");
				this.Add("Uma Thurman");
				this.Add("Quentin Tarantino");
			}
		}

		private string urlFormat(string str)
        {
            return str.ToLowerInvariant().Replace(&quot; &quot;, &quot;-&quot;);
        }

        public int search(string username)
        {
            int userid = -1;
            int i = 0;

            while(this.Count &gt; i)
            {
                if (urlFormat(this[i]) == username)
                {
                    userid = i;
                    break;
                }

                i  ;
            }

            return userid;
        }
	}
</pre>
<h5><em>UsuariosPerfil.aspx.cs</em></h5>
<p>En este <em>.cs</em> daremos valores a algunos elementos del <em>.aspx</em>, y además proporcionaremos métodos que rescatarán de sesión el ID del usuario y devolverán su nombre. Hay dos versiones para este método, uno devolverá el nombre tal cual, y el otro nos dará la posiblidad de devolver el nombre formateado para URL.</p>
<pre class="brush:csharp">
	protected void Page_Load(object sender, EventArgs e)
	{
		this.hTitle.InnerText = this.getUserName();

		Page.Title  = this.getUserName();

		lbUsuariosContacto.PostBackUrl = &quot;~/usuarios/&quot;   this.getUserName(true)   &quot;/contacto/&quot;;
		lbUsuariosContacto.Text  = this.getUserName();
	}

	public string getUserName()
	{
		Datos.Usuarios usu = new Datos.Usuarios();

		if (HttpContext.Current.Items.Contains(&quot;userid&quot;) &amp;&amp; HttpContext.Current.Items[&quot;userid&quot;].ToString() != &quot;-1&quot;)
		{
			return usu[Convert.ToInt32(HttpContext.Current.Items[&quot;userid&quot;])];
		}
		else
		{
			return &quot;user not found&quot;;
		}
	}

	public string getUserName(bool encode)
	{
		Datos.Usuarios usu = new Datos.Usuarios();

		if (HttpContext.Current.Items.Contains(&quot;userid&quot;))
		{
			return usu[Convert.ToInt32(HttpContext.Current.Items[&quot;userid&quot;])].ToLowerInvariant().Replace(&quot; &quot;, &quot;-&quot;);
		}
		else
		{
			return &quot;user not found&quot;;
		}
	}
</pre>
<h5><em>UsuariosContacto.cs</em></h5>
<p>Este <em>.cs</em> es bastante similar al anterior, así que no merece demasiados comentarios:</p>
<pre class="brush:csharp">
	protected void Page_Load(object sender, EventArgs e)
	{
		hTitle.InnerHtml  = getUserName();

		Page.Title  = this.getUserName();

		lbUsuariosPerfil.PostBackUrl = &quot;~/usuarios/&quot;   getUserName(true)   &quot;/&quot;;
		lbUsuariosPerfil.Text  = getUserName();
	}

	public string getUserName()
	{
		Datos.Usuarios usu = new Datos.Usuarios();

		if (HttpContext.Current.Items.Contains(&quot;userid&quot;))
		{
			return usu[Convert.ToInt32(HttpContext.Current.Items[&quot;userid&quot;])];
		}
		else
		{
			return &quot;user not found&quot;;
		}
	}

	public string getUserName(bool encode)
	{
		Datos.Usuarios usu = new Datos.Usuarios();

		if (HttpContext.Current.Items.Contains(&quot;userid&quot;) &amp;&amp; HttpContext.Current.Items[&quot;userid&quot;].ToString() != &quot;-1&quot;)
		{
			return usu[Convert.ToInt32(HttpContext.Current.Items[&quot;userid&quot;])].ToLowerInvariant().Replace(&quot; &quot;, &quot;-&quot;);
		}
		else
		{
			return &quot;user not found&quot;;
		}
	}
</pre>
<h4>Añadimos el <em>Global.asax</em></h4>
<p>Añadimos un nuevo elemento en el proyecto. El nuevo elemento será un Artchivo de aplicación global. No le cambiaremos el nombre: lo dejaremos como <em>&#8220;Global.asax&#8221;</em>.</p>
<p><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/04/01-global-asax.png"><img src="http://www.juanmasantoyo.es/wp-content/uploads/2010/04/01-global-asax.png" alt="Global.asax" title="01-global-asax" width="540" height="329" class="aligncenter size-full wp-image-427" /></a></p>
<h4>Configuramos las nuevas rutas</h4>
<p>Antes que nada, debemos agregar la librería <em>&#8220;System.Web.Routing&#8221;</em>.</p>
<p><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/04/02-system-web-routing.png"><img src="http://www.juanmasantoyo.es/wp-content/uploads/2010/04/02-system-web-routing.png" alt="System.Web.Routing" title="02-system-web-routing" width="503" height="390" class="aligncenter size-full wp-image-428" /></a></p>
<p>Programaremos nuestra propia función para aplicar el enrutamiento. La llamaremos <em>&#8220;ApplyRouting&#8221;</em>:</p>
<pre class="brush:csharp">
	private void ApplyRouting(RouteCollection Routes)
	{
		Routes.Add(new Route(&quot;&quot;, new RouteHandler(&quot;~/Default.aspx&quot;)));
		Routes.Add(new Route(&quot;inicio/&quot;, new RouteHandler(&quot;~/Default.aspx&quot;)));
		Routes.Add(new Route(&quot;buscar/&quot;, new RouteHandler(&quot;~/Buscar.aspx&quot;)));
		Routes.Add(new Route(&quot;usuarios/&quot;, new RouteHandler(&quot;~/Usuarios.aspx&quot;)));
		Routes.Add(new Route(&quot;usuarios/{username}/&quot;, new RouteHandler(&quot;~/UsuariosPerfil.aspx&quot;)));
		Routes.Add(new Route(&quot;usuarios/{username}/contacto/&quot;, new RouteHandler(&quot;~/UsuariosContacto.aspx&quot;)));
	}
</pre>
<p>Y la llamaremos desde el evento <em>Application_BeginRequest</em>:</p>
<pre class="brush:csharp">
	protected void Application_BeginRequest(object sender, EventArgs e)
	{
		ApplyRouting(RouteTable.Routes);
	}
</pre>
<p>Lo que hacemos es, básicamente; añadir nuevas rutas creadas por nosotros a la tabla de rutas de nuestra aplicación. Hay varias cosas destacables:</p>
<ol>
<li>El primer parámetro es la nueva ruta. Si escribimos el valor entre llaves, será un parámetro. Los parámetros que antes enviábamos por <em>GET</em> deberían ser sustituídos por estos.</li>
<li>Estamos creando instancias de RouteHandler. Esta clase no existe en la Api de C#, la tenemos que programar nosotros y será donde añadiremos toda la lógica de redireccionamiento.</li>
<li>Como la clase RouteHandler la programamos nosotros, tenemos ciertas ventajas. En este caso, pasamos como parámetro del constructor el archivo al cual redirigimos. No tendría por qué ser así, pero como una de las características de nuestras URL es que siempre redirigen a un único archivo; este sistema nos viene bastante bien.</li>
</ol>
<h4>Programamos la lógica de redireccionamiento</h4>
<p>Vamos a crear ahora la clase <em>Routing/RouteHandler.cs</em>. Esta clase, deberá heredar de <em>IRouteHandler</em> y por tanto deberá incorporar un método público llamado <em>GetHttpHandler</em>, que recibirá un único parámetro que es un <em>RequestContext</em> y que retornará un <em>IHttpHandler</em>.</p>
<p>En este método pasaran escencialmente dos cosas: por una parte, se meterán los parámetros recibidos en sesión. Por otra, se retornará un <em>IHttpHandler</em> que construiremos usando la ruta del <em>.aspx</em> al que queremos redirigir.</p>
<pre class="brush:csharp">
	public IHttpHandler GetHttpHandler(RequestContext requestContext)
	{
		/*
		 * Tratamos los parámetros de la URL:
		 * Si es el nombre de usuario, buscamos su ID y lo metemos en sesión.
		 * Si no, símplemente lo metemos en sesión
		 */
		foreach (KeyValuePair&lt;string, object&gt; val in requestContext.RouteData.Values)
		{
			if (val.Key == &quot;username&quot;)
			{
				Datos.Usuarios usu = new Datos.Usuarios();
				int userid = usu.search(val.Value.ToString());
				requestContext.HttpContext.Items[&quot;userid&quot;] = userid;
			}

			requestContext.HttpContext.Items[val.Key] = val.Value;
		}

		return (Page) BuildManager.CreateInstanceFromVirtualPath(VirtualPath, typeof(Page));
	}
</pre>
<p>Añadimos además un constructor y una variable global para el parámetro que indica el archivo al que redirigimos.</p>
<pre class="brush:csharp">
	string VirtualPath = string.Empty;

	public RouteHandler(string path)
	{
		this.VirtualPath = path;
	}
</pre>
<p>Hecho esto, nuestras nuevas rutas ya deberían estar funcionando. Sólo nos queda un detalle.</p>
<h4>Ignorando rutas</h4>
<p>Generalmente, no sólo nos va a interesar añadir rutas a nuestra aplicación, sino tambien quitarlas. Por ejemplo, no es una mala idea que se ignoren las rutas directas a un <em>.aspx</em>, o aquellas rutas que no acaben con el carácter <em>&#8220;/&#8221;</em> (para tener un sistema de rutas uniforme). Vamos a programar una función en <em>Global.asax</em>, a la cual llamaremos justo antes de <em>ApplyRouting</em> y que se encargará de comprobar si nuestra ruta debe ser ignorada y, en ese caso, redirigir a <em>/inicio/</em>.</p>
<p>El método <em>Application_BeginRequest</em> en <em>Global.asax</em> queraría de la siguiente forma:</p>
<pre class="brush:csharp">
	protected void Application_BeginRequest(object sender, EventArgs e)
	{
		Redirect();
		ApplyRouting(RouteTable.Routes);
	}
</pre>
<p>El método <em>Redirect</em>:</p>
<pre class="brush:csharp">
	private void Redirect()
	{
		string url = Request.Url.ToString();

		if (url.Contains(&quot;.aspx&quot;))
		{
			Response.Redirect(&quot;~/inicio/&quot;);
		}

		if (url[url.Length - 1] != '/' &amp;&amp; !url.Contains(&quot;?&quot;))
		{
			Response.Redirect(url + &quot;/&quot;);
		}
	}
</pre>
<h4>Descarga el código fuente</h4>
<p>Sé que me odias por no haber puesto este archivo al principio, pero más vale tarde que nunca ¿no?.</p>
<p><a href='http://www.juanmasantoyo.es/wp-content/uploads/2010/04/JuanmaSantoyoSeoFriendly.zip'>Código fuente</a>.</p>
<h3>¡Hemos acabado!</h3>
<p>Como véis, en asp.Net el enrutamiento Seo Friendly se reduce a dos clases: <em>Global.asax</em> y el manejador de rutas. Es bastante simple, y no requiere demasiado tiempo de implementación. Los beneficios de usar este tipo de enrutamiento son múltiples, desde aspectos de seguridad, hasta aspectos de usuabilidad y posicionamiento en buscadores. ¡Ya no hay excusa para no implementarlo!.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/04/06/enrutamiento-seo-friendly-con-asp-net/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introducción al desarrollo sobre Microsoft Surface</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/04/06/introduccion-al-desarrollo-para-microsoft-surface/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/04/06/introduccion-al-desarrollo-para-microsoft-surface/#comments</comments>
		<pubDate>Tue, 06 Apr 2010 06:15:28 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[surface]]></category>
		<category><![CDATA[microsoft]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=158</guid>
		<description><![CDATA[Microsoft Surface (Surface a partir de ahora) es sin duda un desafío para los programadores. Acostumbrados a entornos gráficos basados en respuestas al ratón, una interfaz de usuario multitáctil nos plantea un nuevo reto a la mayoría de nosotros.

En este artículo, pretendo dos cosas: por una parte, informarte de qué necesitas para comenzar a desarrollar y cómo obtenerlo. Por otra parte, facilitarte esa documentación que a todos nos va tan bien para empezar con una nueva tecnología. Así que vamos a ello. <a href="http://www.juanmasantoyo.es/index.php/2010/04/06/introduccion-al-desarrollo-para-microsoft-surface/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Microsoft Surface (Surface a partir de ahora) es sin duda un desafío para los programadores. Acostumbrados a entornos gráficos basados en respuestas al ratón, una interfaz de usuario multitáctil nos plantea un nuevo reto a la mayoría de nosotros.</p>
<p>En este artículo, pretendo dos cosas: por una parte, informarte de qué necesitas para comenzar a desarrollar y cómo obtenerlo. Por otra parte, facilitarte esa documentación que a todos nos va tan bien para empezar con una nueva tecnología. Así que vamos a ello.<br />
<span id="more-158"></span><br />
Lo primero que debes saber, es que una aplicación para Surface no es más que una aplicación WPF o XNA. Por si no lo sabías, WPF es un entorno muy potente para desarrollar aplicaciones de escritorio orientadas a los nuevos entornos gráficos de Windows Vista y Windows 7. XNA es un entorno para desarrollo de videojuegos también facilitado por Microsoft. Si has jugado con la Xbox 360 te habrás preguntado ¿de dónde salen los tantísimos juegos del bazar? Pues bien, la mayoría están desarrollados por compañías independientes con XNA.</p>
<p>Obviamente, optar por WPF o XNA dependerá de nuestra aplicación. Si necesitamos un motor rico en controles de usuario, optaríamos por WPF. Si necesitamos un motor potente en la renderización de gráficos 3D, optaríamos por XNA. Depende de las necesidades de nuestra aplicación.</p>
<p>En este artículo, pretendo dos cosas: por una parte, informarte de qué necesitas para comenzar a desarrollar y cómo obtenerlo. Por otra parte, facilitarte esa documentación que a todos nos va tan bien para empezar con una nueva tecnología. Así que vamos a ello.</p>
<p>Lo primero que debes saber, es que una aplicación para Surface no es más que una aplicación WPF o XNA. Por si no lo sabías, WPF es un entorno muy potente para desarrollar aplicaciones de escritorio orientadas a los nuevos entornos gráficos de Windows Vista y Windows 7. XNA es un entorno para desarrollo de videojuegos también facilitado por Microsoft. Si has jugado con la Xbox 360 te habrás preguntado ¿de dónde salen los tantísimos juegos del bazar? Pues bien, la mayoría están desarrollados por compañías independientes con XNA.Obviamente, optar por WPF o XNA dependerá de nuestra aplicación. Si necesitamos un motor rico en controles de usuario, optaríamos por WPF. Si necesitamos un motor potente en la renderización de gráficos 3D, optaríamos por XNA. Depende de las necesidades de nuestra aplicación.</p>
<h3>Instalación del entorno de desarrollo</h3>
<p>Para empezar, un detalle importante es nuestro sistema operativo: El SDK de Surface no funcionará en Windows XP, necesitaremos Windows Vista o superior.</p>
<p>Bien, empecemos a instalar los programas necesarios. Antes de empezar, no estaría mal asegurarse de que tenemos instalada la última versión del .Net Framework (3.5 SP1 en estos momentos).</p>
<p>La primera herramienta que debemos conseguir es un Visual Studio. Como sabrás, Visual Studio es un IDE desarrollado por Microsoft y orientado al desarrollo de aplicaciones en .Net. Si no lo tienes y eres estudiante universitario, puedes hacerte con una licencia de estudiante. Esta licencia es totalmente gratuita y sólo necesitaras proporcionar tu identificación como estudiante. Puedes obtenerla aquí: <a href="https://www.dreamspark.com/Products/Product.aspx?productid=24">Microsoft Visual Studio</a>.</p>
<p>Cuando tengamos el Visual Studio instalado, deberemos instalar el Framework de XNA (independientemente de que queramos desarrollar con WPF). Lo podemos obtener de aquí: <a href="http://www.microsoft.com/downloads/details.aspx?familyid=15fb9169-4a25-4dca-bf40-9c497568f102&#038;displaylang=en.">Microsoft XNA Framework</a>.</p>
<p>Finalmente, instalamos el SDK de Surface. Lo encontramos aquí: <a href="http://www.microsoft.com/downloads/details.aspx?displaylang=en&#038;FamilyID=3db8987b-47c8-46ca-aafb-9c3b36f43bcc">Microsoft Surface SDK</a>.</p>
<p>Con este software instalado, ya estamos listos para abrir el Visual Studio y crear un nuevo proyecto de Surface, no sin antes ejecutar el emulador de Surface que viene con el SDK. De esta forma, al depurar proyectos desde Visual Studio, se ejecutarán en el emulador.</p>
<p>Debes saber, además; que si conectas más de un ratón por USB el emulador los considerará dedos diferentes. ¡Eso es, soporte multitáctil en tu ordenador!.</p>
<h3>Recursos de documentación para empezar</h3>
<p>Ya tenemos todo lo necesario para empezar a desarrollar aplicaciones de Surface. Ahora, sólo nos falta algo de documentación que nos oriente para empezar a hacer cosas.</p>
<h4>Developing for Microsoft Surface</h4>
<p>Pero ¿dónde nos estamos metiendo realmente? En esta conferencia de Microsoft se ilustra cómo es el desarrollo de aplicaciones en Surface.<br />
<a href="http://channel9.msdn.com/pdc2008/PC17/">Developing for Microsoft Surface</a>.</p>
<h4>Hola Mundo Surface con WPF</h4>
<p>Siguiendo en la línea anterior, este artículo me ha aclarado algunos conceptos generales más a la hora de afrontar el desarrollo con Surface. Interesante sobre todo porque habla algo de los nuevos eventos de contacto de Surface.<br />
<a href="http://www.luisguerrero.net/post/2009/03/16/Hola-Mundo-Surface-con-WPF.aspx">Hola Mundo Surface con WPF</a>.</p>
<h4>MSDN .Net Framework Developer Center</h4>
<p>Pero vamos, ya está bien de conceptos generales, ¿verdad? Seguro que ya tienes ganas de meterle mano a Surface.</p>
<p>En MSDN nos facilitan información y ejemplos varios para empezar a programar algunas cosas. ¡Suerte!.<br />
<a href="http://msdn.microsoft.com/en-us/library/ee804767.aspx">MSDN .Net Framework Developer Center</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/04/06/introduccion-al-desarrollo-para-microsoft-surface/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>XML dinámicos con asp.Net</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/03/16/xml-dinamicos-con-asp-net/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/03/16/xml-dinamicos-con-asp-net/#comments</comments>
		<pubDate>Tue, 16 Mar 2010 19:04:33 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[asmx]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[webservice .net]]></category>
		<category><![CDATA[xml dinamicos]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=397</guid>
		<description><![CDATA[Desde que me inicié en el asp.Net (no hace demasiado de eso) me he estado preguntando cómo se podría hacer una llamada al servidor que devolviese un contenido XML dinámico. Dicho de otra forma, lo que yo quería era generar un contenido XML en base a un parámetro enviado por GET o POST. Estos XML son necesarios para cualquier aplicación basada en Ajax que se precie, además de ser especialmente útiles para otras situaciones (como por ejemplo, compartir contenido interno a un destinatario externo).

Pues bien, hace un par de días me surgió la necesidad y investigué un poco al respecto. En realidad, la solución que nos propone asp.Net no está nada mal: un archivo asmx que actúa como WebService. <a href="http://www.juanmasantoyo.es/index.php/2010/03/16/xml-dinamicos-con-asp-net/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Desde que me inicié en el asp.Net (no hace demasiado de eso) me he estado preguntando cómo se podría hacer una llamada al servidor que devolviese un contenido <em>XML</em> dinámico. Dicho de otra forma, lo que yo quería era generar un contenido XML en base a un parámetro enviado por GET o POST. Estos <em>XML</em> son necesarios para cualquier aplicación basada en <a href="http://www.juanmasantoyo.es/index.php/category/ajax/">Ajax</a> que se precie, además de ser especialmente útiles para otras situaciones (como por ejemplo, compartir contenido interno a un consumidor externo).</p>
<p>Pues bien, hace un par de días me surgió la necesidad y investigué un poco al respecto. En realidad, la solución que nos propone <em>.Net</em> no está nada mal: un archivo <em>.asmx</em> que actúa como <em>WebService</em>.<br />
<span id="more-397"></span></p>
<h3>Sobre los <em>.asmx</em></h3>
<p>De la misma forma que podemos añadir archivos <em>.aspx</em> a un proyecto web, también podemos añadir archivos <em>.asmx</em>. Estos archivos son la base de cualquier proyecto de <em>WebService</em> que desarrollemos en <em>.Net</em>, pero también podemos usarlos en los proyectos web normales.</p>
<h3>¿Cómo se usan los <em>.asmx</em>?</h3>
<p>El uso de los <em>.asmx</em> es simple: básicamente, lo que debemos hacer es crear un método, que devuelva un valor y que tenga la etiqueta [WebMethod]. Al final, la llamada a servidor: &#8220;http://&lt;ruta al asmx&gt;/&lt;nombre del método&gt;/&lt;parámetros get si hay&gt;&#8221; nos devolverá el resultado devuelto por el método.</p>
<p>Por supuesto, el método puede tener parámetros, que enviaremos por <em>GET</em> o por <em>POST</em>; en cuyo caso, al hacer la llamada los parámetros enviados deberán tener el mismo nombre que el parámetro en el método.</p>
<p>Es importante destacar que si queremos enviar parámetros por <em>GET</em> o <em>POST</em>, deberemos activar ambos protocolos en el <em>Web.config</em> de nuestra aplicación.</p>
<h3>Un ejemplo</h3>
<p>La mejor forma de ver cómo funciona un <em>.asmx</em> es viendo un pequeño ejemplo. Con poco esfuerzo podremos hacer algunas pruebas.</p>
<h4>Crear el proyecto</h4>
<p>Sólo necesitaremos crear un nuevo proyecto web en nuestro Visual Studio. Nos creará por defecto un <em>.aspx</em>, pero no nos interesa demasiado.</p>
<h4>Crear el asmx</h4>
<p>Añadiremos en el proyecto web un nuevo elemento, que será un Servicio web. Lo encontrarás entre los posibles elementos de la categoría &#8220;Web&#8221;. Lo llamaremos &#8220;<em>test.asmx</em>&#8220;.</p>
<p>Cuando tengamos nuestro <em>.asmx</em> creado, lo estableceremos como la página inicial de nuestro proyecto (es una opción que aparecerá si clicamos con el botón derecho en el <em>.asmx</em>).</p>
<h4>Un nuevo método en el <em>.asmx</em></h4>
<p>En el <em>.asmx</em> ya tenemos un método creado: <em>HelloWorld</em>. Es muy ilustrativo, ya que nos indica dos cosas: la primera, que nuestros métodos deberán tener al principio la etiqueta <strong>[WebMethod]</strong>. La segunda, que el valor retornado será lo que mostrará nuestro <em>WebService</em> al realizar una petición al método.</p>
<p>Vamos a programar un método más bien absurdo, pero que nos permitirá ver cómo funciona el envío de parámetros:</p>
<pre class="brush:csharp">
	[WebMethod]
	public string ReturnThisString(string str)
	{
		return str;

	}
</pre>
<p>Hay dos cosas a destacar:</p>
<ol>
<li>El parámetro que recibe este método se llama <strong>str</strong>.</li>
<li>El resultado que genera este método, es un <em>string</em>.</li>
</ol>
<h3>Activamos los protocolos <em>GET</em> y <em>POST</em></h3>
<p>Como dije unas líneas más arriba, debemos activar los protocolos de envío <em>GET</em> y <em>POST</em> si queremos usarlos en nuestro <em>WebService</em>. Todo lo que debemos hacer es incluir <strong>dentro</strong> del nodo <em>&lt;system.web/&gt;</em> de nuestro <em>Web.config</em> las siguientes líneas:</p>
<pre class="brush:xml">
	&lt;webServices&gt;
		&lt;protocols&gt;
			&lt;add name=&quot;HttpGet&quot; /&gt;
			&lt;add name=&quot;HttpPost&quot; /&gt;
		&lt;/protocols&gt;
	&lt;/webServices&gt;
</pre>
<h4>Probamos el <em>WebService</em></h4>
<p>Y toca inicar una depuración. La primera vez que lo hagamos nos pedirá que activemos el modo debug. Le decimos que sí, y arrancará el <em>.asmx</em>.</p>
<p><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/03/XmlDinamicosAspNet-01-e1268766894973.png"><img src="http://www.juanmasantoyo.es/wp-content/uploads/2010/03/XmlDinamicosAspNet-01-e1268766894973.png" alt="Métodos del WebService" title="Métodos del WebService" width="450" height="131" class="aligncenter size-full wp-image-403" /></a></p>
<p>Lo que veremos, será un resumen de los métodos que tiene el <em>.asmx</em> (esto no se vería en un entorno de producción). Podemos clicar en el nombre de nuestro método &#8220;<em>ReturnThisString</em>&#8221; para que nos muestre un pequeño formulario donde indicaremos el valor del parámetro &#8220;<em>str</em>&#8220;. Al enviarlo, veremos el resultado de nuestro método de <em>WebService</em>.</p>
<p><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/03/XmlDinamicosAspNet-02-e1268766971605.png"><img src="http://www.juanmasantoyo.es/wp-content/uploads/2010/03/XmlDinamicosAspNet-02-e1268766971605.png" alt="Formulario de envío de parámetros" title="Formulario de envío de parámetros" width="450" height="244" class="aligncenter size-full wp-image-404" /></a></p>
<p>Hay varias cosas interesantes en las que fijarse ahora:</p>
<ol>
<li>
		El resultado devuelto es un XML.	</p>
<p>		Sea cual sea el resultado, el asmx siempre nos dará una respuesta XML.
	</li>
<li>
		La ruta de la página.</p>
<p>		Como ves, la url de este xml es: &#8220;http://&lt;ruta al asmx&gt;/&lt;nombre del método&gt;/&lt;parámetros get si hay&gt;&#8221;. Esta es la dirección a la que accederemos para obtener resultados de nuestro <em>WebService</em>, ya sea con <em>Ajax</em> o con otra tecnología.
	</li>
<li>
		El nodo <em>string</em>.</p>
<p>		El resultado del método viene, en realidad, dentro de un nodo llamado <em>string</em>. Esto es, efectívamente, el tipo del dato devuelto. ¿imaginas lo que pasaría si en vez de un <em>string</em>, devolvemos una lista de <em>strings</em>? Efectivamente, el <em>XML</em> resultante sería una lista de nodos <em>string</em>.</p>
<p><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/03/XmlDinamicosAspNet-03-e1268766951190.png"><img src="http://www.juanmasantoyo.es/wp-content/uploads/2010/03/XmlDinamicosAspNet-03-e1268766951190.png" alt="Resultado de una llamada al método ReturnThisString" title="Resultado de una llamada al método ReturnThisString" width="450" height="114" class="aligncenter size-full wp-image-405" /></a>
	</li>
</ol>
<h4>Generamos un <em>XML</em> como respuesta del método</h4>
<p>Como ves, los <em>WebServices</em> <em>.asmx</em> no son demasiado complicados, aunque por el momento seguimos teniendo un pequeño problema: los <em>XML</em> que generamos como resultado son generados automáticamente y no tenemos control sobre ellos. Vamos a poner solución a esto.</p>
<p>La cuestión es que si en lugar de un <em>string</em>, devolvemos un objeto <em>XmlDocument</em>, el resultado devuelto por el <em>WebService</em> será el contenido del <em>XmlDocument</em> tal cual. Por lo tanto, se podría decir que el proceso para generar respuestas <em>XML</em> personalizadas (la razón de este artículo) es el siguiente:</p>
<ol>
<li>Creamos un método en el <em>.asmx</em> que devuelva un objeto <em>XmlDocument</em>.</li>
<li>Obtenemos todos los datos que queramos devolver.</li>
<li>Los metemos en un <em>XmlDocument</em>.</li>
<li>Retornamos el <em>XmlDocument</em>.</li>
</ol>
<h4>Un último ejemplo</h4>
<p>Vamos a crear un último ejemplo: un pequeño método que nos introducirá en la creación de <em>XML</em> dinámicos:</p>
<pre class="brush:csharp">
	[WebMethod]
	public XmlDocument ReturnXml()
	{
		XmlDocument xml = new XmlDocument();

		XmlNode rootNode = xml.CreateElement("RespuestaXml");
		XmlNode txtNode = xml.CreateTextNode("Xml generado automáticamente");
		XmlNode subNode = xml.CreateElement("HolaMundo");
		XmlNode commentNode = xml.CreateComment("Esto es un comentario");

		subNode.AppendChild(commentNode);
		rootNode.AppendChild(txtNode);
		rootNode.AppendChild(subNode);
		xml.AppendChild(rootNode);

		return xml;
	}
</pre>
<p>Esto generaría el siguiente resultado:</p>
<p><a href="http://www.juanmasantoyo.es/wp-content/uploads/2010/03/XmlDinamicosAspNet-04-e1268766930458.png"><img src="http://www.juanmasantoyo.es/wp-content/uploads/2010/03/XmlDinamicosAspNet-04-e1268766930458.png" alt="Resultado de una llamada al método ReturnXml" title="Resultado de una llamada al método ReturnXml" width="450" height="211" class="aligncenter size-full wp-image-406" /></a></p>
<h3>Descargar el ejemplo</h3>
<p>Puedes descargar el ejemplo si algo no te ha quedado claro. Recuerda también que puedes dejar comentarios.</p>
<p><a href='http://www.juanmasantoyo.es/wp-content/uploads/2010/03/JuanmaSantoyoXmlDinamicos.zip'>JuanmaSantoyoXmlDinamicos</a></p>
<h3>¡Hemos terminado!</h3>
<p>Y de momento no hay mucho más que decir. Con este ejemplo, hemos tocado un tema especialmente importante para la mayoría de proyectos web, ya que los <em>XML</em> dinámicos son importantes para las funcionalidades <em>Ajax</em> y para comunicarnos con otras aplicaciones externas. ¿Aún no tienes RSS en tu sitio web? <a href="http://www.rssboard.org/rss-specificationg">¡A qué esperas!</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/03/16/xml-dinamicos-con-asp-net/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Clase para acceso avanzado a las AppSettings del WebConfig</title>
		<link>http://www.juanmasantoyo.es/index.php/2010/02/08/clase-para-acceso-avanzado-a-las-appsettings-del-webconfig/</link>
		<comments>http://www.juanmasantoyo.es/index.php/2010/02/08/clase-para-acceso-avanzado-a-las-appsettings-del-webconfig/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 17:41:54 +0000</pubDate>
		<dc:creator>Juanma</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[appsettings]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[webconfig]]></category>

		<guid isPermaLink="false">http://www.juanmasantoyo.es/?p=292</guid>
		<description><![CDATA[Para quien no lo sepa, el WebConfig de asp.Net es un archivo XML que contiene todos los aspectos de configuración de nuestra aplicación. Entre los distintos parámetros, hay un nodo llamado AppSettings que nos permite definir pares clave / valor para después acceder a ellas desde cualquier punto de la aplicación.

Pero, a pesar de que el api proporciona dos métodos diferentes de acceso a las AppSettings, ambos tienen algunos inconvenientes. Lo que haremos, es crear una pequeña clase que nos permitirá tratar las AppSettings de una forma más simple y con una funcionalidad mejorada. <a href="http://www.juanmasantoyo.es/index.php/2010/02/08/clase-para-acceso-avanzado-a-las-appsettings-del-webconfig/">Seguir leyendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Para quien no lo sepa, el WebConfig de asp.Net es un archivo XML que contiene todos los aspectos de configuración de nuestra aplicación. Entre los distintos parámetros, hay un nodo llamado AppSettings que nos permite definir pares clave / valor para después acceder a ellas desde cualquier punto de la aplicación.</p>
<p>Pero, a pesar de que el api proporciona dos métodos diferentes de acceso a las AppSettings, ambos tienen algunos inconvenientes. Lo que haremos, es crear una pequeña clase que nos permitirá tratar las AppSettings de una forma más simple y con una funcionalidad mejorada.<br />
<span id="more-292"></span></p>
<p><a href='http://www.juanmasantoyo.es/wp-content/uploads/2010/02/AppSettings.zip'>Clase AppSettings</a></p>
<h3>Los inconvenientes</h3>
<p>Los inconvenientes a la hora de trabajar con las variables de AppSettings son varios. Se podría decir que hay dos formas de acceder. Comentemos las dos formas:</p>
<h4>Primera forma: la clase ConfigurationManager</h4>
<p>La clase ConfigurationManager nos proporciona un diccionario estático que contiene los valores definidos en AppSettings:</p>
<pre class="brush:c-sharp">
ConfigurationManager.AppSettings["clave"];
</pre>
<p>A mi modo de ver, más largo de lo necesario y poco intuitivo (escribid &#8220;Configuration&#8221; en un VisualStudio y mirad cuantas entradas muestra el intellisense). Prometo que me costó un par de intentos memorizar esta línea.</p>
<p>Por otra parte, lo más grave es que el dato devuelto es siempre un <em>string</em>, lo cual nos obliga a hacer una conversión del dato inmediatamente después si queremos otro formato.</p>
<h4>Segunda forma: la clase AppSettingsReader</h4>
<p>Esta segunda clase, tiene algunas ventajas respecto a la anterior, pero también es mejorable.</p>
<p>Por una parte, su uso requiere de una instancia de AppSettingsReader y la posterior invocación a un método:</p>
<pre class="brush:c-sharp">
AppSettingsReader settings = new AppSettingsReader();
string valor = (string) settings.GetValue("clave", typeof(string));
</pre>
<p>Por no mencionar el casting obligatorio.</p>
<p>Por otra parte, aunque en principio se puede especificar el tipo del dato devuelto, hay poco control sobre él. ¿Qué pasa si es una fecha? Sinceramente, ni lo he probado.</p>
<h3>Una posible solución</h3>
<p>Yendo al grano, lo cierto es que el acceso a las AppSettings es mejorable. Vamos a considerar y a anotar algunas cosas sobre una posible alternativa que nos solucione los inconvenientes comentados:</p>
<ul>
<li>Se accede desde una clase estática, pero más simple que la vista en la primera forma de acceder. No debería ser mucho más complejo que <em>AppSettings.GetValue(&#8220;clave&#8221;)</em>.</li>
<li>Se ofrecen varios tipos básicos (pocos, pero controlados) como posibles retornos. Nuestra clase se encarga de hacer la conversión.</li>
<li>Además, la clase será capaz de realizar un tratamiento avanzado del contenido de las AppSettings: podrá retornar listas y diccionarios de los tipos básicos.</li>
</ul>
<h3>Definimos los diferentes tipos básicos de AppSettings</h3>
<p>Serán solamente cuatro, pero a cambio ganaremos el control absoluto sobre los valores que retornamos.</p>
<p>Los tipos serán:</p>
<ul>
<li><em>string</em></li>
<li><em>int</em></li>
<li><em>bool</em></li>
<li><em>DateTime</em></li>
</ul>
<h3>Definimos los diferentes formatos de las claves del AppSettings</h3>
<p>Vamos a estipular como deberán ser escritos los datos en el WebConfig. A partir de ahora, deberemos escribir los valores en AppSettings siguiendo uno de los formatos siguientes. aunque ante cualquier excepción, siempre podremos obtener los datos como un tipo básico <em>string</em> (que será como si no aplicásemos ninguna conversión). </p>
<h4>Datos básicos</h4>
<p>No consideraremos un formato para los datos básicos porque no lo necesitan (excepto las fechas, que las vemos ahora mismo). Las cadenas se escribirán como cadenas, los números como números, y los booleanos como <em>&#8220;true&#8221;</em> y <em>&#8220;false&#8221;</em>.</p>
<h4>Fechas</h4>
<p>Las fechas seguirán un formato típicamente español:</p>
<p>dia/mes/año</p>
<p>Donde los días van del 1 al 31, los meses del 1 al 12, y los años tienen cuatro cifras.</p>
<h4>Listas</h4>
<p>Separaremos los diferentes elementos con comas.</p>
<p>valor1, valor2, valor3, valor4</p>
<p>Los espacios en blanco entre los valores y las comas serán indiferentes.</p>
<h4>Diccionarios</h4>
<p> Separaremos los pares con comas, y los clave valor con dos puntos.</p>
<p>clave1: valor1, clave2: valor2, clave3: valor3</p>
<p>Al igual que antes, los espacios en blanco entre los separadores y los valores son indiferentes.</p>
<h3>Programamos la clase AppSettings</h3>
<p>Ya sabemos que formatos tenemos que considerar. Ahora vamos a programar nuestra clase.</p>
<p>Antes que nada, debemos tener más o menos claro cómo queremos hacerlo. Lo haremos así:</p>
<ul>
<li>Tendremos un método privado, llamado <em>Get</em>; que se encargará simplemente de obtener el dato de WebConfig como un <em>string</em>.</li>
<li>Tendremos tres métodos privados que se encargarán de convertir un <em>string</em> al resto de formatos. Se llamarán <em>ToInt</em>, <em>ToBool</em> y <em>ToDate</em>.</li>
<li>Tendremos varios métodos públicos, cada uno devolverá un tipo de dato. Los métodos se llamarán <em>GetAsString</em>, <em>GetAsStringList</em>, <em>GetAsStringDict</em>, <em>GetAsInt</em>, etc.</li>
</ul>
<h4>El método Get</h4>
<pre class="brush:c-sharp">
private static string Get(string key)
{
	AppSettingsReader settings = new AppSettingsReader();            

	return (string) settings.GetValue(key, typeof(string));
}
</pre>
<p>En este método tenemos la única instancia de AppSettingsReader que vamos a usar. El resto de métodos de la clase se apoyarán en este para obtener la información del WebConfig.</p>
<h4>Los métodos conversores</h4>
<p>Serán tres. Uno convertirá un <em>string</em> a <em>int</em>, otro a <em>bool</em> y el último a <em>DateTime</em>.</p>
<pre class="brush:c-sharp">
private static int ToInt(string value)
{
	int v = 0;

	if (int.TryParse(value, out v))
	{
		return v;
	}

	return 0;
}

private static bool ToBool(string value)
{
	bool v = false;

	if (bool.TryParse(value, out v))
	{
		return v;
	}

	return false;
}

private static DateTime ToDateTime(string value)
{
	DateTime v = DateTime.Today;

	if (DateTime.TryParseExact(value, "dd/MM/yyyy", new CultureInfo("es-ES"), DateTimeStyles.None, out v))
	{
		return v;
	}

	else return new DateTime();
}
</pre>
<h4>Los métodos que retornarán los datos</h4>
<p>Estos métodos se apoyarán en los anteriores para obtener los valores, darles un formato de salida y devolverlos. Hay uno por tipo de dato y tipo de salida (normal, lista o diccionario).</p>
<p>Además, a los métodos que requieran de un separador (los que devuelven listas y diccionarios) les haremos una sobrecarga que permita indicar un separador distinto. De esta forma podremos reaccionar ante posibles conflictos entre nuestros separadores por defecto y los valores que contengan las AppSettings.</p>
<pre class="brush:c-sharp">
public static string GetAsString(string key)
{
	string value = Get(key);

	return value;
}

public static int GetAsInt(string key)
{
	string value = Get(key);

	return ToInt(value);
}

public static bool GetAsBool(string key)
{
	string value = Get(key);

	return ToBool(value);
}

public static DateTime GetAsDateTime(string key)
{
	string value = Get(key);

	return ToDateTime(value);
}

public static List&lt;string&gt; GetAsStringList(string key, char sep)
{
	string value = Get(key);
	List&lt;string&gt; list = new List&lt;string&gt;();

	foreach (string s in value.Split(new char[] { sep }))
	{
		list.Add(s.Trim());
	}

	return list;
}

public static List&lt;string&gt; GetAsStringList(string key)
{
	return GetAsStringList(key, '|');
}

public static List&lt;int&gt; GetAsIntList(string key, char sep)
{
	string value = Get(key);
	List&lt;int&gt; list = new List&lt;int&gt;();

	foreach (string s in value.Split(new char[] { sep }))
	{
		list.Add(ToInt(s.Trim()));
	}

	return list;
}

public static List&lt;int&gt; GetAsIntList(string key)
{
	return GetAsIntList(key, '|');
}

public static List&lt;bool&gt; GetAsBoolList(string key, char sep)
{
	string value = Get(key);
	List&lt;bool&gt; list = new List&lt;bool&gt;();

	foreach (string s in value.Split(new char[] { sep }))
	{
		list.Add(ToBool(s.Trim()));
	}

	return list;
}

public static List&lt;bool&gt; GetAsBoolList(string key)
{
	return GetAsBoolList(key, '|');
}

public static List&lt;DateTime&gt; GetAsDateTimeList(string key, char sep)
{
	string value = Get(key);
	List&lt;DateTime&gt; list = new List&lt;DateTime&gt;();

	foreach (string s in value.Split(new char[] { sep }))
	{
		list.Add(ToDateTime(s.Trim()));
	}

	return list;
}

public static List&lt;DateTime&gt; GetAsDateTimeList(string key)
{
	return GetAsDateTimeList(key, '|');
}

public static Dictionary&lt;string, string&gt; GetAsStringDict(string key, char itemSep, char valueSep)
{
	string raw = Get(key);
	Dictionary&lt;string, string&gt; dict = new Dictionary&lt;string, string&gt;();

	foreach(string i in raw.Split(itemSep))
	{
		string item = i.Trim();

		string[] value = item.Split(valueSep);
		value[0] = value[0].Trim();
		value[1] = value[1].Trim();

		if (!dict.ContainsKey(value[0]))
		{
			dict.Add(value[0], value[1]);
		}
	}

	return dict;
}

public static Dictionary&lt;string, string&gt; GetAsStringDict(string key)
{
	return GetAsStringDict(key, ';', '|');
}

public static Dictionary&lt;string, int&gt; GetAsIntDict(string key, char itemSep, char valueSep)
{
	string raw = Get(key);
	Dictionary&lt;string, int&gt; dict = new Dictionary&lt;string, int&gt;();

	foreach (string i in raw.Split(itemSep))
	{
		string item = i.Trim();

		string[] value = item.Split(valueSep);
		value[0] = value[0].Trim();
		value[1] = value[1].Trim();

		if (!dict.ContainsKey(value[0]))
		{
			dict.Add(value[0], ToInt(value[1]));
		}
	}

	return dict;
}

public static Dictionary&lt;string, int&gt; GetAsIntDict(string key)
{
	return GetAsIntDict(key, ';', '|');
}

public static Dictionary&lt;string, bool&gt; GetAsBoolDict(string key, char itemSep, char valueSep)
{
	string raw = Get(key);
	Dictionary&lt;string, bool&gt; dict = new Dictionary&lt;string, bool&gt;();

	foreach (string i in raw.Split(itemSep))
	{
		string item = i.Trim();

		string[] value = item.Split(valueSep);
		value[0] = value[0].Trim();
		value[1] = value[1].Trim();

		if (!dict.ContainsKey(value[0]))
		{
			dict.Add(value[0], ToBool(value[1]));
		}
	}

	return dict;
}

public static Dictionary&lt;string, bool&gt; GetAsBoolDict(string key)
{
	return GetAsBoolDict(key, ';', '|');
}

public static Dictionary&lt;string, DateTime&gt; GetAsDateTimeDict(string key, char itemSep, char valueSep)
{
	string raw = Get(key);
	Dictionary&lt;string, DateTime&gt; dict = new Dictionary&lt;string, DateTime&gt;();

	foreach (string i in raw.Split(itemSep))
	{
		string item = i.Trim();

		string[] value = item.Split(valueSep);
		value[0] = value[0].Trim();
		value[1] = value[1].Trim();

		if (!dict.ContainsKey(value[0]))
		{
			dict.Add(value[0], ToDateTime(value[1]));
		}
	}

	return dict;
}

public static Dictionary&lt;string, DateTime&gt; GetAsDateTimeDict(string key)
{
	return GetAsDateTimeDict(key, ';', '|');
}
</pre>
<h3>¡Hemos terminado!</h3>
<p>Ahora ya tenemos una clase que, además de ser fácilmente utilizable desde cualquier parte del programa, nos proporciona los valores de AppSettings en el formato que necesitemos. Además hemos extendido la funcionalidad respecto al api de asp.Net por que también es capaz de obtener los datos en forma de listas y diccionarios.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.juanmasantoyo.es/index.php/2010/02/08/clase-para-acceso-avanzado-a-las-appsettings-del-webconfig/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

