<?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:rssdatehelper="urn:rssdatehelper"><channel><title>SetValue.NET</title><link>http://setvalue.net</link><pubDate>2013-05-04T04:32:35</pubDate><generator>umbraco</generator><description></description><language>en</language><item><title>Open XML SDK 2.5</title><link>http://setvalue.net/2013/5/4/open-xml-sdk-25/</link><pubDate>Sat, 04 May 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/5/4/open-xml-sdk-25/</guid><description><![CDATA[ 
<p>Cuando se elabora una aplicación, uno de las situaciones que nos
encontramos, es la definición de los requisitos de instalación que
vamos a tener, y una vez los tenemos claros, debemos decidir,
cuáles podemos proveer nosotros en la instalación por ejemplo un
Microsoft SQL Compact, y cuáles debe tener pre-instalado el usuario
para que la aplicación funcione correctamente.<!--more--></p>

<p>Y es en este segundo apartado, en el que la obligación reside en
la máquina del usuario, es dónde solemos tener un requisito
habitual y es que deba tener instalado Microsoft Office, para que
desde la aplicación podamos generar documentos de Excel o de
Word.</p>

<p>Pero, para salvar este escenario, suele haber soluciones
prácticas, bastante simples, que la verdad es que si bien
solucionan la papeleta, no son las formas más adecuadas. Y es por
ello que elaboro este post, para hablar de <a
href="http://msdn.microsoft.com/en-us/library/office/bb448854.aspx"
target="_blank">Microsoft Open XML SDK</a>.</p>

<p>Este SDK, que se puede descargar desde <a
href="http://www.microsoft.com/en-us/download/details.aspx?id=30425"
 target="_blank">aquí</a>, provee unas librerías que nos van a
permitir hacer documentos de Word, Excel o PowerPoint, desde cero y
con todo lujo de detalles. Para crear los documentos únicamente se
requiere el segundo enlace, que además es el que debemos introducir
entre los requisitos del paquete de instalación de nuestros
desarrollos, para que los usuarios puedan generar documentos sin
necesidad de tener instalado Office. No obstante para el
desarrollador, el primer enlace es muy interesante, porque permite
decompilar un documento de Office y convertirlo a código fuente,
así si no somos muy buenos con el diseño, y sobre todo generando
documentos muy estéticos podremos decompilar uno, coger el código
fuente, y adaptarlo para nuestro objetivo.</p>

<p>Una vez instalado el SDK, creamos un proyecto de Consola de
Windows, y agregamos la referencia al proyecto y al código.</p>

<p><a
href="/media/4945/Windows-Live-Writer_7bd2d794360d_7F43_image_2.png">
<img src="/media/4950/Windows-Live-Writer_7bd2d794360d_7F43_image_thumb.png" width="504" height="348" alt="Agregar referencia" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p>Después hacemos un sencillo documento de Word, en el que
agregamos una línea de texto con un saludo y la hora y fecha en la
que estamos elaborando el documento. Para ello, lo primero es crear
un nuevo Documento, una vez creado, debemos agregar un nuevo
MainDocumentPart y un nuevo Body (esto es porque el documento es
nuevo, si abriéramos uno existente iríamos directamente a por el
cuerpo del Word). Seguido, creamos un nuevo Text, en un objeto Run,
al que le establecemos distintas propiedades de formato, y este
objeto lo anidamos dentro de un párrafo, que será el objeto final,
que introducimos en el cuerpo del documento.</p>

<pre class="brush: c#">
using System;
using System.IO;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

namespace OpenXML
{
    class Program
    {
        static void Main(string[] args)
        {
            var file = Path.Combine(Environment.CurrentDirectory, "MiDocumento.docx");
            try
            {
                var doc = WordprocessingDocument.Create(file, WordprocessingDocumentType.Document, true);
                Console.Write("Introduce tu nombre: ");
                var texto = Console.ReadLine();
                var dt = DateTime.Now;

                doc.AddMainDocumentPart();
                doc.MainDocumentPart.Document = new Document
                                                    {
                                                        Body = new Body()
                                                    };

                var text = new Text(String.Format("Hola {0} son las {1} del {2}",
                                           texto, dt.ToShortTimeString(),
                                           dt.ToShortDateString()));
                var run = new Run
                              {
                                  RunProperties = new RunProperties
                                                      {
                                                          FontSize = new FontSize { Val = "30" },
                                                          Color = new Color { Val = "FF0000" }
                                                      }
                              };
                run.AppendChild(text);
                var parg = new Paragraph();
                parg.AppendChild(run);

                doc.MainDocumentPart.Document.Body.AppendChild(parg);
                doc.Close();
                System.Diagnostics.Process.Start(file);
                Console.WriteLine("Abriendo el documento");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.WriteLine("Pulse una tecla para continuar...");
            Console.ReadKey();
        }
    }
}
</pre>

<p>La estructura interna del documento, es un paquete con distintos
ficheros en formato XML, es por ello que todos los objetos se van
anidando unos dentro de otros, y por eso que parezca tan enrevesado
el agregar un texto.</p>

<p>Finalmente esto nos deja un documento como este.</p>

<p><a
href="/media/4955/Windows-Live-Writer_7bd2d794360d_7F43_WordConsole_2.png">
<img src="/media/4960/Windows-Live-Writer_7bd2d794360d_7F43_WordConsole_thumb.png" width="504" height="254" alt="Documento de Word" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p><strong>Nota</strong>: Si bien generamos los documentos sin
necesidad de tener instalada una versión de Office, sí es necesario
tener un programa que sea capaz de abrir los documentos
generados.</p>

<?UMBRACO_MACRO
location="https://skydrive.live.com/embed?cid=3A3CE6BACC2DC9F6&amp;resid=3A3CE6BACC2DC9F6%2132125&amp;authkey=AEJyS0ezAIW4_Mo"
macroAlias="Skydrive" />
]]></description></item><item><title>Tutorial MVVM IX (Resumen)</title><link>http://setvalue.net/2013/4/9/tutorial-mvvm-ix-(resumen)/</link><pubDate>Tue, 09 Apr 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/4/9/tutorial-mvvm-ix-(resumen)/</guid><description><![CDATA[ 
<p>En este tutorial hemos visto elementos esenciales en el uso del
Patrón MVVM, pero faltan otros, algunos más importantes y otros
menos, el objetivo era tener una de toma de contacto con el patrón.
<!--more--></p>

<p>Cabe destacar, el uso de diccionarios de recursos, que son un
elemento fundamental a la hora de establecer diseños de
componentes. Generalizando, es algo similar a los estilos css en
una aplicación web, aunque más potente y con mayor nivel de
personalización.</p>

<p>Otra cosa interesante y fundamental, y que por el tipo de
ejemplo utilizado en el tutorial, es el acceso a datos mediante
Entity Framework y una capa de servicios WCF.</p>

<p>A continuación un enlace a los distintos artículos, y el fichero
de ejemplo de la aplicación.</p>

<p><a href="/2013/3/29/tutorial-mvvm-i/" target="_blank">Post
1</a>: Breve definición del patrón</p>

<p><a href="/2013/3/31/tutorial-mvvm-ii/" target="_blank">Post
2</a>: Creación del modelo</p>

<p><a href="/2013/4/1/tutorial-mvvm-iii/" target="_blank">Post
3</a>: Lógica de negocio del modelo</p>

<p><a href="/2013/4/2/tutorial-mvvm-iv/" target="_blank">Post
4</a>: Ejemplo de test unitarios</p>

<p><a href="/2013/4/3/tutorial-mvvm-v/" target="_blank">Post 5</a>:
Creación de la vista</p>

<p><a href="/2013/4/4/tutorial-mvvm-vi/" target="_blank">Post
6</a>: Definición ampliada del ViewModel</p>

<p><a href="/2013/4/7/tutorial-mvvm-vii/" target="_blank">Post
7</a>: Creación de la vista-modelo</p>

<p><a href="/2013/4/8/tutorial-mvvm-viii/" target="_blank">Post
8</a>: Uso de convertidores</p>

<p><a href="/2013/4/9/tutorial-mvvm-ix-(resumen)/"
target="_self">Post 9</a>: Resumen y aplicación</p>

<?UMBRACO_MACRO
location="https://skydrive.live.com/embed?cid=3A3CE6BACC2DC9F6&amp;resid=3A3CE6BACC2DC9F6%2132036&amp;authkey=AAR-5JUXraIBRFo"
macroAlias="Skydrive" />
]]></description></item><item><title>Tutorial MVVM VIII</title><link>http://setvalue.net/2013/4/8/tutorial-mvvm-viii/</link><pubDate>Mon, 08 Apr 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/4/8/tutorial-mvvm-viii/</guid><description><![CDATA[ 
<p>Los Converters son una propiedad de los Binding que permite
realizar una llamada a un método, el cuál realiza una conversión a
través del paso de un parámetro desde el marcado XAML. En este
tutorial vamos a implementar el uso del convertidor para realizar
transformaciones entre grados Farenheit y Celsius.<!--more--></p>

<p>Los convertidores son clases que implementan la interfaz
<em>IValueConverter</em> o <em>IMultiValueConverter</em>, la
primera permiten el paso de un valor, y la segunda el paso de
múltiples valores.</p>

<p>Creamos una clase llamada Degrees a la que le implementamos la
interfaz <em>IMultiValueConverter</em>, esta interfaz tiene dos
métodos, uno de conversión que va del origen del dato al destino y
otro de regreso que va del destino al origen.</p>

<h6>Degrees.cs</h6>

<pre class="brush: c#">
using System;
using System.Globalization;
using System.Windows.Data;

namespace WeatherApp.Converters
{
    public class Degrees : IMultiValueConverter
    {
        public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                if (value.Length == 2)
                {
                    var farenheit = value[0] is double ? (double)value[0] : 0;
                    var isFarenheit = value[1] is bool &amp;&amp; (bool)value[1];
                    var celsius = (farenheit - 32)/1.8;
                    return isFarenheit ? farenheit.ToString("N2") : celsius.ToString("N2");
                }
                
                return null;
            }
            catch (Exception)
            {
                return null;
            }
        }

        public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}
</pre>

<p>E implementamos el convertidor dentro de la página
<em>Weather</em>. Para ello, lo primero que hay que hacer es
agregar el namespace a las referencias del marcado, luego crear un
recurso para que pueda ser llamado desde la propiedad, y por último
emplearlo en la propiedad, que vamos a dejarlo así.</p>

<h6>Weather.xaml</h6>

<pre class="brush: xml">
&lt;Page x:Class="WeatherApp.Components.Weather"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:conv="clr-namespace:WeatherApp.Converters"
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
      Title="Weather"&gt;
    &lt;Page.Resources&gt; &lt;conv:Degrees x:Key="ConvDegrees"/&gt; &lt;/Page.Resources&gt;

    &lt;Grid Background="Black"&gt;
        &lt;Grid.ColumnDefinitions&gt;
            &lt;ColumnDefinition/&gt;
            &lt;ColumnDefinition Width="Auto"/&gt;
        &lt;/Grid.ColumnDefinitions&gt;
        &lt;Image Grid.Column="0" Stretch="Uniform" VerticalAlignment="Center" Source="{Binding Path=ImageSource}"/&gt;
        &lt;StackPanel Grid.Column="1" VerticalAlignment="Center" Orientation="Vertical" Margin="0,0,30,0"&gt;
            &lt;TextBlock FontSize="36pt" FontWeight="Bold" Foreground="White" TextAlignment="Right"&gt; &lt;TextBlock.Text&gt; &lt;MultiBinding Converter="{StaticResource ConvDegrees}"&gt; &lt;MultiBinding.Bindings&gt; &lt;Binding Path="CurrentWeather.temperature"/&gt; &lt;Binding Path="IsFarenheit"/&gt; &lt;/MultiBinding.Bindings&gt; &lt;/MultiBinding&gt; &lt;/TextBlock.Text&gt; &lt;/TextBlock&gt;
            &lt;TextBlock FontSize="18pt" FontWeight="Bold" Text="{Binding Path=CurrentWeather.icon}" Foreground="White"/&gt;
        &lt;/StackPanel&gt;
        &lt;Border Grid.ColumnSpan="2" CornerRadius="5" VerticalAlignment="Top" 
                HorizontalAlignment="Right" Margin="0,5,30,0"&gt;
            &lt;StackPanel Orientation="Horizontal"&gt;
                &lt;RadioButton GroupName="Degree" Foreground="White" IsChecked="{Binding Path=IsFarenheit}"&gt;Fº&lt;/RadioButton&gt;
                &lt;RadioButton GroupName="Degree" Foreground="White"&gt;Cº&lt;/RadioButton&gt;
            &lt;/StackPanel&gt;
        &lt;/Border&gt;
    &lt;/Grid&gt;
&lt;/Page&gt;
</pre>

<p>Y finalmente obtenemos algo como esto.</p>

<p><a
href="/media/4620/Windows-Live-Writer_Tutorial-MVVM-VIII_12119_WeatherApp_1.png">
<img src="/media/4625/Windows-Live-Writer_Tutorial-MVVM-VIII_12119_WeatherApp_1_thumb.png" width="504" height="337" alt="WeatherApp_1" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p><a
href="/media/4630/Windows-Live-Writer_Tutorial-MVVM-VIII_12119_WeatherApp_2_2.png">
<img src="/media/4635/Windows-Live-Writer_Tutorial-MVVM-VIII_12119_WeatherApp_2_thumb.png" width="504" height="337" alt="WeatherApp_2" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p><a
href="/media/4640/Windows-Live-Writer_Tutorial-MVVM-VIII_12119_WeatherApp_3.png">
<img src="/media/4645/Windows-Live-Writer_Tutorial-MVVM-VIII_12119_WeatherApp_3_thumb.png" width="504" height="337" alt="WeatherApp_3" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>
]]></description></item><item><title>Tutorial MVVM VII</title><link>http://setvalue.net/2013/4/7/tutorial-mvvm-vii/</link><pubDate>Sun, 07 Apr 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/4/7/tutorial-mvvm-vii/</guid><description><![CDATA[ 
<p>Continuamos con el ViewModel, después de generar la clase
abstracta para la notificación de cambios, y la clase que
implementa <em>ICommand</em>, empezamos a crear propiedades en la
vista-modelo del componente Search.<!--more--></p>

<p>Creamos cinco propiedades públicas y un método privado. Las
propiedades son, un <em>String</em> que se enlazará con el TextBox
de la vista, un <em>ICommand</em> que se enlazará con el botón de
búsqueda, una propiedad
<em>List&lt;Model.Location&gt;</em>,&nbsp;que es la fuente de datos
de la que se va a alimentar el ListBox, otra que será la
localización que se seleccione en el ListBox. Y una propiedad tipo
ViewModel.MainWindow que será el contexto de datos del MainWindow,
de esta forma podremos llamar a un método en esa vista-modelo. El
método hará una llamada a la clase Client dónde habíamos definido
una petición al WebService de Bing Maps.</p>

<h6>ViewModel.Search.cs</h6>

<pre class="brush: c#">
using System;
using System.Collections.Generic;
using System.Windows.Input;
using WeatherApp.Common;
using WeatherApp.Model;

namespace WeatherApp.ViewModel
{
    public class Search : ObservableObject
    {
        private const int MinLength = 3;

        public MainWindow Mw { get; set; }

        private String _textSearch;
        public String TextSearch
        {
            get { return _textSearch; }
            set
            {
                _textSearch = value;
                OnPropertyChanged("TextSearch");
            }
        }

        private List&lt;Location&gt; _locations;
        public List&lt;Location&gt; Locations
        {
            get { return _locations; }
            set
            {
                _locations = value;
                OnPropertyChanged("Locations");
            }
        }

        private Location _location;
        public Location Location
        {
            get { return _location; }
            set
            {
                _location = value;
                OnPropertyChanged("Location");
                Mw.GetWeather(value);
            }
        }

        private ICommand _searchIt;
        public ICommand SearchIt
        {
            get { return _searchIt ?? (_searchIt = new RelayCommand(param =&gt; GetSearch())); }
        }

        private void GetSearch()
        {
            if (String.IsNullOrEmpty(TextSearch)) return;

            if (TextSearch.Length &gt;= MinLength)
            {
                Locations = Client.GetLocations(TextSearch);
            }
        }
    }
}
</pre>

