Antes de ver las novedades aparecidas en la beta de Silverlight 5, debemos tener en cuenta que estamos hablando de una versión de prueba, por lo que no es recomendable desplegarla en una máquina de producción. Es mucho mejor poder disponer de una máquina física sobre la que instalar las betas, por rendimiento y capacidad; pero si no, siempre podemos montar una máquina virtual para ello, aunque no disfrutaremos de las capacidades 3D, ya que éstas se basan en un acceso directo a la GPU.
Para empezar, descargue e instale la beta de Silverlight 5 y las Silverlight 5 Beta Tools for Visual Studio SP1 desde la página oficial, donde adicionalmente encontrará otras descargas muy interesantes como Expression Blend Preview for Silverlight 5, Silverlight 5 Beta SDK y WCF RIA Services for Silverlight 5 Beta.
Entrando en materia, a continuación vamos a ver en detalle y de forma ejemplificada algunas de las principales novedades que ofrece la beta de Silverlight 5.
Anteriormente, la forma de realizar una depuración de los enlaces a datos se basaba en implementar un conversor ad-hoc y poner puntos de interrupción (breakpoints) dentro del mismo.
Veamos cómo lograr esto de manera más efectiva en Silverlight 5 a través de un ejemplo.
Para ello, crearemos una nueva aplicación Silverlight 5, a la que añadiremos dos clases: Cursos? Model, que albergará el contenido del curso (en este caso, una simple propiedad de tipo string), y Cursos_ ViewModel, que contendrá una propiedad de tipo CursosModel. En el code-behind de MainPage. xaml declararemos una variable de tipo Cursos_ ViewModel, que asignaremos al DataContext de MainPage en su constructor. El código puede verse en el listado 1.
Listado 1
(CursosModel.cs)
public class CursosModel
{
public String NombreDelCurso { get; set; }
}
(Cursos_ViewModel.cs)
public class Cursos_ViewModel
{
public CursosModel CursoSeleccionado
{
get; set;
}
public Cursos_ViewModel()
{
CursoSeleccionado = new CursosModel() {
NombreDelCurso = "Introducción a Silverlight 5"
};
}
}
(MainPage.xaml.cs)
public partial class MainPage : UserControl
{
private Cursos_ViewModel Cursos = new Cursos_ViewModel();
public MainPage()
{
InitializeComponent();
this.DataContext = Cursos;
}
}
Seguidamente, en el XAML de MainPage, definimos un StackPanel que contiene un TextBlock, al que enlazamos al campo NombreDel? Curso (ver listado 2).
Listado 2
<Grid x:Name="LayoutRoot"
Background="White">
<StackPanel>
<TextBlock Text=
"{Binding NombreDelCurso}">
</TextBlock>
</StackPanel>
</Grid>
Si ubicamos un punto de interrupción en la línea de XAML que contiene el enlace a datos, veremos que se marca un punto de depuración y se resalta el binding en sí. Y si ejecutamos la aplicación, veremos que ésta se detiene exactamente en este punto y nos indica, además, un error, como se puede apreciar en la figura 1. Puede observar cómo el punto de depuración es resaltado de forma similar a cuando depuramos código, y ofrece una información muy interesante sobre el estado del binding (BindingState), en la que podemos observar la acción que se está realizando, "Updating Target" (actualizando el destino), así como los detalles del error, donde podemos ver que Silverlight no ha hallado la propiedad; además de todo ello, también se nos muestra el FinalSource del enlace a datos: ahora es cuando nos damos cuenta de que nos hemos olvidado de mencionar el objeto que contiene a esa propiedad, con lo que el binding correcto debería ser:
Text="{Binding CursoSeleccionado.
NombreDelCurso}"

Como ejemplo, agregaremos al proyecto creado anteriormente un archivo de tipo .WAV, en modo "contenido", como puede observarse en el código que acompaña a este artículo. En el código, cargaremos el archivo mediante el método GetResourceStream del objeto Application, para continuar pasándole el Stream obtenido al método FromStream de la clase SoundEffect, como puede apreciarse en el listado 3.
Listado 3
private void ReproducirSonido() {
SoundEffect Sonido;
var SonidoStream = Application.GetResourceStream(
new Uri("beep.wav", UriKind.RelativeOrAbsolute));
Sonido = SoundEffect.FromStream(SonidoStream.Stream);
SoundEffectInstance instance = Sonido.CreateInstance();
instance.Play();
}
Otro detalle interesante es que esta clase nos permite controlar varias características del sonido a reproducir: volumen, pitch (la frecuencia, que determina si un sonido es agudo o grave) y pan (el balance del sonido, que emula el dial de los equipos de audio y nos permite ajustar la potencia de los ejes izquierdo y derecho del mismo). A las correspondientes propiedades pueden asignársele valores en los rangos que se indican en la tabla 1.

