Esta es la continuación del artículo “Creando un control de usuario reutilizable : El mapa de México en XAML en acción – Parte I”.

Crear un control de usuario reutilizable en Silverlight 2 es una tarea muy sencilla y más si organizamos e identificamos bien los pasos a seguir.

Actualmente en nuestra solución de Silverlight tenemos varios archivos entre ellos Page.xaml y Mexico.xaml.  Page.xaml es el archivo que incluye por default la plantilla de proyectos Silverlight 2 para Visual Studio .NET 2008.  Este archivo representa una página en nuestra aplicación; podemos tener más de 1 página por proyecto no obstante la página que estaremos viendo en la aplicación es aquella que asignes en la propiedad RootVisual lo cual podemos comprobar si abrimos el archivo de code-behind App.xaml.cs:

public App()
{
    this.Startup += this.Application_Startup;
    this.Exit += this.Application_Exit;
    this.UnhandledException += this.Application_UnhandledException;
 
    InitializeComponent();
}
 
private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = new Page();
}

El fragmento de código muestra el comportamiento predeterminado de una aplicación Silverlight 2: la aplicación mostrará una instancia de Page.xaml, la cual no es mas que una clase que extiende la clase base UserControl; esta instancia es asignada a la propiedad RootVisual de la aplicación y todos felices y contentos.

Ahora bien, queremos crear nuestro control del mapa de México como un control reutilizable para poder usarlo en Page.xaml, Page2.xaml o CualquierOtroArchivo.xaml.

El siguiente paso será importar el namespace del proyecto en la declaración del UserControl de Page.xaml, asignándole un alias (por ejemplo “mapas”) para poder nombrar las nuevas instancias del mapa de una manera sencilla:

<UserControl x:Class="PortalMexico.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:mapas="clr-namespace:Mexico"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <mapas:Mexico x:Name="mapaMexico" />
    </Grid>
</UserControl>

Tenemos nuestra primer instancia del control de usuario en Page.xaml ! No obstante, el mapa del control de usuario tiene un tamaño superior al tamaño de Page.xaml.  Esto es fácilmente corregido si eliminamos las declaraciones fijas de tamaño que tiene Mexico.xaml (Width y Height) en los Canvas LayoutRoot y MapaMexico y en la declaración del UserControl.  Además es buen momento para quitar la declaración de color azul del Canvas MapaMexico para que el mapa sea totalmente transparente cuando lo coloquemos en una página:

Estos últimos pasos nos permitirán declarar el mapa de México como cualquier otro elemento o control de Silverlight 2.  La siguiente figura muestra cómo se dibuja el nuevo control de usuario en el diseñador de Visual Studio .NET 2008:

Ya que el mapa de México lo podemos tratar como cualquier otro elemento o control de Silverlight, apliquemos una transformación de escala (ScaleTransform) para manipular su tamaño y mostrarlo un poco mejor en la página:

<mapas:Mexico x:Name="mapaMexico">
    <mapas:Mexico.RenderTransform>
        <ScaleTransform ScaleX="0.8" ScaleY="0.8" />
    </mapas:Mexico.RenderTransform>
</mapas:Mexico>

Es buen momento para diseñar la página de nuestra aplicación (Page.xaml).  Al diseño agregaremos 2 columnas y 3 filas ya sea manualmente en el archivo Page.xaml o por medio de Expression Blend.  La primer fila nos servirá para colocar el título de la aplicación, la segunda para colocar el mapa de México y la tercera para indicar cuál estado ha sido seleccionado.

El siguiente fragmento de código muestra las modificaciones realizadas a Page.xaml y la figura la aplicación ejecutándose en el navegador:

<UserControl x:Class="PortalMexico.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:mapas="clr-namespace:Mexico"
    Width="800" Height="600">
    <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="80" />
            <RowDefinition />
            <RowDefinition Height="0.5*" />
        </Grid.RowDefinitions>
        <TextBlock Text="Portal de México" FontSize="60" Grid.ColumnSpan="2">
            <TextBlock.Foreground>
                <LinearGradientBrush>
                    <GradientStop Color="Green" Offset="0.1" />
                    <GradientStop Color="Red" Offset="1" />
                </LinearGradientBrush>
            </TextBlock.Foreground>
        </TextBlock>
        <mapas:Mexico x:Name="mapaMexico" 
                      VerticalAlignment="Top" 
                      HorizontalAlignment="Left" 
                      Margin="10"
                      Grid.Row="1"
                      >
            <mapas:Mexico.RenderTransform>
                <ScaleTransform ScaleX="0.8" ScaleY="0.8" />
            </mapas:Mexico.RenderTransform>
        </mapas:Mexico>
        <TextBlock x:Name="tbEstado" FontSize="40" Grid.Row="2" Margin="10" Text="[Estado]" />
    </Grid>