<p>A continuación enlazamos las propiedades que hemos definido a la
vista, es decir al componente Search, para enlazar estas
propiedades, hay que ir a cada elemento del XAML y establecer un
<em>Binding</em>.</p>

<h6>Search.xaml</h6>

<pre class="brush: xml">
&lt;UserControl x:Class="WeatherApp.Components.Search"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"&gt;
    &lt;Grid&gt;
        &lt;Grid.RowDefinitions&gt;
            &lt;RowDefinition Height="Auto"/&gt;
            &lt;RowDefinition /&gt;
        &lt;/Grid.RowDefinitions&gt;
        &lt;StackPanel Grid.Row="0" Orientation="Vertical" Margin="5"&gt;
            &lt;TextBox MinWidth="200" Text="{Binding Path=TextSearch}"/&gt;
            &lt;Button Content="Buscar" HorizontalAlignment="Right" Command="{Binding Path=SearchIt}"/&gt;
        &lt;/StackPanel&gt;
        &lt;ListBox Grid.Row="1" Margin="5" ItemsSource="{Binding Path=Locations}" ItemTemplate="{DynamicResource DtLocations}" SelectedValue="{Binding Path=Location}"&gt;
            &lt;ListBox.Resources&gt;
                &lt;DataTemplate x:Key="DtLocations"&gt;
                    &lt;Grid MaxWidth="250"&gt;
                        &lt;Grid.RowDefinitions&gt;
                            &lt;RowDefinition/&gt;
                            &lt;RowDefinition/&gt;
                        &lt;/Grid.RowDefinitions&gt;
                        &lt;StackPanel Grid.Row="1" Orientation="Horizontal"&gt;
                            &lt;TextBlock Margin="3"&gt;
                                &lt;Run Text="Lat: " FontWeight="Bold"/&gt;
                                &lt;Run Text="{Binding Path=Coordinates.Latitude, StringFormat=N2}"/&gt;
                            &lt;/TextBlock&gt;
                            &lt;TextBlock Margin="3"&gt;
                                &lt;Run Text="Lon: " FontWeight="Bold"/&gt;
                                &lt;Run Text="{Binding Path=Coordinates.Longitude, StringFormat=N2}"/&gt;
                            &lt;/TextBlock&gt;
                        &lt;/StackPanel&gt;
                        &lt;TextBlock Grid.Row="0" FontWeight="Bold" FontSize="14pt"&gt;
                            &lt;TextBlock.Text&gt;
                                &lt;MultiBinding StringFormat="{}{0} ({1})"&gt;
                                    &lt;Binding Path="Name"/&gt;
                                    &lt;Binding Path="Country"/&gt;
                                &lt;/MultiBinding&gt;
                            &lt;/TextBlock.Text&gt;
                        &lt;/TextBlock&gt;
                    &lt;/Grid&gt;
                &lt;/DataTemplate&gt;
            &lt;/ListBox.Resources&gt;
        &lt;/ListBox&gt;
    &lt;/Grid&gt;
&lt;/UserControl&gt;
</pre>

<p>Seguimos con la página Weather que en su ViewModel tiene cuatro
propiedades y un método, una propiedad que define la ruta de la
imagen que se va a cargar con todos los datos del clima de la
ciudad seleccionada, una de tipo <em>Weather</em>, otra tipo
<em>DataPoint</em> con el clima actual, y una última que indica si
es Farenheit o Celsius. El método transforma el clima en una imagen
que he cargado en la carpeta Resources, y a las que le he cambiado
la forma en que se incluyen en la compilación a Contenido y que se
copie siempre.</p>

<p><a
href="/media/4560/Windows-Live-Writer_Tutorial-MVVM-VII_9448_Resources_2.png">
<img src="/media/4565/Windows-Live-Writer_Tutorial-MVVM-VII_9448_Resources_thumb.png" width="130" height="244" alt="Resources" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<h6>ViewModel.Weather.cs</h6>

<pre class="brush: c#">
using System;
using System.IO;
using WeatherApp.Common;
using WeatherApp.Model;

namespace WeatherApp.ViewModel
{
    public class Weather : ObservableObject
    {
        private Uri _imageSource;
        public Uri ImageSource
        {
            get { return _imageSource; }
            set
            {
                _imageSource = value;
                OnPropertyChanged("ImageSource");
            }
        }

        private Model.Weather _city;
        public Model.Weather City
        {
            get { return _city; }
            set
            {
                _city = value;
                OnPropertyChanged("City");
                GetCurrent();
            }
        }

        private bool _isFarenheit;
        public bool IsFarenheit
        {
            get { return _isFarenheit; }
            set
            {
                _isFarenheit = value;
                OnPropertyChanged("IsFarenheit");
            }
        }

        private DataPoint _currentWeather;
        public DataPoint CurrentWeather
        {
            get { return _currentWeather; }
            set
            {
                _currentWeather = value;
                OnPropertyChanged("CurrentWeather");
            }
        }

        private void GetCurrent()
        {
            if (City != null &amp;&amp; City.currently != null)
            {
                CurrentWeather = City.currently;
                var png = String.Format(@"{0}\Resources\{1}.png", 
                    Environment.CurrentDirectory, CurrentWeather.icon);
                ImageSource = new FileInfo(png).Exists
                    ? ImageSource = new Uri(png, UriKind.RelativeOrAbsolute)
                    : null;
            }
            else
            {
                ImageSource = null;
            }
        }
    }
}
</pre>

<p>Y enlazamos las propiedades a la página.</p>

<h6>Weather.xaml</h6>

<pre class="brush: xml">
&lt;Page x:Class="WeatherApp.Components.Weather"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
      Title="Weather"&gt;

    &lt;Grid Background="Black"&gt;
        &lt;Grid.ColumnDefinitions&gt;
            &lt;ColumnDefinition/&gt;
            &lt;ColumnDefinition Width="Auto"/&gt;
        &lt;/Grid.ColumnDefinitions&gt;
        &lt;Image Grid.Column="0" Stretch="Uniform" VerticalAlignment="Center" Source="{Binding Path=ImageSource}"/&gt;
        &lt;StackPanel Grid.Column="1" VerticalAlignment="Center" Orientation="Vertical" Margin="0,0,30,0"&gt;
            &lt;TextBlock FontSize="36pt" FontWeight="Bold" Text="{Binding Path=CurrentWeather.temperature}" Foreground="White"/&gt;
            &lt;TextBlock FontSize="18pt" FontWeight="Bold" Text="{Binding Path=CurrentWeather.icon}" Foreground="White"/&gt;
        &lt;/StackPanel&gt;
        &lt;Border Grid.ColumnSpan="2" CornerRadius="5" VerticalAlignment="Top" 
                HorizontalAlignment="Right" Margin="0,5,30,0"&gt;
            &lt;StackPanel Orientation="Horizontal"&gt;
                &lt;RadioButton GroupName="Degree" Foreground="White"&gt;Fº&lt;/RadioButton&gt;
                &lt;RadioButton GroupName="Degree" Foreground="White" IsChecked="{Binding Path=IsFarenheit}"&gt;Cº&lt;/RadioButton&gt;
            &lt;/StackPanel&gt;
        &lt;/Border&gt;
    &lt;/Grid&gt;
&lt;/Page&gt;
</pre>

<p>Y seguimos con la ViewModel de MainWindow, aquí estableceremos
tres propiedades y dos métodos públicos. La primera propiedad
indica si está o no expandido el Expander, la segunda es de tipo
<em>ViewModel.Search</em> y una tercera que es un objeto Frame. Los
dos métodos son uno que inicia el componente de búsqueda, y el
segundo es un método que obtiene el clima de la ciudad
seleccionada.</p>

<h6>ViewModel.MainWindow.cs</h6>

<pre class="brush: c#">
using System.Windows.Controls;
using WeatherApp.Common;
using WeatherApp.Model;

namespace WeatherApp.ViewModel
{
    public class MainWindow : ObservableObject
    {
        private bool _isSearchExpanded;
        public bool IsSearchExpanded
        {
            get { return _isSearchExpanded; }
            set
            {
                _isSearchExpanded = value;
                OnPropertyChanged("IsSearchExpanded");
            }
        }

        public Search Search { get; set; }

        public Frame Browser { get; set; }

        public void Init()
        {
            Search = new Search { Mw = this };
        }

        public void GetWeather(Location location)
        {
            if (location != null)
            {
                var city = Client.GetWeather(location.Coordinates);
                var vm = new Weather { City = city, IsFarenheit = false };
                Browser.Navigate(new Components.Weather { DataContext = vm });
                IsSearchExpanded = false;
            }
        }
    }
}
</pre>

<h6>MainWindow.xaml</h6>

<pre class="brush: xml">
&lt;Window x:Class="WeatherApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:comp="clr-namespace:WeatherApp.Components"
        Title="MainWindow" Height="350" Width="525"&gt;
    &lt;Grid&gt;
        &lt;Frame x:Name="Browser"/&gt;
        &lt;Expander ExpandDirection="Left" IsExpanded="{Binding Path=IsSearchExpanded}" HorizontalAlignment="Right" Background="Gainsboro"&gt;
            &lt;Expander.Header&gt;
                &lt;TextBlock Text="Búsqueda"&gt;
                    &lt;TextBlock.LayoutTransform&gt;
                        &lt;TransformGroup&gt;
                            &lt;ScaleTransform/&gt;
                            &lt;SkewTransform/&gt;
                            &lt;RotateTransform Angle="90"/&gt;
                            &lt;TranslateTransform/&gt;
                        &lt;/TransformGroup&gt;
                    &lt;/TextBlock.LayoutTransform&gt;
                &lt;/TextBlock&gt;
            &lt;/Expander.Header&gt;
            &lt;comp:Search DataContext="{Binding Path=Search}"/&gt;
        &lt;/Expander&gt;
    &lt;/Grid&gt;
&lt;/Window&gt;
</pre>

<p>Y por último, para poder enlazar los distintos ViewModel a los
componentes, tenemos que enlazar el
<em>ViewModel.MainWindow.cs</em> al contexto de datos de
MainWindow, y para ello accedemos al evento <em>Initialize()</em>
de MainWindow, y establecemos el enlace al contexto.</p>

<h6>MainWindow.xaml.cs</h6>

<pre class="brush: c#">
using System.Windows;

namespace WeatherApp
{
    /// &lt;summary&gt;Interaction logic for MainWindow.xaml&lt;/summary&gt;
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var vm = new ViewModel.MainWindow {Browser = Browser};
            vm.Init();
            DataContext = vm;
        }
    }
}
</pre>

<p><strong>Nota</strong>: Aunque es una práctica habitual acceder
al code behind para incluir los DataContext, no es la más correcta,
que sería a través de una clase que gestionase los distintos
contextos, y que fuese inicializada en un sólo lugar de nuestra
aplicación.</p>

<p>En el siguiente post, el uso de converters, en el que
cambiaremos los grados que actualmente estamos mostrando como
Farenheit</p>
]]></description></item><item><title>Tutorial MVVM VI</title><link>http://setvalue.net/2013/4/4/tutorial-mvvm-vi/</link><pubDate>Thu, 04 Apr 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/4/4/tutorial-mvvm-vi/</guid><description><![CDATA[ 
<p>El ViewModel es la parte del patrón MVVM dónde vamos a
establecer las distintas propiedades públicas que se van a enlazar
a la Vista, así como los distintos comandos de la lógica de
presentación.<!--more--></p>

<p>Depende de quién sea el desarrollador, hay varias convenciones a
la hora de nombrar los ficheros del ViewModel. A mi personalmente
me gusta crear un <em>namespace</em> llamado ViewModel, pero a
otros les gusta llamar a las clases con el sufijo (o prefijo) VM o
ViewModel.</p>

<p><a
href="/media/4508/Windows-Live-Writer_Tutorial-MVVM-VI_11B79_ViewModel.png">
<img src="/media/4513/Windows-Live-Writer_Tutorial-MVVM-VI_11B79_ViewModel_thumb.png" width="199" height="209" alt="ViewModel" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p>Agregamos tres clases nuevas al proyecto, Search, Weather y
MainWindow, que van a ser las que utilizaremos para el
ViewModel.</p>

<h5>INotifyPropertyChanged</h5>

<p>Esta interfaz, es fundamental en el patrón MVVM, nos permite
actualizar los elementos de la vista que están enlazados a
propiedades en la vista-modelo, para ello basta con implementarla
en la ViewModel y al modificarse una propiedad lanzamos&nbsp; el
evento del cambio. Por ejemplo algo así:</p>

<pre class="brush: c#">
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace WeatherApp.ViewModel
{
    public class MainWindow : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }

        private String _sample;
        public String Sample
        {
            get { return _sample; }
            set
            {
                _sample = value;
                OnPropertyChanged("Sample");
            }
        }
    }
}
</pre>

<p>Al realizar cualquier cambio en la propiedad "Sample"
automáticamente se reproduce el cambio en la propiedad o
propiedades que la tengan enlazada en la Vista, bueno y siendo
rigurosos también dependiendo del modo en que esté enlazado, pero
en principio asumimos un modo de enlace "TwoWay".</p>

<h5>ICommand</h5>

<p>Esta interfaz, dispone de tres miembros (un método, un booleano
y un manejador de evento), y es el mecanismo a través del cual los
distintos controles del XAML van a realizar operaciones, la ventaja
fundamental de esta interfaz frente a los eventos, es que no deja
de ser una propiedad en la vista-modelo, y por tanto podremos
enlazarlos con la vista y reutilizarlos.</p>

<pre class="brush: c#">
using System;
using System.Windows.Input;

namespace WeatherApp.ViewModel
{
    public class Weather : ICommand
    {
        public void Execute(object parameter)
        {
            throw new NotImplementedException();
        }

        public bool CanExecute(object parameter)
        {
            throw new NotImplementedException();
        }

        public event EventHandler CanExecuteChanged;
    }
}
</pre>

<p>El método es la lógica de la acción, la función booleana es un
"flag" que permite ejecutar el comando, y bueno el manejador
controla la ejecución del comando.</p>

<h5>Implementando las interfaces</h5>

<p>Pero…, si tengo que implementar todo este código por cada acción
de la aplicación, o para establecer la notificación de las
propiedades de cada clase, ¿dónde está el beneficio?. Pues
evidentemente esto para una aplicación sencilla con pocos comandos
y con pocas vistas-modelos, es algo asequible, pero para
aplicaciones más complejas, conviene elaborar una clase que delegue
la implementación del "ICommand", y a la que podamos enviar la
ejecución de la acción y sus parámetros.</p>

<pre class="brush: c#">
using System;
using System.Diagnostics;
using System.Windows.Input;

namespace WeatherApp.Common
{
    public class RelayCommand : ICommand
    {
        private readonly Action&lt;object&gt; _execute;
        private readonly Predicate&lt;object&gt; _canExecute;

        public RelayCommand(Action&lt;object&gt; execute) : this(execute, null)
        {
        }

        public RelayCommand(Action&lt;object&gt; execute, Predicate&lt;object&gt; canExecute)
        {
            if (execute == null)
            {
                throw new ArgumentNullException("execute");
            }
            _execute = execute;
            _canExecute = canExecute;
        }

        [DebuggerStepThrough]
        public bool CanExecute(object parameters)
        {
            return _canExecute == null || _canExecute(parameters);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameters)
        {
            _execute(parameters);
        }
    }
}
</pre>

<p>Para la notificación de propiedades, generaríamos una clase
abstracta, que implementase en un método público,
"INotifyPropertyChanged", y después cada clase del ViewModel
implementaría esta clase abstracta, llamando únicamente al método
que lanza la implementación.</p>

<pre class="brush: c#">
using System.ComponentModel;

namespace WeatherApp.Common
{
    public abstract class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
    }
}
</pre>

<p>Después de implementar todo esto, en el siguiente post, toca
empezar a agregar las propiedades al ViewModel, establecer la
notificación de los cambios para que cuando se produzcan se
actualicen inmediatamente en la vista, y enlazarlas al marcado XAML
mediante el contexto de datos y los Binding.</p>
]]></description></item><item><title>Tutorial MVVM V</title><link>http://setvalue.net/2013/4/3/tutorial-mvvm-v/</link><pubDate>Wed, 03 Apr 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/4/3/tutorial-mvvm-v/</guid><description><![CDATA[ 
<p>En este quinto artículo, después de haber montado el Modelo en
los anteriores post, y unas pruebas unitarias asociadas al Modelo,
vamos a crear la vista, la cual va a estar formada a partir de 3
componentes, aunque todo podría estar en el mismo componente, es
más práctico y funcional que esté separado en distintos controles,
ya que así nos permite la reutilización de elementos en otros, y es
que una de las ventajas más importantes de WPF, es la potencia del
marcado XAML, que nos deja introducir controles que contienen a
otros que a su vez pueden contener a otros distintos. 
<!--more--></p>