Un ejemplo práctico de aplicación de estas propiedades sería disponer de un sonido con un volumen bajo ejecutándose permanentemente, como suelen hacer los sonidos de fondo. Para ello, asociaremos a un botón una reproducción de sonido asociándole true a la propiedad IsLooped, un valor bajo a Volume y un pitch también bajo, ya que se trata de un sonido "de fondo". A un segundo botón asociaremos la reproducción de otro sonido. Todo ello podemos verlo en el listado 4.
Listado 4
SoundEffect SonidoDeFondo;
SoundEffect SonidoClick;
private void InicializarSonidos()
{
var SonidoStream = Application.GetResourceStream(
new Uri("beep.wav", UriKind.RelativeOrAbsolute));
SonidoClick = SoundEffect.FromStream(SonidoStream.Stream);
var SonidoStream2 = Application.GetResourceStream(
new Uri("Loop Banjo.wav", UriKind.RelativeOrAbsolute));
SonidoDeFondo = SoundEffect.FromStream(SonidoStream2.Stream);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
InicializarSonidos();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
SoundEffectInstance instance = SonidoClick.CreateInstance();
instance.Play();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
SoundEffectInstance instance = SonidoDeFondo.CreateInstance();
instance.IsLooped = true;
instance.Pitch = -0.5f;
instance.Volume = 0.2f;
instance.Play();
}
Como observación, si el botón button2 se pulsa varias veces, estaremos iniciando el sonido de fondo repetidamente, creando diferentes instancias del mismo efecto de sonido.
Como ejemplo, añadiremos al proyecto un MediaElement asociado a la reproducción automática de un vídeo, seguido de un control tipo slider, al que asignaremos como valor mínimo 0.25 y máximo 2, y que utilizaremos como origen de un enlace a datos desde el control MediaElement; concretamente, a la propiedad PlaybackRate del mismo. Ello hará que podamos cambiar la velocidad del video sobre la marcha directamente desde la interfaz de usuario. El código necesario puede verse en la figura 2.


Las mejoras en la disposición del texto nos permiten ahora construir layouts similares a los de una revista, ya sea permitiendo ubicarlo en múltiples columnas en las que el texto fluya, definir en detalle el espaciado del mismo, y a la vez, mediante Pixel Snapping, presentarlo de una forma más clara. También disponemos de soporte ampliado para fuentes Open Type, y una optimización significativa en cuanto a su rendimiento.
Pero veamos cómo funciona todo esto en la práctica. Por un lado, disponemos de una nueva propiedad, Run.CharacterSpacing, en los controles TextBlock y RichTextBlock, que nos ofrece control sobre el espaciado de los caracteres contenidos en dicho elemento. Un ejemplo se muestra en el listado 6, cuyo resultado se puede apreciar en la figura 4.
Lisado 6
<StackPanel>
<TextBlock FontSize="18" CharacterSpacing="250"
Text="Three Rings for the Elven-kings under the sky," />
<RichTextBox FontSize="18" CharacterSpacing="200" BorderBrush="{x:Null}"
BorderThickness="0">
<Paragraph>Seven for the Dwarf-lords in their halls of stone,</Paragraph>
</RichTextBox>
<TextBlock FontSize="18">
<Run CharacterSpacing="150"
Text="Nine for Mortal Men doomed to die," />
</TextBlock>
<TextBlock FontSize="18">
<Run CharacterSpacing="100"
Text="One for the Dark Lord on his dark throne" />
</TextBlock>
<TextBlock FontSize="18">
<Run CharacterSpacing="50"
Text="In the Land of Mordor where the Shadows lie." />
</TextBlock>
<TextBlock FontSize="18">
<Run CharacterSpacing="0"
Text="One Ring to rule them all, One Ring to find them," />
</TextBlock>
<TextBlock FontSize="18">
<Run CharacterSpacing="-50"
Text="One Ring to bring them all and in the silver light bind them" />
</TextBlock>
<TextBlock FontSize="18">
<Run CharacterSpacing="-100"
Text="In the Land of Developers where the Shadows lie." />
</TextBlock>
</StackPanel>

Con respecto a las capacidades renovadas en lo que a la disposición del texto se refiere, tenemos el nuevo control RichTextBoxOverflow, que nos permite enlazar diferentes contenedores de texto de manera que éste fluya hacia otro elemento cuando no quepa más en el mismo. Con ello podemos conseguir disponer el texto en múltiples columnas que se ajustan dinámicamente al espacio disponible. Este control puede asociarse únicamente a un control Rich-TextBox, que encadenará su contenido mediante una
nueva propiedad, OverflowContentTarget; si el texto no cabe en el control "normal", el texto sobrante se presentará en el control enlazado con OverflowContentTarget. En el listado 7, se dispone de un control RichTextBox llamado RTB1, cuya propiedad Overflow? ContentTarget se enlaza mediante un binding a un control RichTextBoxOverflow de nombre RTBOverflow2; éste, a su vez, canaliza su overflow hacia un segundo RichTextBoxOverflow de nombre RTBOverflow3.
Listado 7
<RichTextBox x:Name="RTB1"
OverflowContentTarget="{Binding ElementName=RTBOverflow2}"
HorizontalAlignment="Stretch"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled"
BorderThickness="0" TextAlignment="Justify">
<Paragraph> Lorem ipsum ... </Paragraph>
<Paragraph>...</Paragraph>
</RichTextBox>
<RichTextBoxOverflow x:Name="RTBOverflow2"
OverflowContentTarget="{Binding ElementName=RTBOverflow3}"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled"
BorderThickness="0"
Grid.Column="1"
/>
<RichTextBoxOverflow x:Name="RTBOverflow3"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled"
BorderThickness="0"
Grid.Column="2"
/>
El resultado podemos verlo en las figuras 5 y 6. Recomendamos al lector que descargue el ejemplo, disponible tanto en la web de dotNetManía como en mi blog [2], pruebe a ajustar el tamaño de la ventana del navegador, y vea como fluye el texto, ajustándose de forma dinámica y muy rápidamente a los cambios de su tamaño.