</UserControl>

Al ejecutar la aplicación nos damos cuenta que el mapa de México continúa sirviendo, pero aún necesitamos una manera de avisarle a la página Page.xaml que un estado ha sido seleccionado!  Esto lo haremos creando un evento en Mexico.xaml.cs.

Primeramente necesitamos definir una nueva clase de Argumentos.  Esta clase incluirá una propiedad llamada Estado la cual indicará qué estado ha sido seleccionado por el usuario:

public class MexicoEventArgs : EventArgs
{
    public string Estado { get; set; }
}

Posteriormente un nuevo delegado que sea la base para el evento que estamos buscando disparar.  Este nuevo delegado usará dos parámetros: object y MexicoEventArgs; siendo el primero el objeto (instancia del mapa) que ha disparado el evento y el segundo los argumentos del evento.  Sin sorpresa alguna vemos que estoy usando MexicoEventArgs ya que precisamente usaremos su propiedad Estado para indicar el estado seleccionado.

public delegate void MexicoEventHandler (object sender, MexicoEventArgs e);

Ahora bien, declaremos el evento dentro de la clase Mexico:  Este evento estará basado en MexicoEventHandler (el nuevo delegado):

public event MexicoEventHandler EstadoSeleccionado;

Finalmente, vamos a disparar este evento en el clic de cada estado en vez de estar mostrando la ventana de alerta:

void estado_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    if (EstadoSeleccionado != null)
    {
        EstadoSeleccionado(this, 
            new MexicoEventArgs() { Estado = ((Canvas)sender).Name });
    }
}

Este último fragmento de código  demuestra varias cosas:

  1. Estamos obteniendo el nombre del estado haciendo un cast a Canvas de la propiedad sender para determinar qué estado fue seleccionado y
  2. Estamos creando una nueva instancia de MexicoEventArgs a la cual le estaremos asignando en su propiedad Estado el nombre del estado obtenido del punto número 1.
  3. Estamos usando una nueva característica del .NET Framework 3.5 la cual es “Object Initializer” o “Inicializadores de Objectos”.  Con ella podemos en la misma línea de código establecer las propiedades públicas que necesitemos sin necesidad de contar con un constructor adecuado para ello.

Perfecto!  Por último, necesitamos “inscribirnos” al evento EstadoSeleccionado en Page.xaml, para poder establecer el texto del TextBlock tbEstado con el nombre del estado seleccionado:

public Page()
{
    InitializeComponent();
 
    mapaMexico.EstadoSeleccionado += (o, args) => tbEstado.Text = args.Estado;
}

Estamos usando una expresión Lambda, en vez de la declaración del método en el cuerpo de la clase.  Las expresiones Lambda son un tipo especial de Métodos Anónimos (concepto introducido desde la versión 2.0 del .NET Framework) y su sintaxis incluye el operador => que significa “se va a”.  Es notable cómo el compilador conoce de antemano el tipo de los parámetros de la expresión Lambda sin necesidad de explícitamente indicarlos (o es Object, args es MexicoEventArgs).

Listo!  Hemos creado nuestro primer control de usuario y lo hemos utilizado en Page.xaml. Si ejecutamos la aplicación podemos darnos que el comportamiento esperado es correcto:  al hacer clic en un estado del control de usuario mapaMexico, el TextBlock tbEstado cambia su texto correctamente: (la propiedad ShowGridLines del Grid en Page.xaml ha sido establecida a false en la siguiente figura)

Puedes descargar el código fuente de este artículo en la sección Contenido de la La Liga Silverlight en esta dirección.

En el siguiente artículo veremos cómo extender la funcionalidad de nuestra aplicación para crear un portal que muestre fotografías relacionadas con el estado seleccionado del control de usuario!