<p>En la solución, tenemos un proyecto de WPF, si no es así lo
creamos, y lo establecemos como proyecto principal de la
compilación. Por defecto, vendrá un control
<em>MainWindow.xaml</em> que será la ventana principal de la
aplicación. Agregamos un nuevo control de usuario, y en el marcado
de este control, al objeto Grid, le agregamos dos filas, en la
primera insertamos un StackPanel con orientación vertical, que
contenga un TextBox con un ancho mínimo de 200px y un Button
alineado a la derecha. En la segunda fila agregamos un objeto
ListBox el cual albergará los resultados de la búsqueda que
hagamos, que serán las localizaciones que coincidan con el texto
del TextBox. Y nos queda algo así:</p>

<pre class="brush: xml">
&lt;UserControl x:Class="WeatherApp.Components.Search"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"&gt;
    &lt;Grid&gt;
        &lt;Grid.RowDefinitions&gt;
            &lt;RowDefinition Height="Auto"/&gt;
            &lt;RowDefinition /&gt;
        &lt;/Grid.RowDefinitions&gt;
        &lt;StackPanel Grid.Row="0" Orientation="Vertical" Margin="5"&gt;
            &lt;TextBox MinWidth="200"/&gt;
            &lt;Button Content="Buscar" HorizontalAlignment="Right"/&gt;
        &lt;/StackPanel&gt;
        &lt;ListBox Grid.Row="1" Margin="5"&gt;&lt;/ListBox&gt;
    &lt;/Grid&gt;
&lt;/UserControl&gt;
</pre>

<p>A continuación agregamos un control tipo Page, este objeto se
puede utilizar para integrarlo en un Frame, el cual trae de serie
la gestión de una pila de páginas navegadas, y los controles de
navegación para poder ir atrás o hacía adelante. La página estará
compuesta por un Grid de dos columnas, la primera que se autoajuste
y la segunda que se ajuste el ancho al contenido, en la primera
agregamos un objeto Image, que de momento no configuramos su
Source, y en la segunda agregamos un StackPanel en el que los
objetos se orienten verticalmente, y que su alineación vertical sea
también en el centro, este panel contiene a su vez dos TextBlock
que albergarán la temperatura y el nombre de la ciudad. Por último
agregamos un objeto Border con un <em>ColSpan</em> y orientado
arriba a la derecha, con dos RadioButton los cuales permitirán el
cambio entre grados Farenheit y Celsius.</p>

<pre class="brush: xml">
&lt;Page x:Class="WeatherApp.Components.Weather"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
    Title="Weather"&gt;

    &lt;Grid&gt;
        &lt;Grid.ColumnDefinitions&gt;
            &lt;ColumnDefinition/&gt;
            &lt;ColumnDefinition Width="Auto"/&gt;
        &lt;/Grid.ColumnDefinitions&gt;
        &lt;Image Grid.Column="0" Stretch="Uniform" VerticalAlignment="Center"/&gt;
        &lt;StackPanel VerticalAlignment="Center" Orientation="Vertical"&gt;
            &lt;TextBlock FontSize="36" FontWeight="Bold"/&gt;
            &lt;TextBlock FontSize="18" FontWeight="Bold"/&gt;
        &lt;/StackPanel&gt;
        &lt;Border Grid.ColumnSpan="2" CornerRadius="5" VerticalAlignment="Top" 
                HorizontalAlignment="Right" Margin="3"&gt;
            &lt;StackPanel Orientation="Horizontal"&gt;
                &lt;RadioButton GroupName="Degree"&gt;Fº&lt;/RadioButton&gt;
                &lt;RadioButton GroupName="Degree"&gt;Cº&lt;/RadioButton&gt;
            &lt;/StackPanel&gt;
        &lt;/Border&gt;
    &lt;/Grid&gt;
&lt;/Page&gt;
</pre>

<p>Seguido, introducimos en el MainWindow un objeto Frame, y a
continuación un objeto Expander, que se expande hacía a la
izquierda, y lo ubicamos alineado a la derecha, en el Header
introducimos un TextBlock con el texto "Búsqueda" y rotado 90
grados, y en el Content el componente de búsqueda que hemos creado
antes.</p>

<pre class="brush: xml">
&lt;Window x:Class="WeatherApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:comp="clr-namespace:WeatherApp.Components"
        Title="MainWindow" Height="350" Width="525"&gt;
    &lt;Grid&gt;
        &lt;Frame /&gt;
        &lt;Expander ExpandDirection="Left" HorizontalAlignment="Right"&gt;
            &lt;Expander.Header&gt;
                &lt;TextBlock Text="Búsqueda" &gt;
                    &lt;TextBlock.LayoutTransform&gt;
                        &lt;TransformGroup&gt;
                            &lt;ScaleTransform/&gt;
                            &lt;SkewTransform/&gt;
                            &lt;RotateTransform Angle="90"/&gt;
                            &lt;TranslateTransform/&gt;
                        &lt;/TransformGroup&gt;
                    &lt;/TextBlock.LayoutTransform&gt;
                &lt;/TextBlock&gt;
            &lt;/Expander.Header&gt;
            &lt;comp:Search /&gt;
        &lt;/Expander&gt;
    &lt;/Grid&gt;
&lt;/Window&gt;
</pre>

<p>En el siguiente post, continuaremos elaborando el ViewModel de
cada uno de estos objetos, y los relacionaremos para después
continuar haciendo los Binding a las propiedades.</p>
]]></description></item><item><title>Tutorial MVVM IV</title><link>http://setvalue.net/2013/4/2/tutorial-mvvm-iv/</link><pubDate>Tue, 02 Apr 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/4/2/tutorial-mvvm-iv/</guid><description><![CDATA[ 
<p>Creamos un proyecto de Test unitarios, para que verifique el
comportamiento de los métodos que hemos incluido en el modelo. 
<!--more--></p>

<p>Por organizar un poco el proyecto de test, creamos una carpeta
llamada <em>Model</em>, a la que le introducimos una clase llamada
<em>ClientTest.cs</em>. Esta clase, verifica lo métodos de la clase
<em>Client</em>, los parámetros que recibe y los resultados que
arroja:</p>

<ul>
<li><em>GetLocations(String query, int maxResults)</em>. Este
método debe devolver un listado de la clase <em>Location</em>, el
cual no puede ser nulo y los parámetros de entrada no pueden ser
nulos, y en el caso de <em>maxResults</em>, debe ser un entero en
un intervalo entre 0 y 20.</li>

<li><em>GetWeather(Coordinates coords)</em>. Debe devolver un
objeto <em>Weather</em>, el cuál no puede ser nulo, y el parámetro
de entrada nunca debe llegar nulo.</li>
</ul>

<p>Para ello debemos referenciar, el proyecto del Modelo en el
proyecto de Test, y luego en la clase debemos agregar un <span
class="brush: c#">Using</span> referenciando al modelo dentro de la
clase.</p>

<pre class="brush: c#">
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WeatherApp.Model;

namespace WeatherApp.Test.Model
{
    [TestClass]
    public class ClientTest
    {
        [TestMethod]
        public void GetLocations()
        {
            var query = String.Empty;
            var maxResults = -1;
            var list = Client.GetLocations(query, maxResults);
            Assert.AreEqual(list, null);
            query = "Madrid";
            maxResults = new Random().Next(0, 20);
            list = Client.GetLocations(query, maxResults);
            Assert.IsTrue(list != null &amp;&amp; list.Count &gt;= 0);
        }

        [TestMethod]
        public void GetWeather()
        {
            var coords = new Coordinates
                             {
                                 Latitude = 0D,
                                 Longitude = 0D
                             };
            var weather = Client.GetWeather(null);
            Assert.IsNull(weather);
            weather = Client.GetWeather(coords);
            Assert.IsNotNull(weather);
        }
    }
}
</pre>

<p>Una vez elaborado el test, programamos la clase <em>Client</em>
del Modelo, y nos queda algo así:</p>

<pre class="brush: c#">
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Xml.Linq;

namespace WeatherApp.Model
{
    public static class Client
    {
        private static readonly CultureInfo Culture = new CultureInfo("EN-US");

        public static List&lt;Location&gt; GetLocations(String query, int maxResults = 20)
        {
            if (String.IsNullOrEmpty(query) ||
                !(maxResults &gt; 0 &amp;&amp; maxResults &lt;= 20)) return null;

            var url = String.Format(Properties.Resources.UrlBing, query,
                                    maxResults, Properties.Resources.KeyBing);

            List&lt;Location&gt; list;
            try
            {
                var doc = XDocument.Load(url);
                var locs = from q in doc.Descendants().Where(q =&gt; q.Name.LocalName == "Location")
                           select new
                                      {
                                          Name = q.Descendants()
                                                 .Where(r =&gt; r.Name.LocalName == "Address")
                                                 .Elements().First(s =&gt; s.Name.LocalName == "Locality").Value,
                                          Country = q.Descendants()
                                                    .Where(r =&gt; r.Name.LocalName == "Address")
                                                    .Elements().First(s =&gt; s.Name.LocalName == "CountryRegion").Value,
                                          Latitude = q.Descendants()
                                                     .Where(r =&gt; r.Name.LocalName == "Point")
                                                     .Elements().First(s =&gt; s.Name.LocalName == "Latitude").Value,
                                          Longitude = q.Descendants()
                                                      .Where(r =&gt; r.Name.LocalName == "Point")
                                                      .Elements().First(s =&gt; s.Name.LocalName == "Longitude").Value,

                                      };

                list = locs.Select(q =&gt; new Location
                                            {
                                                Name = q.Name,
                                                Country = q.Country,
                                                Coordinates = new Coordinates
                                                                  {
                                                                      Latitude = Convert.ToDouble(q.Latitude, Culture),
                                                                      Longitude = Convert.ToDouble(q.Longitude, Culture)
                                                                  }
                                            }).ToList();
            }
            catch (Exception)
            {
                list = null;
            }
            return list;
        }

        public static Weather GetWeather(Coordinates coords)
        {
            if (coords == null) return null;

            var url = String.Format(Properties.Resources.UrlAPI, Properties.Resources.KeyAPI,
                                    coords.Latitude.ToString(Culture), coords.Longitude.ToString(Culture));
            Weather weather = null;
            try
            {
                var dser = new DataContractJsonSerializer(typeof(Weather));
                var req = WebRequest.Create(url);

                using (var s = req.GetResponse().GetResponseStream())
                {
                    if (s != null)
                    {
                        weather = (Weather)dser.ReadObject(s);
                    }
                }
            }
            catch (Exception)
            {
                weather = null;
            }
            return weather;
        }
    }
}
</pre>

<p>En el siguiente capítulo empezaremos a trabajar con la vista, a
agregar controles al proyecto de WPF.</p>
]]></description></item><item><title>Tutorial MVVM III</title><link>http://setvalue.net/2013/4/1/tutorial-mvvm-iii/</link><pubDate>Mon, 01 Apr 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/4/1/tutorial-mvvm-iii/</guid><description><![CDATA[ 
<p>En este tercer capítulo, continuamos con el modelo, dónde vamos
a agregar una clase estática, que se ocupe de hacer las peticiones
a los servicios web, y transformen las respuestas en listas de
localizaciones y datos meteorológicos.<!--more--></p>

<p>Vamos a hacer peticiones a dos servicios web, el primero es para
obtener coordenadas y localizaciones, a partir de una cadena de
texto que se introduzca en la aplicación. El segundo devuelve un
bloque de datos a partir de las coordenadas que le pasemos de la
localización seleccionada de las mostradas en el servicio
anterior.</p>

<p>Para hacer todo esto, utilizaremos los servicios gratuitos de <a
href="http://www.bingmapsportal.com/" target="_blank">Bing Maps</a>
para la localización, y de <a href="http://forecast.io"
target="_blank">Dark Sky Company</a>. La razón de seleccionar ambos
es que el primero, me parece bastante sencillo y en este <a
href="/2012/8/14/howto-hacer-mapping-y-no-morir-en-el-intento-47/"
target="_blank">post</a> puedes ver como obtener la key para poder
utilizar el servicio. Y el segundo web service, es porque me parece
bastante curioso, puedes hacer peticiones del tiempo por minuto u
hora, en un momento determinado en el intervalo de 60 años atrás y
hasta 10 años hacía adelante, y aunque tiene algunas limitaciones,
creo que puede ser interesante probarlo.</p>

<h5>Web Services</h5>

<p>Bing Maps<br />
 http://dev.virtualearth.net/REST/v1/Locations?query=query&amp;maxResults=maxResults&amp;key=APIKEY&amp;output=xml<br />
 Parámetros:</p>

<ul>
<li><strong>query</strong>: Palabra objeto para la consulta</li>

<li><strong>maxResults</strong>: Número de resultados que queremos
establecer como máximo. Tiene un límite de 20 por consulta.</li>

<li><strong>APIKEY</strong>: Es la clave del API que hayamos
obtenido de Bing</li>

<li><strong>output</strong>: Lo he establecido directamente en XML,
pero si se elimina el parámetro automáticamente devuelve un
JSON.</li>
</ul>

<p>Forecast<br />
 http://api.forecast.io/forecast/APIKEY/Latitude,Longitude<br />
 Parámetros:</p>

<ul>
<li><strong>APIKEY</strong>: Es la clave del API que hayamos
obtenido de Forecast</li>

<li><strong>Latitude</strong>: Latitud en grados decimales.</li>

<li><strong>Longitude</strong>: Longitud en grados decimales.</li>
</ul>

<p>Antes de generar la clase estática, vamos a almacenar estas urls
parametrizadas en el fichero de recursos de proyecto.</p>

<p><a
href="/media/4344/Windows-Live-Writer_Tutorial-MVVM-III_F6F2_Resources_4.png">
<img src="/media/4349/Windows-Live-Writer_Tutorial-MVVM-III_F6F2_Resources_thumb_1.png" width="504" height="270" alt="Resources" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p>Creamos la clase estática, con estos miembros, GetLocations() y
GetWeather(), los cuales nos van a proveer los datos para la
vista.</p>

<h6>Client.cs</h6>

<pre class="brush: c#">
using System;
using System.Collections.Generic;
using System.Globalization;

namespace WeatherApp.Model
{
    public static class Client
    {
        private static readonly CultureInfo Culture = new CultureInfo("EN-US");

        public static List&lt;Location&gt; GetLocations(String query, int maxResults = 20)
        {
            return null;
        }

        public static Weather GetWeather(Coordinates coords)
        {
            return null;
        }
    }
}
</pre>

<p>En el siguiente post vamos a establecer unas pruebas unitarias
para desarrollar estas funciones.</p>
]]></description></item><item><title>Tutorial MVVM II</title><link>http://setvalue.net/2013/3/31/tutorial-mvvm-ii/</link><pubDate>Sun, 31 Mar 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/3/31/tutorial-mvvm-ii/</guid><description><![CDATA[ 
<p>Después de ver un resumen de la teoría del patrón en el anterior
post, vamos a comenzar a programar, para ello, lo más conveniente
es preparar el modelo. El objetivo de este tutorial, es mostrar el
patrón MVVM a través de una aplicación WPF que consuma unos datos
de un servicio web. En definitiva una aplicación que muestre el
tiempo de una ciudad.<!--more--></p>

<p>Empezamos creando un nuevo proyecto de WPF, y a la solución le
agregamos un nuevo proyecto de librería dinámica, en la cual vamos
a trabajar el modelo.</p>

<h5>Creación del modelo</h5>

<p>Una vez agregado el proyecto, introducimos cinco clases:
Location y Coordinates, y DataBlock, DataPoint y Weather.</p>

<h6>Coordinates.cs</h6>

<pre class="brush: c#">
namespace WeatherApp.Model
{
    public class Coordinates
    {
        public double Latitude { get; set; }

        public double Longitude { get; set; }
    }
}
</pre>

<h6>Location.cs</h6>

<pre class="brush: c#">
using System;
namespace WeatherApp.Model
{
    public class Location
    {
        public String Name { get; set; }

        public String Country { get; set; }

