Controles de usuario

Una de las tareas comunes a llevar a cabo por un desarrollador durante el ciclo de desarrollo de una aplicación, ya sea una aplicación web o de escritorio, es la creación de controles personalizados. Estos controles de usuario deben permitir reflejar necesidades específicas de nuestra aplicación de modo que facilite la interacción con el usuario o presente información de una manera sencilla y que resulte útil.

Para esta tarea Silverlight nos expone un tipo UserControl que nos hará accesibles todas las bondades de la tecnología, permitiéndonos hacer uso de animaciones, personalizando el aspecto visual del mismo o simplemente preparándolo para el uso de éste con las características de enlace a datos de Silverlight.

Los controles de usuario en Silverlight aportan al desarrollador un modo sencillo de encapsular funcionalidad común en nuestras aplicaciones.
Además, nos proporcionan un modo de crear nuevos controles con una funcionalidad y un aspecto totalmente adaptado a nuestras necesidades o las de nuestros usuarios.

Para entender la importancia de estos controles llamaremos la atención sobre el siguiente código XAML:

 <UserControl 
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="DemoSL.Page" Width="640"Height="480">
<Grid/>
</UserControl>

El tipo raíz del que partimos a la hora de desarrollar una nueva aplicación Silverlight es UserControl

Es por este motivo que los controles de usuario cobran una especial importancia en nuestros desarrollos.

Los controles de usuario en Silverlight nos garantizan una separación clara entre elementos de presentación (código en XAML) y funcionalidad (código en .NET).
Esta separación entre archivos que componen la misma clase parcial hacen de los UserControls una potente herramienta de encapsulación.

Los UserControls son muy fáciles de mantener y versionar dada esta separación entre elementos de presentación y funcionalidad. Además, podremos hacer uso de todas las ventajas que nos proporciona la tecnología como el uso de estilos o plantillas como parte inherente de nuestros controles.

Nuestro primer UserControl

Para ilustrar la creación de UserControls en Silverlight crearemos una pequeña aplicación que nos permita hacer apuestas y mantener un detalle de las realizadas hasta el momento.

Nuestra aplicación de ejemplo en ejecución

Para llevar a cabo el desarrollo de esta aplicación añadiremos un nuevo control de usuario a nuestro proyecto de aplicación Silverlight y le daremos un nombre (TipControl).

Comenzaremos con los controles de formarán parte de nuestro UserControl:

 <UserControl x:Class="UserControls.TipControl" 
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls"Width="340">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="Nombre:">
</TextBlock>
<TextBox VerticalAlignment="Center"x:Name="Name" Width="120">
</TextBox>
<TextBlock VerticalAlignment="Center" Text="Cantidad">
</TextBlock>
<TextBox VerticalAlignment="Center"x:Name="Amount" Width="60">
</TextBox>
<TextBlock VerticalAlignment="Center" Text="€">
</TextBlock>
<Button VerticalAlignment="Center" Margin="6,0,0,0"
Content="Apostar" Click="Button_Click" />
</StackPanel>
</Grid>
</UserControl>

XAML correspondiente al control de usuario TipControl

Una vez realizada esta tarea pasaremos a añadir la lógica necesaria para que nuestro control pueda notificar a la interfaz de usuario de las apuestas que se van a realizar.
Para almacenar los valores de nuestro control (Nombre y cantidad apostada) y poder alamacenarlos haremos uso de las DependencyProperties.

Las Dependency Properties o Propiedades de Dependencia extienden la funcionalidad de una propiedad del CLR.
Las propiedades de dependensia implementan un patrón Observer haciendo uso de callbacks. Esto nos permitirá que los cambios realizados en los valores de estas propiedades se vean reflejados en la interfaz de usuario de forma automática.

Además las propiedades de dependencia también nos proporcionan una manera de inicializar nuestras propiedades con un valor por defecto, además de controlar el acceso a los valores de la misma.

Adicionalmente las Dependency Properties habilitan el uso de Estilos, Plantillas y Animaciones, por lo que nuestro control podrá hacer uso de toda la potencia de Silverlight.

Un ejemplo de cómo asignar un valor a una propiedad de dependencia:

 <Canvas Background="Red" Width="100" Height="30"/> 

A continuación se muestra cómo hacer uso de las Dependency Properties en el desarrollo de nuestro control de usuario:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace UserControls
{
public partial class TipControl : UserControl
{
public delegate void CustomEventArgs(object sender, TipEventArgs e);
public event CustomEventArgs InitTransaction;
public TipControl()
{
InitializeComponent();
}

#region TipAmmount (Dependency Property)
public static readonly DependencyProperty
TipAmmountProperty = DependencyProperty.Register("TipAmmount",
typeof(double), typeof(TipControl), new PropertyMetadata(0.0,
new PropertyChangedCallback(TipAmmountChanged)) );
public double TipAmmount
{
get { return (double)GetValue(TipAmmountProperty); }
set { SetValue(TipAmmountProperty, value); }
}
private static void TipAmmountChanged(DependencyObject
sender, DependencyPropertyChangedEventArgs args)
{
TipControl control = sender as TipControl;
if (control != null)
{
control.Amount.Text = args.NewValue.ToString();
}
}
#endregion

#region TipName (Dependency Property)
public static readonly DependencyProperty TipNameProperty =
DependencyProperty.Register("TipName", typeof(String),
typeof(TipControl), new PropertyMetadata(String.Empty,
new PropertyChangedCallback(TipNameChanged)));
public String TipName
{
get { return (String)GetValue(TipNameProperty);
}
set { SetValue(TipNameProperty, value);
}
}
private static void TipNameChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs args) { TipControl control
= sender as TipControl;
if (control != null)
{
control.Name.Text = args.NewValue.ToString();
}
}
#endregion
private void Button_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(Name.Text))
{
TipName = Name.Text;
if (!String.IsNullOrEmpty(Amount.Text))
{
TipAmmount = Double.Parse(Amount.Text);
if (InitTransaction != null)
{
InitTransaction(this, new TipEventArgs(TipName, TipAmmount));
}
}
}
}
}
}

Además haremos uso de una clase auxiliar derivada de EventArgs para crear un evento personalizado al que poder suscribirse y que nos proporcione la información necesaria para poder responder adecuadamente a los mecanismos de interacción lanzados por el usuario.

 public class TipEventArgs: EventArgs 
{
public TipEventArgs(String name, double tip)
{
this.Name = name;
this.Tip = tip;
}
private string name;
public string Name
{
get { return name;
}
set { name = value;
}
}
private double tip;
public double Tip
{
get { return tip;
}
set { tip = value;
}
}
}

Tienes disponible el código fuente de este ejemplo en el archivo del curso UserControls.zip, para poder examinarlo con más detalle.