        public Coordinates Coordinates { get; set; }
    }
}
</pre>

<h6>DataPoint.cs</h6>

<pre class="brush: c#">
using System;
namespace WeatherApp.Model
{
    public class DataPoint
    {
        public int time { get; set; }
        public String summary { get; set; }
        public String icon { get; set; }
        public int? sunriseTime { get; set; }
        public int? punsetTime { get; set; }
        public double precipIntensity { get; set; }
        public double? precipProbability { get; set; }
        public String precipType { get; set; }
        public String precipAccumulation { get; set; }
        public double? temperature { get; set; }
        public double? temperatureMin { get; set; }
        public int? temperatureMinTime { get; set; }
        public double? temperatureMax { get; set; }
        public int? temperatureMaxTime { get; set; }
        public double windSpeed { get; set; }
        public int windBearing { get; set; }
        public double cloudCover { get; set; }
        public double humidity { get; set; }
        public double pressure { get; set; }
        public double visibility { get; set; }
    }
}
</pre>

<h6>DataBlock.cs</h6>

<pre class="brush: c#">
using System;

namespace WeatherApp.Model
{
    public class DataBlock
    {
        public String summary { get; set; }

        public String icon { get; set; }
        
        public DataPoint[] data { get; set; }
    }
}
</pre>

<h6>Weather.cs</h6>

<pre class="brush: c#">
using System;

namespace WeatherApp.Model
{
    public class Weather
    {
        public double latitude { get; set; }
        public double longitude { get; set; }
        public String timeZone { get; set; }
        public int offset { get; set; }
        public DataPoint currently { get; set; }
        public DataBlock minutely { get; set; }
        public DataBlock hourly { get; set; }
        public DataBlock dayly { get; set; }
        public object[] alerts { get; set; }
        public object flags { get; set; }
    }
}
</pre>

<p>En el siguiente post continuaremos con el modelo también,
generando más lógica de negocio para obtener los datos.</p>
]]></description></item><item><title>Tutorial MVVM I</title><link>http://setvalue.net/2013/3/30/tutorial-mvvm-i/</link><pubDate>Sat, 30 Mar 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/3/30/tutorial-mvvm-i/</guid><description><![CDATA[ 
<p>En este serial, intentaré explicar de una forma somera y
modesta, cómo desarrollar una aplicación WPF con el patrón MVVM. 
<!--more--></p>

<p>Las siglas MVVM, corresponden a Model View ViewModel o dicho en
castellano Modelo Vista Vista-Modelo. Este patrón es una evolución
del patrón MVC, cuyo objetivo es separar la Vista, de la lógica de
presentación (el ViewModel), y ésta última procesa los datos
recibidos desde el Modelo. Pero…, ¿y qué diferencia hay con lo que
hasta ahora se hacía en un desarrollo con un patrón
<em>n-layer</em>?, pues fundamentalmente que la capa de
presentación se aísla más de la lógica, dividiéndose en más
componentes, lo cual permite el trabajo al mismo tiempo de más
miembros del equipo de desarrollo (o incluso del equipo de diseño).
Y se intenta evitar de forma general, el uso de los ficheros de
<em>code-behind</em> asociados a los distintos controles,
utilizando el enlace a datos de WPF para adjuntar comandos a la
vista.</p>

<h5>View</h5>

<p><strong>Lógica UI</strong>. La vista son los componentes
definidos en los ficheros con el marcado XAML. El contexto de datos
de los componentes se les asocia su ViewModel. Y se intenta reducir
a la mínima expresión el uso del <em>code-behind</em>.</p>

<h5>Model</h5>

<p><strong>Lógica de negocio.</strong> No tiene ninguna relación
directa con la Vista, se ocupa de las reglas de negocio, los datos
y de la persistencia en el almacén de datos.</p>

<h5>ViewModel</h5>

<p><strong>Lógica de presentación</strong>. Es el adaptador entre
la Vista y el Modelo. Maneja y valida los distintos eventos de la
Vista y del Modelo. Implementa interfaces como
INotifyPropertyChanged e ICommand.</p>

<p><a
href="/media/4318/Windows-Live-Writer_f405fbe1d104_14BDA_Patr%C3%B3n_4.png">
<img src="/media/4318/Windows-Live-Writer_f405fbe1d104_14BDA_Patr%C3%B3n_4.png" width="404" height="200" alt="Patrón" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>
]]></description></item><item><title>Vista diseño WPF en Blend for Visual Studio 2012</title><link>http://setvalue.net/2013/3/28/vista-diseño-wpf-en-blend-for-visual-studio-2012/</link><pubDate>Thu, 28 Mar 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/3/28/vista-diseño-wpf-en-blend-for-visual-studio-2012/</guid><description><![CDATA[ 
<p>Estaba intentando hacer un post de WPF con Visual Studio 2012, y
al intentar utilizar Blend para Visual Studio 2012, la vista diseño
de los componentes no está disponible.<!--more--></p>

<p><a
href="/media/4128/Windows-Live-Writer_Desarrollo-WPF-con-Visual-Studio-2012_9165_Blend_2.png">
<img src="/media/4133/Windows-Live-Writer_Desarrollo-WPF-con-Visual-Studio-2012_9165_Blend_thumb.png" width="504" height="271" alt="Blend" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p>Buscando por internet, veo que el Blend for Visual Studio 2012
sólo tiene disponible el diseñador para aplicaciones Windows Store
y Windows Phone, por lo que no se puede visualizar el layout de
componentes de las aplicaciones para Silverlight o WPF, únicamente
se puede ver el marcado XAML. Sólo estará disponible a partir del
Update 2, y una de las soluciones que se plantean es instalar la
Preview de Blend + Sketchflow.</p>

<p>Para ello vamos al centro de descargas de Microsoft y
descargamos la versión de la <a
href="http://www.microsoft.com/en-us/download/details.aspx?id=30702">
Preview</a>, para poder instalarlo, se requiere según el Sistema
Operativo:</p>

<h4>Windows 8</h4>

<ul>
<li>Visual Studio 2012 Professional o superior</li>

<li>Visual Studio 2012 Express</li>
</ul>

<h4>Windows 7 o Windows Server 2012</h4>

<ul>
<li>Visual Studio 2012 Professional o superior</li>
</ul>

<p>Una vez descargado, ejecutamos la instalación, aceptamos la
licencia.</p>

<p><a
href="/media/4138/Windows-Live-Writer_Desarrollo-WPF-con-Visual-Studio-2012_9165_license_2.png">
<img src="/media/4143/Windows-Live-Writer_Desarrollo-WPF-con-Visual-Studio-2012_9165_license_thumb.png" width="504" height="362" alt="license" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p>Seleccionamos las opciones, por defecto todo.</p>

<p><a
href="/media/4148/Windows-Live-Writer_Desarrollo-WPF-con-Visual-Studio-2012_9165_Options.png">
<img src="/media/4153/Windows-Live-Writer_Desarrollo-WPF-con-Visual-Studio-2012_9165_Options_thumb.png" width="504" height="362" alt="Options" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p>Esperamos más o menos 3 minutos en hacerse la instalación.</p>

<p><a
href="/media/4158/Windows-Live-Writer_Desarrollo-WPF-con-Visual-Studio-2012_9165_Install.png">
<img src="/media/4163/Windows-Live-Writer_Desarrollo-WPF-con-Visual-Studio-2012_9165_Install_thumb.png" width="504" height="362" alt="Install" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p>Una vez completada la instalación tenemos disponible el
diseñador en el Blend + SketchFlow Preview for Visual Studio
2012.</p>

<p><a
href="/media/4168/Windows-Live-Writer_Desarrollo-WPF-con-Visual-Studio-2012_9165_Thanks.png">
<img src="/media/4173/Windows-Live-Writer_Desarrollo-WPF-con-Visual-Studio-2012_9165_Thanks_thumb.png" width="504" height="362" alt="Thanks" border="0" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;"/></a></p>

<p>Aunque esta no es la mejor solución, es la única mientras no
salga el Update 2 de Visual Studio 2012, que ya soportará de nuevo
el diseñador para WPF.</p>
]]></description></item><item><title>Convertir DataGrid.View a DataTable</title><link>http://setvalue.net/2013/2/4/convertir-datagridview-a-datatable/</link><pubDate>Mon, 04 Feb 2013 00:00:00 GMT</pubDate><guid>http://setvalue.net/2013/2/4/convertir-datagridview-a-datatable/</guid><description><![CDATA[ 
<p>Un problema recurrente al trabajar con el DataGrid de WPF es
recuperar los datos para trabajar con ellos, bien para realizar una
nueva consulta, bien para exportarlos a Excel, o incluso para
exportarlos a otro almacén de persistencia distinto al original. 
<!--more--></p>

<p>El problema que hay es que una vez son cargados en la vista, el
origen de datos, salvo que sea una colección que se alimenta de una
lista de entidades conocida, a la que se pueda llamar para realizar
un casting, y poder volver a trabajar con ella, la colección se
convierte en anónima, y más si la colección del DataGrid ha sido
manipulada.<!--Fin--></p>

<p>Bien, la idea de este post es trabajar con ese objeto anónimo,
que contiene el resultado de una consulta, y que no podemos volver
a convertir mediante un casting porque desconocemos su origen.</p>

<p>Para ello, lo primero es crear un nuevo proyecto de WPF al que
le vamos a agregar un DataGrid, el cual tenga un DataContext con un
CollectionViewSource que provea la colección de datos.</p>

<pre class="brush: xml">
&lt;Window x:Class="DataGridToDataTable.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Loaded="WindowLoaded" WindowStartupLocation="CenterScreen"&gt;
    &lt;Window.Resources&gt;
        &lt;CollectionViewSource x:Key="CvPeople"/&gt;
    &lt;/Window.Resources&gt;
    &lt;Grid&gt;
        &lt;Grid.RowDefinitions&gt;
            &lt;RowDefinition Height="Auto"/&gt;
            &lt;RowDefinition/&gt;
        &lt;/Grid.RowDefinitions&gt;
        &lt;DataGrid Grid.Row="1" DataContext="{StaticResource CvPeople}" ItemsSource="{Binding}" AutoGenerateColumns="True" /&gt;
        &lt;Button x:Name="BtnGetDataTable" Content="Obtener DataTable" HorizontalAlignment="Left"
                ToolTip="Obtiene el DataTable desde la fuente de datos actual" Click="BtnGetDataTableClick"/&gt;
        &lt;Popup x:Name="PopResults" Grid.RowSpan="2" StaysOpen="False" IsOpen="False" Placement="Center" Width="500" Height="300"&gt;
            &lt;TextBox x:Name="TxtResults" TextWrapping="Wrap"  /&gt;
        &lt;/Popup&gt;
    &lt;/Grid&gt;
&lt;/Window&gt;
</pre>

<p>Además se ha definido un PopUp, un botón y un TextBox con el fin
de desencadenar el evento de transformación de la colección en un
DataTable, y ver su resultado.</p>

<p>En una clase estática nueva, generamos dos métodos. El primero
devuelve una lista de personas, la cual va a alimentar el
CollectionViewSource que hemos creado en el XAML.</p>

<p>El segundo método es el encargado de realizar la conversión de
View a DataTable, para ello es preciso agregar la directiva
System.Reflection.</p>

<pre class="brush: c#">
using System.Reflection;
</pre>

<p>Dentro del método creamos un nuevo DataTable que vamos a definir
recorriendo las propiedades de la Collection (que no es más que el
miembro View del DataGrid) y en un segundo bucle cargamos los
valores de las distintas propiedades fila a fila en el nuevo
DataTable.</p>

<pre class="brush: c#">
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Reflection;

namespace DataGridToDataTable
{
    public static class Data
    {
        public static List&lt;Person&gt; GetPeople()
        {
            return new List&lt;Person&gt;
                           {
                               new Person() {Name = "Raúl", Age = 29, Job = "Analyst"},
                               new Person() {Name = "Juan", Age = 34, Job = "Tester"},
                               new Person() {Name = "Paco", Age = 31, Job = "Developer"}
                           };
        }

        public static DataTable GetTable(IEnumerable collection)
        {
            var dt = new DataTable();
            try
            {
                foreach (var q in collection)
                {
                    Type t = q.GetType();
                    PropertyInfo[] pi = t.GetProperties();
                    if (dt.Columns.Count == 0)
                    {
                        foreach (var p in pi)
                        {
                            var dc = new DataColumn(p.Name, p.PropertyType);
                            dt.Columns.Add(dc);
                        }
                    }
                    var dr = dt.NewRow();
                    for (int x = 0; x &lt; pi.Length; x++)
                    {
                        var value = q.GetType().GetProperty(pi[x].Name).GetValue(q, null);
                        dr[x] = value ?? String.Empty;
                    }
                    dt.Rows.Add(dr);
                    dt.AcceptChanges();
                }
            }
            catch (Exception)
            {
                dt = null;
            }
            return dt;
        }
    }

    public class Person
    {
        public String Name { get; set; }
        public int Age { get; set; }
        public String Job { get; set; }
    }

}
</pre>

<p>Por simplificar el ejemplo, la ejecución de los eventos lo vamos
a hacer desde el Code Behind del MainWindow, pero lo suyo sería
hacer esto siguiendo el patrón MVVM y realizarlo en la Vista
Modelo.</p>

<p>En el evento Loaded, buscamos el recurso que hemos creado como
CollectionViewSource y lo rellenamos con el método GetPeople() de
la clase estática creada para tener datos de prueba.</p>

<p>En el evento Click del botón creamos un DataSet al que agregamos
el resultado de la conversión del View, aceptamos los cambios y
recogemos el XML del DataSet generado dentro del TextBox.</p>

<pre class="brush: c#">
using System.Data;
using System.Windows;
using System.Windows.Data;

namespace DataGridToDataTable
{
    /// &lt;summary&gt;Lógica de interacción para MainWindow.xaml&lt;/summary&gt;
    public partial class MainWindow : Window
    {

        public CollectionViewSource Cv { get; set; }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            Cv = (CollectionViewSource)TryFindResource("CvPeople");
            if (Cv != null) Cv.Source = Data.GetPeople();
        }

        private void BtnGetDataTableClick(object sender, RoutedEventArgs e)
        {
            var ds = new DataSet("PeopleDs");
            if (Cv != null)
            {
                var dt = Data.GetTable(Cv.View);
                dt.TableName = "Person";
                dt.AcceptChanges();
                ds.Tables.Add(dt);
            }
            ds.AcceptChanges();
            this.TxtResults.Text = ds.GetXml();
            this.PopResults.IsOpen = true;
        }
    }
}
</pre>
]]></description></item><item><title>Cambiar de tema dinámicamente</title><link>http://setvalue.net/2012/10/15/cambiar-de-tema-dinámicamente/</link><pubDate>Mon, 15 Oct 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/10/15/cambiar-de-tema-dinámicamente/</guid><description><![CDATA[ 
<p>Al elaborar un desarrollo para un cliente, cuando el que dirige
el proyecto no es uno, sino varios individuos, te sueles encontrar
con problemas con el diseño, más allá del funciona o no funciona,
lo más importante es que esto es azul y no verde, o este tono no me
gusta, y problemas de esa índole.<!--more--></p>

<p>Un tiempo atrás, me encontré con un desarrollo, que cuando los
clientes vieron la primera beta, la bautizaron como Matrix, la beta
fallaba que daba gusto, hubo un cambio de última hora que arrastró
varios fallos, pero lo más importante fue que la aplicación fue
llamada Matrix, porque era negra con algunos detalles verdes.</p>

<p>Para evitar esto, lo más sencillo, es ofrecer un cambio dinámico
en el conjunto de diseños de la aplicación, ojo, sin modificar la
estructura, simplemente, haciendo participe al usuario final, del
conjunto de colores, o incluso estilos que quiere darle a la
aplicación.</p>

<p>Para evitar esto, surgieron varias propuestas de diseño, pero
viendo que los diseñadores no terminaban de acertar con el gusto
del cliente (de todos los individuos que estaban del lado del
cliente) decidimos implementar un cambio dinámico, e incluso, un
cambio en ciertos elementos como el tamaño de la fuente, la fuente,
o detalles de color.</p>

<p>Creamos un proyecto nuevo de WPF, y le agregamos una carpeta
"Theme" por ejemplo, y a ésta le vamos a agregar un diccionario de
recursos.</p>

<p><img src="/media/2257/Windows-Live-Writer_Cambiar-de-tema-dinmicamente_106EF_image_3.png" width="544" height="333" alt="Agregar Diccinario" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border-width: 0px;"/></p>

<p>Una vez agregado, lo referenciamos en el App.xaml, para que los
estilos del diccionario estén accesibles de forma dinámica.</p>

<pre class="brush: xml">
    &lt;Application.Resources&gt;
        &lt;ResourceDictionary&gt;
            &lt;ResourceDictionary.MergedDictionaries&gt;
                &lt;ResourceDictionary Source="Theme/Principal.xaml"/&gt;
            &lt;/ResourceDictionary.MergedDictionaries&gt;
        &lt;/ResourceDictionary&gt;
    &lt;/Application.Resources&gt;
</pre>

<p>Después agregamos un estilo para hacer un ejemplo que modifica
los TextBlock, ya que es para lo que vamos a preparar un estilo.
Una vez realizado este estilo, duplicaremos el diccionario de
recursos y modificaremos los estilos para diferenciar los TextBlock
entre diccionarios.</p>

<pre class="brush: xml">
&lt;!--Diccionario Principal--&gt;
&lt;ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
    
    &lt;Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}"&gt;
        &lt;Setter Property="OverridesDefaultStyle" Value="True"/&gt;
        &lt;Setter Property="FontWeight" Value="Bold"/&gt;
        &lt;Setter Property="FontSize" Value="20pt"/&gt;
        &lt;Setter Property="FontStyle" Value="Normal"/&gt;
        &lt;Setter Property="Foreground" Value="Blue"/&gt;
        &lt;Setter Property="VerticalAlignment" Value="Center"/&gt;
        &lt;Setter Property="HorizontalAlignment" Value="Center"/&gt;
    &lt;/Style&gt;
&lt;/ResourceDictionary&gt;
&lt;!--Diccionario Alternativo 1--&gt;
&lt;ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
    
    &lt;Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}"&gt;
        &lt;Setter Property="OverridesDefaultStyle" Value="True"/&gt;
        &lt;Setter Property="FontWeight" Value="Bold"/&gt;
        &lt;Setter Property="FontSize" Value="16pt"/&gt;
        &lt;Setter Property="FontStyle" Value="Italic"/&gt;
        &lt;Setter Property="Foreground" Value="Green"/&gt;
        &lt;Setter Property="VerticalAlignment" Value="Center"/&gt;
        &lt;Setter Property="HorizontalAlignment" Value="Center"/&gt;
    &lt;/Style&gt;
&lt;/ResourceDictionary&gt;
&lt;!--Diccionario Alternativo 2--&gt;
&lt;ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
    
    &lt;Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}"&gt;
        &lt;Setter Property="OverridesDefaultStyle" Value="True"/&gt;
        &lt;Setter Property="FontWeight" Value="Normal"/&gt;
        &lt;Setter Property="FontSize" Value="18pt"/&gt;
        &lt;Setter Property="FontStyle" Value="Oblique"/&gt;
        &lt;Setter Property="Foreground" Value="Red"/&gt;
        &lt;Setter Property="VerticalAlignment" Value="Center"/&gt;
        &lt;Setter Property="HorizontalAlignment" Value="Center"/&gt;
    &lt;/Style&gt;
&lt;/ResourceDictionary&gt;
</pre>

<p>A continuación, introducimos en la aplicación, un ComboBox y un
TextBlock, el primero lo vamos a cargar con los estilos existentes
y el TextBlock, recibirá el estilo, y se modificará el texto con el
valor del ComboBox.</p>

<pre class="brush: xml">
&lt;Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="300"&gt;
    &lt;Window.Resources&gt;
        &lt;CollectionViewSource x:Key="CvThemes"/&gt;
    &lt;/Window.Resources&gt;
    &lt;StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center"&gt;
        &lt;ComboBox x:Name="CmbThemes" ItemsSource="{Binding Source={StaticResource CvThemes}}" 
                  SelectionChanged="CmbThemesSelectionChanged" /&gt;
        &lt;TextBlock Text="{Binding SelectedItem, ElementName=CmbThemes}" 
                   Style="{DynamicResource TextBlockStyle}"/&gt;
    &lt;/StackPanel&gt;
&lt;/Window&gt;
</pre>

<p>El combo se está cargando con un CollectionViewSource, que a
priori, no tiene elementos, para ello deberemos cargarlo desde el
code behind. Pero, antes de cargar esta colección, debemos
modificar unas propiedades de la compilación de los diccionarios
alternativos, ya que lo que vamos a hacer es recorrer un directorio
de la aplicación en busca de estos diccionarios, así podremos dejar
una puerta abierta a la creación dinámica de diccionarios, bien por
manualmente por el usuario, bien a través de un control de nuestra
aplicación. Las propiedades que debemos modificar son las
siguientes, Acción de compilación de Page a Contenido, y Copiar en
el directorio a Siempre o a si es Posterior.</p>

<p><a
href="/media/2262/Windows-Live-Writer_Cambiar-de-tema-dinmicamente_106EF_image_5.png">
<img src="/media/2267/Windows-Live-Writer_Cambiar-de-tema-dinmicamente_106EF_image_thumb_1.png" width="244" height="222" alt="Compilación" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border-width: 0px;"/></a></p>

<p>Para obtener los diccionarios, tenemos que recorrer el
directorio "Theme", que es donde hemos creado los distintos
diccionarios, y los agregamos uno a uno al CollectionViewSource.
Después adaptaremos el evento SelectionChanged del combo para que
desencadene el cambio de estilo.</p>

<pre class="brush: c#">
            var cv = (CollectionViewSource)TryFindResource("CvThemes");
            var list = new ArrayList {"Principal"};
            
            foreach (FileInfo file in new DirectoryInfo(Environment.CurrentDirectory + @"\Theme\").GetFiles("*.xaml"))
            {
                list.Add(file.Name.Replace(file.Extension, String.Empty));
            }
            cv.Source = list;
</pre>

<p>Para agregar un diccionario de forma dinámica, debemos crear un
objeto ResourceDictionary, al cual le referenciamos una URI
relativa del diccionario a agregar, después se limpia la caché de
diccionarios agregados, y se agrega el que acabamos de crear.</p>

<pre class="brush: c#">
using System;
using System.Collections;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        private const string PathTheme = "Theme/{0}.xaml";
        public MainWindow()
        {
            InitializeComponent();
            var cv = (CollectionViewSource)TryFindResource("CvThemes");
            var list = new ArrayList {"Principal"};
            
            foreach (FileInfo file in new DirectoryInfo(Environment.CurrentDirectory + @"\Theme\").GetFiles("*.xaml"))
            {
                list.Add(file.Name.Replace(file.Extension, String.Empty));
            }
            cv.Source = list;
        }

        private void CmbThemesSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var dictionary = new ResourceDictionary
                {
                    Source = new Uri(String.Format(PathTheme, CmbThemes.SelectedItem), 
                        UriKind.RelativeOrAbsolute)
                };
            Application.Current.Resources.MergedDictionaries.Clear();
            Application.Current.Resources.MergedDictionaries.Add(dictionary);
        }
    }
}
</pre>

<!--?UMBRACO_MACRO 
location="https://skydrive.live.com/embed?cid=3A3CE6BACC2DC9F6&amp;resid=3A3CE6BACC2DC9F6%218566&amp;authkey=AO44xuPlhY5Yli4"
macroAlias="Skydrive" /?-->
]]></description></item><item><title>Custom Control WPF II</title><link>http://setvalue.net/2012/9/17/custom-control-wpf-ii/</link><pubDate>Mon, 17 Sep 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/9/17/custom-control-wpf-ii/</guid><description><![CDATA[ 
<p>Continuando con el <a href="/2012/7/15/custom-control-wpf-i/"
target="_blank">artículo anterior</a>, vamos a insertar el control
que hemos creado y personalizado en el diccionario de recursos, a
la aplicación.<!--more--></p>

<p>Para ello, referenciamos el namespace al marcado, como antes,
con el prefijo "local" e insertamos el objeto en el punto de la
aplicación que deseemos.</p>

<pre class="brush: xml">
&lt;Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525"&gt;
    &lt;Grid Background="AliceBlue"&gt;
        &lt;local:CustomControl Style="{DynamicResource CustomCtrlv1}" Text="@rfcm83"
                             Clicks="0 clicks" HorizontalAlignment="Center" VerticalAlignment="Center"&gt;
            &lt;local:CustomControl.Icon&gt;
                &lt;Image Source="twitter.png"/&gt;
            &lt;/local:CustomControl.Icon&gt;
        &lt;/local:CustomControl&gt;
    &lt;/Grid&gt;
&lt;/Window&gt;
</pre>

<p>Es fundamental explicar que para que el control tenga el diseño
que hemos creado, hay que referenciarlo dinámicamente al estilo
creado, y después introducir las distintas propiedades que hemos
creado. Para el texto he puesto "@rfcm83", para el contador he
introducido un texto que dice "0 clicks" y luego una imagen de
twitter, que previamente he introducido al proyecto. Las
propiedades de alineación vertical y horizontal, es para que el
control salga en el centro de la pantalla, pero esto es totalmente
opcional.</p>

<p>Aunque he establecido un valor a la propiedad "Clicks", lo
lógico es que esta propiedad se actualice con cada click que se
realiza en la aplicación, para ello, hay que implementar la
interfaz INotifyPropertyChanged de System.ComponentModel, la cual
va a gestionar los cambios en la propiedad "Clicks" y relacionar
esta propiedad con una que implementemos en el code behind. Además,
es necesario crear un evento MouseLeftButtonDown sobre el control,
para que se desencadene finalmente la modificación de la propiedad
"Clicks".</p>

<p>Implementamos el código fuente, empezando por implementar los
miembros de la interfaz INotifyPropertyChanged. Después creamos la
propiedad pública "ClicksTxt", la cual va a ser la que que
notifique el cambio, y modifique el texto del control. Por último
creamos el evento MouseLeftButtonDown de nuestro control, y este se
ocupará de modificar la propiedad, incrementando un contador en el
código.</p>

<pre class="brush: c#">
using System;
using System.Windows;
using System.Windows.Input;
using System.ComponentModel;

namespace WpfApplication1
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private const string ClickStr = "{0} click(s)";
        private int _nClicks = 0;
        private string _clicksTxt;
        public String ClicksTxt
        {
            get { return _clicksTxt; }
            set
            {
                _clicksTxt = value;
                NotifyChange("ClicksTxt");
            }
        }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void BtnCControlClick(object sender, MouseButtonEventArgs e)
        {
            _nClicks++;
            ClicksTxt = String.Format(ClickStr, _nClicks);
        }
        
        private void NotifyChange(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
    }
}
</pre>

<p>A continuación vamos al diseñador, y modificamos el control,
agregándole un evento y modificando la propiedad "Clicks" del
control, enlazándola a la propiedad que hemos creado en el code
behind ("ClicksTxt").</p>

<pre class="brush: xml">
        &lt;local:CustomControl x:Name="BtnCControl" Style="{DynamicResource CustomCtrlv1}" Text="@rfcm83"
                             Clicks="{Binding ClicksTxt, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
                             HorizontalAlignment="Center" VerticalAlignment="Center"
                             MouseLeftButtonDown="BtnCControlClick"&gt;
            &lt;local:CustomControl.Icon&gt;
                &lt;Image Source="twitter.png"/&gt;
            &lt;/local:CustomControl.Icon&gt;
        &lt;/local:CustomControl&gt;
</pre>

<p>Una vez realizados estos cambios, el control debería incrementar
el número de clicks que se vayan pulsando sobre el objeto.</p>

<p><a
href="/media/2211/Windows-Live-Writer_Custom-Control-WPF-II_E4A0_image_2.png">
<img src="/media/2216/Windows-Live-Writer_Custom-Control-WPF-II_E4A0_image_thumb.png" width="544" height="364" alt="Botón sin iniciar" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border-width: 0px;"/></a></p>

<p>Al arrancar, tendríamos el control sin ningún texto, pues la
propiedad no estará iniciada, después de hacer clicks tendremos
algo como esto.</p>

<p><a
href="/media/2221/Windows-Live-Writer_Custom-Control-WPF-II_E4A0_image_4.png">
<img src="/media/2226/Windows-Live-Writer_Custom-Control-WPF-II_E4A0_image_thumb_1.png" width="544" height="364" alt="Botón iniciado" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border-width: 0px;"/></a></p>

<p>&nbsp;</p>

<?UMBRACO_MACRO
location="https://skydrive.live.com/embed?cid=3A3CE6BACC2DC9F6&amp;resid=3A3CE6BACC2DC9F6%218512&amp;authkey=ANXuPihaCzbkOSc"
macroAlias="Skydrive" />
]]></description></item><item><title>Custom Control WPF I</title><link>http://setvalue.net/2012/9/15/custom-control-wpf-i/</link><pubDate>Sat, 15 Sep 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/9/15/custom-control-wpf-i/</guid><description><![CDATA[ 
<p>Muchas veces, realizar una personalización de un botón, o de
otro de los controles que encontramos en el namespace
System.Windows para utilizar directamente en el marcado XAML,
resulta un trabajo bastante tedioso y a veces complejo personalizar
uno de estos controles si no tienes mucha habilidad con el diseño,
y no siempre cumple con las expectativas que tenemos.
<!--more--></p>

<p>Con este artículo, mi intención es crear un control, que
contenga un texto, una imagen, y un contador, el cual lo vamos a
utilizar a modo de ejemplo para contar los clicks que hagamos en el
control.</p>

<h4>Crear un proyecto WPF</h4>

<p>Creamos un nuevo proyecto de WPF, al que le vamos a agregar dos
nuevos elementos, el primero un diccionario de recursos, y el otro
un control personalizado.</p>

<p><a
href="/media/2183/Windows-Live-Writer_Custom-Control_94DA_image_4.png">
<img src="/media/2188/Windows-Live-Writer_Custom-Control_94DA_image_thumb_1.png" width="544" height="333" alt="Agregar nuevo elemento" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border-width: 0px;"/></a></p>

<h4>Control personalizado</h4>

<p>Este control, no deja de ser una clase que implementa Control
que representa la clase base de los elementos de la interfaz de
usuario (UI) que utilizan un control para definir su apariencia de
System.Windows.Controls.ControlTemplate.</p>

<p>Lo primero es agregar varias DependecyProperties, una para el
icono, otra para el texto del control, y la última el contador de
clicks.</p>

<pre class="brush: c#">
        public static readonly DependencyProperty TextProperty;
        public static readonly DependencyProperty IconProperty;
        public static readonly DependencyProperty ClicksProperty;

        public object Icon
        {
            get { return (Image)GetValue(IconProperty); }
            set { SetValue(IconProperty, value); }
        }
        public String Text
        {
            get { return (String)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
        public String Clicks
        {
            get { return (String)GetValue(ClicksProperty); }
            set { SetValue(ClicksProperty, value); }
        }
</pre>

<p>Una vez agregadas las distintas propiedades, las registramos en
el constructor.</p>

<pre class="brush: c#">
        static CustomControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
            CustomControl.TextProperty = DependencyProperty.Register("Text", typeof(String), typeof(CustomControl), new UIPropertyMetadata(null));
            CustomControl.IconProperty = DependencyProperty.Register("Icon", typeof(Image), typeof(CustomControl), new UIPropertyMetadata(null));
            CustomControl.ClicksProperty = DependencyProperty.Register("Clicks", typeof(String), typeof(CustomControl), new UIPropertyMetadata(null));
        }
</pre>

<p>Finalmente tenemos el control listo para ser usado, salvo por un
detalle, que no está especificado cómo se disponen los distintos
objetos.</p>

<pre class="brush: c#">
using System;
using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1
{
    public class CustomControl : Control
    {
        public static readonly DependencyProperty TextProperty;
        public static readonly DependencyProperty IconProperty;
        public static readonly DependencyProperty ClicksProperty;

        public object Icon
        {
            get { return (Image)GetValue(IconProperty); }
            set { SetValue(IconProperty, value); }
        }
        public String Text
        {
            get { return (String)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
        public String Clicks
        {
            get { return (String)GetValue(ClicksProperty); }
            set { SetValue(ClicksProperty, value); }
        }
        static CustomControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
            CustomControl.TextProperty = DependencyProperty.Register("Text", typeof(String), typeof(CustomControl), new UIPropertyMetadata(null));
            CustomControl.IconProperty = DependencyProperty.Register("Icon", typeof(Image), typeof(CustomControl), new UIPropertyMetadata(null));
            CustomControl.ClicksProperty = DependencyProperty.Register("Clicks", typeof(String), typeof(CustomControl), new UIPropertyMetadata(null));
        }
    }
}
</pre>

<h4>Diccionario de recursos</h4>

<p>El diccionario de recursos, nos permite crear plantillas y
estilos globales o específicos, para los controles que introducimos
en la aplicación, además, nos permite añadir valores como colores,
brochas, fuentes, tamaños, y características comunes entre
controles, lo cual nos permite reutilizar recursos, con el fin de
dar un formato homogéneo a nuestra aplicación. Más información en
<a
href="http://msdn.microsoft.com/es-es/library/system.windows.resourcedictionary.aspx"
 target="_blank">MSDN Microsoft</a>.</p>

<p>Una vez agregado el diccionario, lo primero que hay que realizar
es vincularlo a la aplicación. Blend Studio lo suele hacer de forma
automática, pero si trabajas desde Visual Studio, lo normal es
hacerlo uno mismo, ya que si utilizas varias hojas de recursos, el
orden de estas es fundamental, ya que en el marcado, cuando agregas
varios componentes en la misma posición, prevalece y se superpone
el que se encuentra en último lugar.</p>

<p>Bien, para agregar un nuevo diccionario, abrimos el App.xaml, y
ahí agregamos a los recursos el diccionario.</p>

<pre class="brush: xml">
&lt;Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml"&gt;
    &lt;Application.Resources&gt;
        &lt;ResourceDictionary&gt;
            &lt;ResourceDictionary.MergedDictionaries&gt;
                &lt;ResourceDictionary Source="Dictionary.xaml"/&gt;
                &lt;!--&lt;ResourceDictionary Source="Otro.xaml"/&gt; Este prevalecería sobre el anterior--&gt;
            &lt;/ResourceDictionary.MergedDictionaries&gt;
        &lt;/ResourceDictionary&gt;
    &lt;/Application.Resources&gt;
&lt;/Application&gt;
</pre>

<p>Una vez realizado esto, los recursos estarán disponibles de
forma dinámica para nuestra aplicación.</p>

<p>A continuación en el diccionario de recursos agregamos un nuevo
estilo para personalizar el control que hemos creado, para que
podamos crear este estilo primero debemos agregar el namespace al
marcado, para lo que voy a utilizar "local" como prefijo.</p>

<pre class="brush: xml">
&lt;ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfApplication1"&gt;
&lt;/ResourceDictionary&gt;
</pre>

<p>Después agregamos un par de colores, y sus respectivas brochas,
para que podamos dar color al control.</p>

<pre class="brush: xml">
    &lt;Color x:Key="ColorTwitter"&gt;#FF77AADB&lt;/Color&gt;
    &lt;Color x:Key="ColorWhite"&gt;#FFFDFDFD&lt;/Color&gt;
    &lt;Color x:Key="ColorBlue"&gt;#FF24348C&lt;/Color&gt;
    &lt;SolidColorBrush x:Key="BrushTwitter" Color="{StaticResource ColorTwitter}"/&gt;
    &lt;SolidColorBrush x:Key="BrushWhite" Color="{StaticResource ColorWhite}"/&gt;
    &lt;SolidColorBrush x:Key="BrushBlue" Color="{StaticResource ColorBlue}"/&gt;
</pre>

<p>Y empezamos a crear el estilo, para componer el objeto, voy a
utilizar un grid, con dos columnas, y dos filas, en la primera
columna insertaré a doble fila una imagen. En la otra columna, en
una fila un texto, y en la otra fila un contador de clicks.</p>

<pre class="brush: xml">
&lt;ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfApplication1"&gt;

    &lt;Color x:Key="ColorTwitter"&gt;#FF77AADB&lt;/Color&gt;
    &lt;Color x:Key="ColorWhite"&gt;#FFF3F3F3&lt;/Color&gt;
    &lt;Color x:Key="ColorBlue"&gt;#FF24348C&lt;/Color&gt;
    &lt;SolidColorBrush x:Key="BrushTwitter" Color="{StaticResource ColorTwitter}"/&gt;
    &lt;SolidColorBrush x:Key="BrushWhite" Color="{StaticResource ColorWhite}"/&gt;
    &lt;SolidColorBrush x:Key="BrushBlue" Color="{StaticResource ColorBlue}"/&gt;

    &lt;Style x:Key="CustomCtrlv1" TargetType="{x:Type local:CustomControl}"&gt;
        &lt;Setter Property="Background" Value="{StaticResource BrushTwitter}"/&gt;
        &lt;Setter Property="Foreground" Value="{StaticResource BrushWhite}"/&gt;
        &lt;Setter Property="BorderBrush" Value="{StaticResource BrushBlue}"/&gt;
        &lt;Setter Property="BorderThickness" Value="1"/&gt;
        &lt;Setter Property="Margin" Value="7,3"/&gt;
        &lt;Setter Property="Cursor" Value="Hand"/&gt;
        &lt;Setter Property="Template"&gt;
            &lt;Setter.Value&gt;
                &lt;ControlTemplate TargetType="{x:Type local:CustomControl}"&gt;
                    &lt;Border x:Name="bd" Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"&gt;
                        &lt;Grid Margin="{TemplateBinding Margin}"&gt;
                            &lt;Grid.ColumnDefinitions&gt;
                                &lt;ColumnDefinition Width="Auto" /&gt;
                                &lt;ColumnDefinition /&gt;
                            &lt;/Grid.ColumnDefinitions&gt;
                            &lt;Grid.RowDefinitions&gt;
                                &lt;RowDefinition /&gt;
                                &lt;RowDefinition /&gt;
                            &lt;/Grid.RowDefinitions&gt;
                            &lt;ContentPresenter x:Name="img" Grid.Column="0" Grid.Row="0"
                                              Grid.RowSpan="2" ContentSource="Icon"
                                              Width="30" Height="30" Margin="5"/&gt;
                            &lt;TextBlock x:Name="txt" Grid.Column="1" Grid.Row="0"
                                       VerticalAlignment="Center" Text="{TemplateBinding Text}"
                                       Foreground="{TemplateBinding Foreground}"
                                       FontWeight="Bold"/&gt;
                            &lt;TextBlock x:Name="clicks" Grid.Column="1" Grid.Row="1"
                                       VerticalAlignment="Center" Text="{TemplateBinding Clicks}"
                                       Foreground="{TemplateBinding Foreground}"
                                       FontStyle="Italic"/&gt;
                        &lt;/Grid&gt;
                    &lt;/Border&gt;
                    &lt;ControlTemplate.Triggers&gt;
                        &lt;Trigger Property="IsMouseOver" Value="True"&gt;
                            &lt;Setter Property="Background" Value="{DynamicResource BrushWhite}"/&gt;
                            &lt;Setter Property="Foreground" Value="{DynamicResource BrushBlue}"/&gt;
                            &lt;Setter Property="BorderBrush" Value="{DynamicResource BrushTwitter}"/&gt;
                        &lt;/Trigger&gt;
                    &lt;/ControlTemplate.Triggers&gt;
                &lt;/ControlTemplate&gt;
            &lt;/Setter.Value&gt;
        &lt;/Setter&gt;
    &lt;/Style&gt;
</pre>

<p>Aprovechando que estaba creando el estilo, he agregado un
Trigger para cambiar la apariencia del control cuando el ratón se
encuentre encima. Así tendremos dos estados distintos cuando el
control tiene encima el cursor.</p>

<p>El siguiente paso es insertar el control dentro de la
aplicación, para ello continúo en el <a
href="/2012/7/17/custom-control-wpf-ii/" target="_blank">siguiente
artículo</a>.</p>
]]></description></item><item><title>Visor GIS con WPF y el API de Esri</title><link>http://setvalue.net/2012/9/8/visor-gis-con-wpf-y-el-api-de-esri/</link><pubDate>Sat, 08 Sep 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/9/8/visor-gis-con-wpf-y-el-api-de-esri/</guid><description><![CDATA[ 
<p>Continuando con el hilo del <a
href="/2012/6/27/howto-hacer-mapping-y-no-morir-en-el-intento-77/"
target="_blank">último post</a>, y aprovechando que el marcado en
Silverlight y WPF es el mismo, generaremos una aplicación de
escritorio, que acceda a servicios WMS como los del PNOA, e
implementaremos una funcionalidad de búsqueda mediante el uso de
Bing Services.<!--more--></p>

<h5>Ficha técnica:</h5>

<ul>
<li><a
href="http://www.esri.com/apps/products/download/index.cfm?fuseaction=download.main&amp;downloadid=747"
 target="_blank">ESRI API 2.4 for WPF</a></li>

<li>C# .NET (4.0)</li>

<li>Key de Desarrollador de Bing (ver este <a
href="/2012/6/14/howto-hacer-mapping-y-no-morir-en-el-intento-47/"
target="_blank">post</a>)</li>
</ul>

<p>Una vez instalado el API, lo primero, como con el API para
Silverlight, es referenciar el ArcGIS.Client, ArcGIS.Toolkit y
ArcGIS.Toolkit.DataSources. <strong>Nota</strong>: Al agregar las
referencias, es importante agregar únicamente las que son
compatibles con el Framework que estamos utilizando, en este post
yo estoy utilizando el 4.0, pero aparecen disponibles las del
3.5.</p>

<p>Continuamos trayéndonos el marcado XAML del ejemplo anterior, al
que vamos a hacerle algún cambio que otro cambio:</p>

<pre class="brush: xml">
        &lt;Grid Grid.Row="1"&gt;
            &lt;esri:Map x:Name="MyMap" WrapAround="True"&gt;
                &lt;esri:Map.Extent&gt;
                    &lt;esri:Envelope XMin="-21.37979367" XMax="5.07891717" YMin="27.14108121" YMax="44.03141073"&gt;
                        &lt;esri:Envelope.SpatialReference&gt;
                            &lt;esri:SpatialReference WKID="4326"/&gt;
                        &lt;/esri:Envelope.SpatialReference&gt;
                    &lt;/esri:Envelope&gt;
                &lt;/esri:Map.Extent&gt;
            &lt;/esri:Map&gt;
            &lt;esri:ScaleLine x:Name="Scale" Map="{Binding ElementName=MyMap}" HorizontalAlignment="Left" VerticalAlignment="Bottom" Visibility="Visible" /&gt;
            &lt;esri:Legend x:Name="Legend" Map="{Binding ElementName=MyMap}" HorizontalAlignment="Right" VerticalAlignment="Center"/&gt;
            &lt;esri:Navigation x:Name="Navigation" Map="{Binding ElementName=MyMap}"/&gt;
            &lt;esri:MapProgressBar x:Name="PBar" Map="{Binding ElementName=MyMap}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="30"/&gt;
        &lt;/Grid&gt;
</pre>

<p>Después vamos con el code behind</p>

<pre class="brush: c#">
        private void MainWindowLoaded(object sender, RoutedEventArgs e)
        {
            const String wmsStr = @"http://www.idee.es/wms/PNOA/PNOA";

            var wms = new WmsLayer
            {
                Url = wmsStr,
                SkipGetCapabilities = false,
                Layers = new string[] { "PNOA" },
                Version = "1.3.0"
               //ProxyUrl = proxyStr
            };

            MyMap.Layers.Add(wms);
            MyMap.Extent = MyMap.Layers.GetFullExtent();
        }
</pre>

<p>He comentado la parte del proxy porque aquí no la vamos a
necesitar. Tampoco vamos a añadir ningún tipo de fichero kml, ya
que lo que vamos a hacer es realizar una búsqueda de un lugar
mediante una búsqueda de texto. Para ello, vamos a agregar un
TextBox y un botón para trabajar esta funcionalidad.</p>

<pre class="brush: xml">
        &lt;Grid Grid.Row="0"&gt;
            &lt;Grid.ColumnDefinitions&gt;
                &lt;ColumnDefinition /&gt;
                &lt;ColumnDefinition Width="Auto"/&gt;
            &lt;/Grid.ColumnDefinitions&gt;
            &lt;TextBox x:Name="TxtSearch" Grid.Column="0"/&gt;
            &lt;Button x:Name="BtnSearch" Grid.Column="1" Click="BtnSearchClick"&gt;
                &lt;AccessText Text="_Buscar"/&gt;
            &lt;/Button&gt;
        &lt;/Grid&gt;
</pre>

<p>Al evento Click del botón, ya le he añadido un método, el cual
va a iniciar la petición de coordenadas al servicio Bing. Cosas
fundamentales a tener en cuenta:</p>

<ul>
<li>La petición de datos al servicio, en este ejemplo es en
castellano, pero podría ser en inglés</li>

<li>El ámbito de trabajo siempre va a ser España</li>

<li>Muchas ciudades de España tienen el mismo nombre en América,
(Córdoba, Toledo, Madrid, Málaga...)</li>
</ul>

<p>Considerando esto podemos comprobar si el literal que ha pasado
el usuario contiene la palabra España o Spain, y si no lo tiene
concatenarlo, y enviárselo al servicio de Bing. Una vez realizada
la petición recibimos un xml, al que debemos comprobar que las
coordenadas que trae se encuentran dentro de nuestros límites.</p>

<p>Hacemos esto porque si pones New York, España, nunca va a
devolverte una ubicación, pero si pones Córdoba, España, seguro que
no va a devolverte la Córdoba Argentina. Si las coordenadas que
recibimos están fuera del ámbito, le mostraríamos directamente al
usuario un mensaje diciéndole que está fuera de los límites del
mapa.</p>

<pre class="brush: c#">
        private const Double XMin = -21.37979367;
        private const Double XMax = 5.07891717;
        private const Double YMin = 27.14108121;
        private const Double YMax = 44.03141073;

        private const string UrlBingLocation =
            "http://dev.virtualearth.net/REST/v1/Locations?query={0}&amp;includeNeighborhood=true&amp;maxResults=1&amp;output=xml&amp;key={1}";

        private void BtnSearchClick(object sender, RoutedEventArgs e)
        {
            var txt = TxtSearch.Text;
            if (!String.IsNullOrEmpty(txt))
            {
                if (!txt.ToUpper().Contains("ESPAÑA") &amp;&amp; !txt.ToUpper().Contains("SPAIN"))
                {
                    txt += ",%20Spain";
                }
                txt = txt.Replace(" ", "%20");
                var env = GetCoordinates(ref txt);
                if (env != null)
                {
                    var center = env.GetCenter(); 
                    if (IsInSpain(center.X, XMin, XMax) &amp;&amp; IsInSpain(center.Y, YMin, YMax))
                    {
                        var gl = new GraphicsLayer {ID = txt};
                        gl.Graphics.Add(new Graphic { Geometry = new MapPoint(center.X, center.Y)});
                        MyMap.Layers.Add(gl);
                        MyMap.Extent = env;
                    }
                    else
                    {
                        MessageBox.Show("La ciudad seleccionada no se encuentra en España, o no está escrita correctamente");
                    }
                }
            }
        }

        private Envelope GetCoordinates(ref string txtSearched)
        {
            var env = new Envelope();
            var key = "KEY_BING_MAPS";
            var url = String.Format(UrlBingLocation, txtSearched, key);
            
            try
            {
                var xmlTxtReader = new XmlTextReader(url);
                var ds = new DataSet("Location");
                ds.ReadXml(xmlTxtReader);
                var dt = ds.Tables["BoundingBox"];
                var sLat = Convert.ToDouble((dt.Rows[0]["SouthLatitude"]).ToString().Replace(".", ","));
                var wLon = Convert.ToDouble((dt.Rows[0]["WestLongitude"]).ToString().Replace(".", ","));
                var nLat = Convert.ToDouble((dt.Rows[0]["NorthLatitude"]).ToString().Replace(".", ","));
                var eLon = Convert.ToDouble((dt.Rows[0]["EastLongitude"]).ToString().Replace(".", ","));
                var dtN = ds.Tables["Location"];
                txtSearched = dtN.Rows[0]["Name"].ToString();
                env = new Envelope(wLon, sLat, eLon, nLat);
                return env;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return null;
            }
        }

        private bool IsInSpain(double value, double min, double max)
        {
            return (min &lt;= value &amp;&amp; max &gt;= value);
        }
</pre>

<p>Es importante reseñar, que estamos haciendo trabajos de remplazo
de texto, "." por ",", para salvar la diferencia cultural de los
números, y espacios por "%20" para montar la url sin espacios.</p>

<p>Finalmente agregamos una nueva capa gráfica al mapa, con el
nombre que nos devuelve el servicio, para mostrar lo que realmente
ha localizado.</p>

<p><a
href="/media/2065/Windows-Live-Writer_Visor-GIS-con-WPF-y-el-API-de-Esri_6E41_image_2.png">
<img src="/media/2070/Windows-Live-Writer_Visor-GIS-con-WPF-y-el-API-de-Esri_6E41_image_thumb.png" width="463" height="322" alt="Quintanar de la Orden" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<?UMBRACO_MACRO
location="https://skydrive.live.com/embed?cid=3A3CE6BACC2DC9F6&amp;resid=3A3CE6BACC2DC9F6%214507&amp;authkey=AMgFFryyEFdJCi8"
macroAlias="Skydrive" />
]]></description></item><item><title>#HowTo: Hacer mapping y no morir en el intento 7/7</title><link>http://setvalue.net/2012/8/27/howto-hacer-mapping-y-no-morir-en-el-intento-77/</link><pubDate>Mon, 27 Aug 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/8/27/howto-hacer-mapping-y-no-morir-en-el-intento-77/</guid><description><![CDATA[ 
<p>ESRI también provee varios APIs para la web y para escritorio,
que mediante controles de alto nivel, permiten proveer cartografía
a nuestros usuarios. Aunque claro, llevan algunas limitaciones, que
en algunos casos sólo puedes resolver con un licencia de ArcGIS
Server, que por supuesto ya no es gratis.<!--more--></p>

<h5>Ficha técnica:</h5>

<ul>
<li>ESRI API 3.0 for Silverlight</li>

<li>C#.NET (4.0)</li>
</ul>

<p>He seleccionado este API, aunque Silverlight ya no es una
prioridad para el gigante de Redmond, el código que vamos a generar
servirá para enlazar con el siguiente post, ya que el código XAML y
el code behind serán aproximadamente los mismos.</p>

<p>Lo primero que hemos de hacer es descargar el API de la página
de descargas de <a
href="http://www.esri.com/apps/products/download/index.cfm?fuseaction=download.all"
 target="_blank">ESRI</a>, si no eres usuario, te puedes registrar
rápidamente para descargarlo.</p>

<p>Una vez descargado, procedemos a instalar.</p>

<p><a
href="/media/2939/Windows-Live-Writer_4d6db97b8f3e_121BE_image_6.png">
<img src="/media/2944/Windows-Live-Writer_4d6db97b8f3e_121BE_image_thumb_2.png" width="421" height="322" alt="Instalación API" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p><a
href="/media/2949/Windows-Live-Writer_4d6db97b8f3e_121BE_image_8.png">
<img src="/media/2954/Windows-Live-Writer_4d6db97b8f3e_121BE_image_thumb_3.png" width="421" height="322" alt="Configurando Instalación" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>Creamos un nuevo proyecto de Silverlight / ESRI Standard Map
Application</p>

<p><a
href="/media/2969/Windows-Live-Writer_4d6db97b8f3e_121BE_image_10.png">
<img src="/media/2974/Windows-Live-Writer_4d6db97b8f3e_121BE_image_thumb_4.png" width="464" height="322" alt="image" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>Una vez creamos el proyecto, seleccionando una de las plantillas
que nos ofrece ESRI, debemos actualizar las referencias de
Microsoft.Expressions.Interactions y System.Windows.Interactivity.
Generamos y voila, tenemos mapa.</p>

<p><a
href="/media/2979/Windows-Live-Writer_4d6db97b8f3e_121BE_image_14.png">
<img src="/media/2984/Windows-Live-Writer_4d6db97b8f3e_121BE_image_thumb_6.png" width="507" height="322" alt="Plantilla Standard en ejecución" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>Esta plantilla está muy bien, tenemos un control de mapa, con un
montón de elementos y funcionalidades, qué a priori, a parte de
estar muy estético, puede resultar que no nos sirva para nada. Así,
que vamos a crear nuestra propio control al que le iremos añadiendo
los componentes que consideramos necesarios. Para ello, agregamos
un nuevo proyecto de Silverlight.</p>

<p><a
href="/media/2959/Windows-Live-Writer_4d6db97b8f3e_121BE_image_12.png">
<img src="/media/2964/Windows-Live-Writer_4d6db97b8f3e_121BE_image_thumb_5.png" width="464" height="322" alt="Nuevo proyecto" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>Si la instalación del API ha sido correcto, tendremos en nuestro
cuadro de herramientas los controles de ESRI, pero sino es así. Lo
más sencillo, y más "limpio" es agregar las referencias a mano,
para ello, agregamos la referencia ESRI.ArcGIS.Client a nuestro
proyecto, y a nuestro marcado.</p>

<pre class="brush: xml">
&lt;UserControl x:Class="SilverlightApplication2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:esri="http://schemas.esri.com/arcgis/client/2009"
    mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="800"&gt;

    &lt;Grid x:Name="LayoutRoot" Background="White" /&gt;
&lt;/UserControl&gt;
</pre>

<p>Una vez lo hemos agregado, incluimos un nuevo control de mapa en
el grid.</p>

<pre class="brush: xml">
    &lt;Grid x:Name="LayoutRoot" Background="White"&gt;
        &lt;esri:Map x:Name="map1" WrapAround="True"&gt;
            &lt;esri:Map.Layers&gt;
                &lt;esri:LayerCollection&gt;
                    &lt;esri:ArcGISTiledMapServiceLayer Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer" /&gt;
                &lt;/esri:LayerCollection&gt;
            &lt;/esri:Map.Layers&gt;
        &lt;/esri:Map&gt;
    &lt;/Grid&gt;
</pre>

<p>Y con esto, tenemos un control de mapa pelado sin ningún
componente más. Ahora lo que vamos a hacer es incluir un servicio
<a href="http://es.wikipedia.org/wiki/Web_Map_Service"
target="_blank">WMS</a>, concretamente el del <a
href="http://www.ign.es/PNOA/" target="_blank">PNOA</a>, el cual
nos va a permitir cargar el servicio web del plan nacional de
ortofotografía aérea española. Para ello, agregamos otra referencia
al proyecto de Silverlight, ESRI.ArcGIS.Client.Toolkit.DataSources,
y dejamos el control de mapa sin ninguna capa, ya que vamos a
agregar el servicio wms desde el código.</p>

<pre class="brush: c#">
            var wms = new WmsLayer
                          {
                              Url = "http://www.idee.es/wms/PNOA/PNOA",
                              SkipGetCapabilities = false,
                              Layers = new string[] { "PNOA" },
                              Version = "1.3.0",
                              ProxyUrl = "http://tusitio.web/proxy.ashx"
                          };
</pre>

<p>El API de Silverlight, requiere de un proxy para realizar las
peticiones al servicio WMS, para ello ESRI sirve un <a
href="http://help.arcgis.com/en/webapi/silverlight/help/0166/other/SLProxyPage.zip"
 target="_blank">ejemplo</a> para que puedas incluirlo en tu sitio
web, más <a
href="http://help.arcgis.com/en/webapi/silverlight/help/index.html#//016600000022000000"
 target="_blank">información</a>.</p>

<p>Después continuamos creando una capa KML, y agregando tanto el
sevicio WMS como el KML que hemos agregado.</p>

<pre class="brush: c#">
            var kml = new KmlLayer
                          {
                              Url = new Uri("http://tusitio.web/fichero.kml"),
                              ProxyUrl = "http://tusitio.web/proxy.ashx"
                          };
            MyMap.Layers.Add(wms);
            MyMap.Layers.Add(kml);
</pre>

<p>Por último incorporamos al control de usuario una barra de
progreso para que nos indique el proceso de carga de las capas, un
control de navegación, la leyenda y la escala.</p>

<pre class="brush: c#">
&lt;UserControl x:Class="SilverlightApplication2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:esri="http://schemas.esri.com/arcgis/client/2009"
    mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="800" Loaded="UserControlLoaded"&gt;

    &lt;Grid x:Name="LayoutRoot" Background="AliceBlue"&gt;
        &lt;esri:Map x:Name="MyMap" WrapAround="True"&gt;
            &lt;esri:Map.Extent&gt;
                &lt;esri:Envelope XMin="-21.37979367" XMax="5.07891717" YMin="27.14108121" YMax="44.03141073" &gt;
                    &lt;esri:Envelope.SpatialReference&gt;
                        &lt;esri:SpatialReference WKID="4326"/&gt;
                    &lt;/esri:Envelope.SpatialReference&gt;
                &lt;/esri:Envelope&gt;
            &lt;/esri:Map.Extent&gt;
        &lt;/esri:Map&gt;
        &lt;esri:ScaleLine x:Name="Scale" Map="{Binding ElementName=MyMap}" 
        HorizontalAlignment="Left" VerticalAlignment="Bottom" Visibility="Visible" /&gt;
        &lt;esri:Legend x:Name="Legend" Map="{Binding ElementName=MyMap}" 
        HorizontalAlignment="Right" VerticalAlignment="Center"/&gt;
        &lt;esri:Navigation x:Name="Navigation" Map="{Binding ElementName=MyMap}"/&gt;
        &lt;esri:MapProgressBar x:Name="PBar" Map="{Binding ElementName=MyMap}" 
        HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="30"/&gt;
    &lt;/Grid&gt;
&lt;/UserControl&gt;
</pre>

<p><a
href="/media/2039/Windows-Live-Writer_21cee03bb7dd_114B0_image_2.png">
<img src="/media/2044/Windows-Live-Writer_21cee03bb7dd_114B0_image_thumb.png" width="516" height="316" alt="Aplicación Silverlight" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>
]]></description></item><item><title>#HowTo: Hacer mapping y no morir en el intento 6/7</title><link>http://setvalue.net/2012/8/23/howto-hacer-mapping-y-no-morir-en-el-intento-67/</link><pubDate>Thu, 23 Aug 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/8/23/howto-hacer-mapping-y-no-morir-en-el-intento-67/</guid><description><![CDATA[ 
<p>Una vez vistos los fáciles ejemplos para realizar mapping en tu
sitio web, sin necesidad de invertir muchísimo tiempo, podemos
incluir un mapa muy sencillo, que nos permite mostrar la ubicación
de nuestra empresa, o en definitiva lo que deseemos.</p>

<p>En la actualidad, desde la llegada de los smartphone, una de las
prioridades es la geolocalización del usuario, para así poder
mostrar contenidos en función de la ubicación en la que se
encuentra el consumidor. No solo podemos incluir mapas, sino que
también información o publicidad acorde a la zona.<!--more--></p>

<p>HTML5 entre sus novedades propuestas, incluye acceso a la
geolocalización, para ello utilizaremos una propiedad del
navegador, "<a href="http://dev.w3.org/geo/api/spec-source.html"
target="_blank">geolocation</a>", la cual nos va a permitir acceder
a la posición actual del cliente web. Acceder a esta información,
requiere que el usuario acepte dar esta información mediante un
prompt que aparece en el momento que consultamos esta
propiedad.</p>

<p><a
href="/media/2817/Windows-Live-Writer_7623bd207b65_B137_image_2.png">
<img src="/media/2822/Windows-Live-Writer_7623bd207b65_B137_image_thumb.png" width="513" height="322" alt="IE9 Geolocation" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p><a
href="/media/2827/Windows-Live-Writer_7623bd207b65_B137_image_4.png">
<img src="/media/2832/Windows-Live-Writer_7623bd207b65_B137_image_thumb_1.png" width="511" height="322" alt="Chrome Geolocation" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>Para ello verificamos que tiene geolocalización, una vez
comprobado, accedemos a la posición, y guardamos la latitud y la
longitud en dos input en nuestra página a modo de ejemplo, más
adelante, podemos ver que trabajando con esas coordenadas, podemos
tener bastante juego.</p>

<pre class="brush: javascript">
&lt;script type="text/javascript"&gt;
    if(navigator.geolocation)
    {
        navigator.geolocation.getCurrentPosition(function(position)
        {
            var lat = position.coords.latitude;
            var lon = position.coords.longitude;
            document.getElementById("lat").value = lat;
            document.getElementById("lon").value = lon;
        });
    }
&lt;/script&gt;
</pre>

<h5>Ficha técnica:</h5>

<ul>
<li>HTML5</li>

<li>Google Maps / Bing Maps</li>
</ul>

<p>Vamos a empezar con el API de Google, para ello, lo primero es
agregar la clave pública de nuestro sitio. Una vez agregado,
creamos una capa con el id "mapGoogle" a la que vamos a cargar el
API de Google con las coordenadas que nos ha proveído la
geolocalización.</p>

<pre class="brush: javascript">
  var lat = position.coords.latitude;
    var lon = position.coords.longitude;
    var myOptions = {
        center: new google.maps.LatLng(lat, lon),
        zoom: 14,
        mapTypeId: google.maps.MapTypeId.ROADMAP };
    var mapGoogle = new google.maps.Map(document.getElementById("mapGoogle"), myOptions);
</pre>

<p>Después agregamos otra capa "mapBing" la cual va albergar el API
de Bing Maps, para ello debemos agregar la referencia al JavaScript
y hacemos un "getMap" incluyendo la Key de Bing asociada a nuestro
sitio web.</p>

<pre class="brush: xml">
&lt;!DOCTYPE HTML&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Geolocation(HTML5)&lt;/title&gt;
    &lt;script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key=KEY_GOOGLE_MAPS&amp;sensor=false"&gt;&lt;/script&gt;
    &lt;script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"&gt;&lt;/script&gt;
    &lt;script type="text/javascript"&gt;
        if(navigator.geolocation)
        {
            navigator.geolocation.getCurrentPosition(function(position)
            {
                var lat = position.coords.latitude;
                var lon = position.coords.longitude;
                const Zoom = 14;
                var myOptions = {
                    center: new google.maps.LatLng(lat, lon),
                    zoom: Zoom,
                    mapTypeId: google.maps.MapTypeId.ROADMAP };
                var mapGoogle = new google.maps.Map(document.getElementById("mapGoogle"), myOptions);
                var mapBing = null;
                map = new Microsoft.Maps.Map(document.getElementById('mapBing'), {
                                credentials: 'KEY_BING_MAPS',
                                center: new Microsoft.Maps.Location(lat, lon), zoom: Zoom});
                document.getElementById("lat").value = lat;
                document.getElementById("lon").value = lon;
            });
        }
        else
        {
            alert("No dispone de geolocalización");
        }
    &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div id="inputs"&gt;
    &lt;input id="lat" /&gt;
    &lt;input id="lon" /&gt;
    &lt;/div&gt;
    &lt;div id="maps" width="100%" height="450px"&gt;
        &lt;div id="mapGoogle" style="margin:0; width:50%; height:450px; float:left;"&gt;&lt;/div&gt;
        &lt;div id="mapBing" style="margin:0; width:50%; height:450px; float:right; position: relative;"&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>

<?UMBRACO_MACRO idcode="1" macroAlias="ScriptsInLine"
titleembed="Google Maps" /> <?UMBRACO_MACRO idcode="2"
macroAlias="ScriptsInLine" titleembed="Bing Maps" /> 

<p>&nbsp;</p>
]]></description></item><item><title>#HowTo: Hacer mapping y no morir en el intento 5/7</title><link>http://setvalue.net/2012/8/19/howto-hacer-mapping-y-no-morir-en-el-intento-57/</link><pubDate>Sun, 19 Aug 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/8/19/howto-hacer-mapping-y-no-morir-en-el-intento-57/</guid><description><![CDATA[ 
<p>Siguiendo con el API de Bing Maps, vamos a incluir un fichero
KML, el cual nos va a permitir cargar nuestra propia cartografía
con los detalles o elementos que hayamos identificado o dibujado en
nuestro mapa.<!--more--></p>

<p>Con el mismo ejemplo, realizamos varios cambios, el primero es
indicar la versión del API 6.3, el <a
href="/2012/4/20/howto-hacer-mapping-y-no-morir-en-el-intento-(parte-iv)/">
anterior artículo</a> contiene referencias al API 7.0, y agregamos
al código unas nuevas variables, una de tipo VEShapeLayer y otra de
tipo VEShapeSourceSpecification, ésta última nos permitirá agregar
un kml, mediante un valor del enumerado VEDataType, que permite
importar un xml.</p>

<p>&nbsp;</p>

<pre class="brush: xml">
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Mapa KML&lt;/title&gt;
        &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/&gt;
        &lt;script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.3"&gt;&lt;/script&gt;
        &lt;script type="text/javascript"&gt;
            var map = null;
            function getMap()
            {
                map = new VEMap('myMap');
                map.LoadMap();
                var layer = new VEShapeLayer();
                var layerSpec = new VEShapeSourceSpecification(VEDataType.ImportXML, "TU_FICHERO_KML", layer);
                map.ImportShapeLayerData(layerSpec);
            }
        &lt;/script&gt;
    &lt;/head&gt;
    &lt;body onload="getMap();"&gt;
    &lt;div id='myMap' style="position:relative; width:400px; height:400px;"&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>

<p>Una vez sustituido el texto "TU_FICHERO_KML", por tu documento
KML. Al final obtenemos un mapa como el siguiente:</p>

<?UMBRACO_MACRO idcode="1" macroAlias="ScriptsInLine"
titleembed="" /> 

<p>Hay algunos intérpretes de GeoRSS, que haciendo transformaciones
desde KML a CSV, por XSL, y desde CSV, mediante el intérprete poder
visualizar los datos en el API.</p>

<p>Más información del API 6.3: <a
href="http://msdn.microsoft.com/en-us/library/bb429619"
target="_blank"
title="http://msdn.microsoft.com/en-us/library/bb429619">http://msdn.microsoft.com/en-us/library/bb429619</a>
e información y ejemplos del SDK 7.0 <a
href="http://www.bingmapsportal.com/isdk/ajaxv7" target="_blank"
title="http://www.bingmapsportal.com/isdk/ajaxv7">http://www.bingmapsportal.com/isdk/ajaxv7</a></p>
]]></description></item><item><title>#HowTo: Hacer mapping y no morir en el intento 4/7</title><link>http://setvalue.net/2012/8/14/howto-hacer-mapping-y-no-morir-en-el-intento-47/</link><pubDate>Tue, 14 Aug 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/8/14/howto-hacer-mapping-y-no-morir-en-el-intento-47/</guid><description><![CDATA[ 
<p>Microsoft también provee su propio API de mapas, Bing, el cual
posee funciones muy similares al de Google, y otras distintas
propias.</p>

<p>En definitiva, escoger uno u otro es una cuestión de gustos, o
de afinidad con un proveedor u otro. Por lo que, escojamos el que
escojamos, tendremos funciones típicas de APIs de mapas, y otras
relacionadas con las tecnologías que vende el proveedor. 
<!--more--></p>

<h5>Ficha técnica:</h5>

<ul>
<li>Bing Maps</li>

<li>Ajax 7.0</li>
</ul>

<p>Lo primero que hay que hacer, al igual que con los APIs de
Google, es registrarse como desarrollador de Bing. Para ello
accedemos <a
href="http://www.microsoft.com/maps/developers/web.aspx"
target="_blank">aquí</a>&nbsp; y nos registramos como
desarrolladores de Bing para así poder obtener la clave para
nuestro sitio web.</p>

<p><a
href="/media/2401/Windows-Live-Writer_db8cfa741631_14F34_image_4.png">
<img src="/media/2406/Windows-Live-Writer_db8cfa741631_14F34_image_thumb_1.png" width="507" height="322" alt="Bing Maps Developer Resources" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>En este <a
href="http://www.microsoft.com/en-us/showcase/details.aspx?uuid=7b4206fa-69e1-4c6e-ad10-c8685f5268c9"
 target="_blank">PowerPoint</a> se detalla el proceso completo de
registro y obtención de la key necesaria para nuestro sitio
web.</p>

<p>Nos logamos con nuestro live id, si no tienes una cuenta puedes
crearla desde <a
href="https://signup.live.com/signup.aspx?wa=wsignin1.0&amp;rpsnv=11&amp;ct=1341082842&amp;rver=6.1.6206.0&amp;wp=MBI_SSL_SHARED&amp;wreply=https:%2F%2Fhome.live.com%2F&amp;lc=3082&amp;id=251248&amp;cbcxt=hom&amp;mkt=es-ES&amp;bk=1341082843&amp;cru=https://login.live.com/login.srf%3fwa%3dwsignin1.0%26rpsnv%3d11%26rver%3d6.1.6206.0%26wp%3dMBI_SSL_SHARED%26wreply%3dhttps:%252F%252Fhome.live.com%252F%26lc%3d3082%26id%3d251248%26cbcxt%3dhom%26mkt%3des-ES&amp;uiflavor=web&amp;lic=1"
 target="_blank">aquí</a> no es necesario hacer una nueva cuenta de
correo puedes asociar tu propia dirección de correo actual.</p>

<p><a
href="/media/2411/Windows-Live-Writer_db8cfa741631_14F34_image_6.png">
<img src="/media/2416/Windows-Live-Writer_db8cfa741631_14F34_image_thumb_2.png" width="513" height="322" alt="Account Details" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>Una vez dentro del centro de cuentas de map, creamos una nueva
key, la cual introducimos el nombre que le queremos poner a nuestra
aplicación, la url y el tipo.</p>

<p><a
href="/media/2421/Windows-Live-Writer_db8cfa741631_14F34_image_11.png">
<img src="/media/2426/Windows-Live-Writer_db8cfa741631_14F34_image_thumb_4.png" width="516" height="320" alt="Create Keys" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>En este caso he seleccionado para desarrollador, que no te
permite exceder más de 125.000 sesiones o 500.000 transacciones en
periodos de 12 meses, lo cual para lo que nos ocupa es más que
suficiente.</p>

<p><a
href="/media/2431/Windows-Live-Writer_db8cfa741631_14F34_image_13.png">
<img src="/media/2436/Windows-Live-Writer_db8cfa741631_14F34_image_thumb_5.png" width="516" height="320" alt="Key" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>Una vez obtenida la clave empezamos con la "chicha". En el
siguiente ejemplo, introducimos nuestra key en las credenciales
donde sustituiremos 'TUCLAVE'.</p>

<pre class="brush: xml">
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Nuestro mapa&lt;/title&gt;
        &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/&gt;
        &lt;script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"&gt;&lt;/script&gt;
        &lt;script type="text/javascript"&gt;
            var map = null;
            function getMap()
            {
                map = new Microsoft.Maps.Map(document.getElementById('myMap'), {credentials: 'TUCLAVE'});
            }
        &lt;/script&gt;
    &lt;/head&gt;
    &lt;body onload="getMap();"&gt;
    &lt;div id='myMap' style="position:relative; width:400px; height:400px;"&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>

<p>Finalmente tenemos algo similar a esto:&nbsp;</p>

<?UMBRACO_MACRO idcode="1" macroAlias="ScriptsInLine"
titleembed=" " />
]]></description></item><item><title>#HowTo: Hacer mapping y no morir en el intento 3/7</title><link>http://setvalue.net/2012/8/9/howto-hacer-mapping-y-no-morir-en-el-intento-37/</link><pubDate>Thu, 09 Aug 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/8/9/howto-hacer-mapping-y-no-morir-en-el-intento-37/</guid><description><![CDATA[ 
<p>Una alternativa de Google a Maps, es emplear el API de Google
Earth, el cual le da la funcionalidad 3D a nuestro mapa. El
inconveniente de emplear este API es que requiere que el usuario
tenga instalado un complemento de Google Earth, y obligar al
usuario que se instale un plugin no suele ser bien recibido, bien
por el desconocimiento y el miedo a instalar algún malware, o bien,
porque no quiere tener un plugin que por lo pronto a él, únicamente
le sirve para nuestro sitio.<!--more--></p>

<p><a
href="/media/2116/Windows-Live-Writer_HowTo-Hacer-mapping-y-no-morir-en-el-int_25D_captura1.png">
<img src="/media/2121/Windows-Live-Writer_HowTo-Hacer-mapping-y-no-morir-en-el-int_25D_captura1_thumb.png" width="504" height="344" alt="Descargar complemento Google Earth" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>La implementación del API, es similar a la que hemos visto de
Google Maps, deberemos establecer nuestra clave del sitio web
obtenida en la consola de Google Code <a
href="https://code.google.com/apis/console/" target="_blank"
title="https://code.google.com/apis/console/">https://code.google.com/apis/console/</a>,
además debes configurar la variable <em>myKml</em> con la url del
KML que quieres integrar, y picar un código parecido a este.</p>

<pre class="brush: javascript">
   &lt;script src="http://www.google.com/jsapi?key=TU_CLAVE_API"&gt;&lt;/script&gt;
    &lt;script&gt;
        google.load("earth", "1");
        var ge = null;
        var myKml = 'URL_FICHERO_KML';
        function initMap() {
            google.earth.createInstance("map3d", initCallback, failureCallback);
        }
        function initCallback(object) {
            ge = object;
            ge.getWindow().setVisibility(true);
            var link = ge.createLink('');
            link.setHref(myKml);
            var nwLink = ge.createNetworkLink('');
            nwLink.set(link, true, true);
            ge.getFeatures().appendChild(nwLink);
        }
        function failureCallback(object) {
        }
    &lt;/script&gt;
    &lt;div id="map3d"&gt;&lt;/div&gt;
</pre>

<p>El resultado final es el siguiente:</p>

<?UMBRACO_MACRO idcode="1" macroAlias="ScriptsInLine"
titleembed="Google Earth" />
]]></description></item><item><title>#HowTo: Hacer mapping y no morir en el intento 2/7</title><link>http://setvalue.net/2012/8/4/howto-hacer-mapping-y-no-morir-en-el-intento-27/</link><pubDate>Sat, 04 Aug 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/8/4/howto-hacer-mapping-y-no-morir-en-el-intento-27/</guid><description><![CDATA[ 
<p>Siguiendo con el API de Google, vamos a integrar un fichero <a
href="http://es.wikipedia.org/wiki/KML" target="_blank">KML</a>, el
cual permitirá centrar el mapa en los elementos de la capa, con la
simbología que hayamos predefinido previamente.<!--more--></p>

<p>Implementamos un código similar a este, donde como siempre,
debemos establecer el código de nuestra API para el sitio web.
Cuando cargamos una cartografía en el API no es preciso establecer
en <em>myOptions</em> el zoom o las coordenadas, porque el propio
control se centrará en la cartografía que le proveamos (en el
script, <em>kmlLayer</em>).</p>

<pre class="brush: javascript">
        &lt;script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=TU_CODIGO_API&amp;sensor=false"&gt;&lt;/script&gt;
        &lt;script type="text/javascript"&gt;
            function initialize() {
            var myOptions = { mapTypeId: google.maps.MapTypeId.ROADMAP }
            var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
            var kmlLayer = new google.maps.KmlLayer('http://contoso/myLayer.kml');
            kmlLayer.setMap(map);
            }
        &lt;/script&gt;
</pre>

<?UMBRACO_MACRO idcode="1" macroAlias="ScriptsInLine"
titleembed=" " /> 

<p>Por supuesto, podemos agregar tantas capas cartográficas como
deseemos, pero lo más recomendable es intentar integrarlas todas en
un mismo documento KML, porque la implementación de muchas capas,
requiere un aumento en el tiempo de carga del control, ya que ha de
solicitar más ficheros, interpretarlos y por supuesto recalcular la
extensión del zoom.</p>
]]></description></item><item><title>#HowTo: Hacer mapping y no morir en el intento 1/7</title><link>http://setvalue.net/2012/8/1/howto-hacer-mapping-y-no-morir-en-el-intento-17/</link><pubDate>Wed, 01 Aug 2012 00:00:00 GMT</pubDate><guid>http://setvalue.net/2012/8/1/howto-hacer-mapping-y-no-morir-en-el-intento-17/</guid><description><![CDATA[ 
<p>Muchas veces, al acceder a una página de una empresa, encuentras
que tiene una dirección escrita de donde se encuentran sus
oficinas, y si quieres localizarla, has de entrar en otro sitio web
como Google Maps o Bing Maps, para ubicar esa empresa. Dadas las
facilidades que nos brindan gigantes como Google o Microsoft,
mejorar la imagen de nuestra empresa, proveyendo un simple mapa, es
algo que está en mano de los desarrolladores, y que implementando
unas pocas líneas de código, podemos mejorar la imagen de nuestra
empresa, a la par que evitamos que el usuario deje de visualizar
nuestro sitio web.<!--more--></p>

<p>Algunas veces por desconocimiento, y otras por comodidad, se
emplean APIs oficiales adaptadas por terceros, que evidentemente
unas veces cumplen con las expectativas y otras obligan a integrar
un componente muy complejo donde realmente se precisa algo mucho
más sencillo, y otras por el contrario no cumplen con todos los
requisitos y requiere al final más trabajo del que se estimaba en
un principio.</p>

<h5>Ficha técnica:</h5>

<ul>
<li>Google Maps API v3</li>

<li>HTML + Javascript</li>
</ul>

<p>Para utilizar los API de Google, es preciso, en primer lugar hay
que visitar el APIs Console de Google (<a
href="https://code.google.com/apis/console/" target="_blank"
title="https://code.google.com/apis/console/">https://code.google.com/apis/console/</a>),
y crear un nuevo proyecto.</p>

<p><a
href="/media/1801/Windows-Live-Writer_af4bb691e4e1_13152_captura1.png">
<img src="/media/1806/Windows-Live-Writer_af4bb691e4e1_13152_captura1_thumb.png" width="516" height="322" alt="APIs Console" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p><a
href="/media/1811/Windows-Live-Writer_af4bb691e4e1_13152_captura2.png">
<img src="/media/1816/Windows-Live-Writer_af4bb691e4e1_13152_captura2_thumb.png" width="516" height="322" alt="APIs Console II" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>Después debemos activar el servicio de <strong>Google Maps API
v3</strong> y aceptamos el contrato, Google advierte entre otras
cosas, de que sólo admite un máximo de 25000 peticiones al día, y
que no se puede utilizar con fines comerciales, lo más
recomendable, si no estás seguro es leerlo con detenimiento.</p>

<p><a
href="/media/1821/Windows-Live-Writer_af4bb691e4e1_13152_captura3.png">
<img src="/media/1826/Windows-Live-Writer_af4bb691e4e1_13152_captura3_thumb.png" width="516" height="323" alt="Browser key" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>Una vez aceptado vamos a la sección <strong>API Access</strong>
y creamos una nueva <strong>Browser key</strong> con la dns del
sitio web para el que estamos desarrollando el mapa.
<strong>Nota</strong>: Generalmente no es necesario generar una
clave específica para localhost (127.0.0.1), pero si lo fuera con
crear una nueva clave bien IP, bien browser, estaría listo.</p>

<p><a
href="/media/1831/Windows-Live-Writer_af4bb691e4e1_13152_captura4.png">
<img src="/media/1836/Windows-Live-Writer_af4bb691e4e1_13152_captura4_thumb.png" width="516" height="323" alt="API Access" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p><a
href="/media/1841/Windows-Live-Writer_af4bb691e4e1_13152_captura5.png">
<img src="/media/1846/Windows-Live-Writer_af4bb691e4e1_13152_captura5_thumb.png" width="516" height="323" alt="captura5" border="0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;"/></a></p>

<p>Por último implementamos un código similar a este, sustituyendo
TU_CODIGO_API y TRUE_O_FALSE, por el código de la API y con un
verdadero o falso.</p>

<pre class="brush: xml">
&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta name="viewport" content="initial-scale=1.0, user-scalable=no" /&gt;
        &lt;style type="text/css"&gt;
            html { height: 100% }
            body { height: 100%; margin: 0; padding: 0 }
            #map_canvas { height: 100% }
        &lt;/style&gt;
        &lt;script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key=TU_CODIGO_API&amp;sensor=TRUE_O_FALSE"&gt;&lt;/script&gt;
        &lt;script type="text/javascript"&gt;
            function initialize()
            {
                var myOptions = {
                    center: new google.maps.LatLng(40.39, -3.41),
                    zoom: 8,
                    mapTypeId: google.maps.MapTypeId.ROADMAP };
                var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
            }    
        &lt;/script&gt;
    &lt;/head&gt;
    &lt;body onload="initialize()"&gt;
        &lt;div id="map_canvas" style="width:100%; height:100%"&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>

<?UMBRACO_MACRO idcode="1" macroAlias="ScriptsInLine"
titleembed="Google Maps" />
]]></description></item></channel></rss>
