Featured Posts

Diplomado de desarrollo de aplicaciones para Windows Phone 7.1–LatinoaméricaDiplomado de desarrollo de aplicaciones para Windows... Los invito cordialmente a que se inscriban al Diplomado de desarrollo de aplicaciones para Windows Phone 7.1, impulsado por Microsoft México para todo el mundo!  Completamente...

Readmore

Nueva carrera en Microsoft Virtual Academy: Windows Phone 7.1 (Mango)Nueva carrera en Microsoft Virtual Academy: Windows... Tengo el gusto de darles a conocer la disponibilidad de una nueva carrera en el Microsoft Virtual Academy (MVA): La carrera de Windows Phone 7.1 Mango. Esta es la descripción...

Readmore

Nueva carrera en Microsoft Virtual Academy: Silverlight 4Nueva carrera en Microsoft Virtual Academy: Silverlight... ¿Eres estudiante de Microsoft Virtual Academy (MVA)? Tengo el gusto de anunciarles oficialmente el lanzamiento de la nueva carrera de Silverlight 4 para esta plataforma educativa. La...

Readmore

Rodrigo Díaz Concha Rss

Silverlight Tour – Próximas fechas en México

Posted on : 30-11-2009 | By : Rodrigo | In : Cursos, Silverlight 3, Silverlight 4, Silverlight Tour Workshop

Tags: , , , , , , , , ,

2

Silverlight Tour: El Curso de SilverlightSi deseas aprender de una manera profunda Silverlight por parte de los Expertos, les invito que se registren a alguna de las fechas que ya tenemos programadas en la República Mexicana.  El Silverlight Tour es un curso de más de 27 horas (3 días) acerca de Silverlight 3 y Silverlight 4 Beta el cual se ha impartido y se imparte al rededor del mundo.  Además, es el primer curso en estar actualizado a la versión Silverlight 4 Beta, con material completamente en Español.

En el primer trimestre del 2010 regresa el Silverlight Tour a México en las siguientes ciudades y fechas:

El registro ya está abierto.  ¿Estás listo para Aprender Silverlight?

¡Los esperamos!

Silverlight 4 Beta – Nuevas propiedades de Binding

Posted on : 29-11-2009 | By : Rodrigo | In : Silverlight 4

Tags: , , , , ,

1

En Silverlight 4 se agregan algunas propiedades para el Atado de Datos que nos permiten tener más control acerca de cómo se están enlazando los datos en los controles destino.  Las propiedades nuevas son las siguientes:

FallBackValue

Determina un valor predeterminado para los casos en los que el Atado de Datos falle o sea inválido.

TargetNullValue

Indica el valor que tendrá el control destino cuando el dato que se está enlazando es nulo.

StringFormat

Con esta propiedad podemos determinar el formato que deseemos que tenga la cadena cuando se está atando a un control.  Antes de esta propiedad prácticamente estábamos obligados a crear un Convertidor e invocarlo por medio de la propiedad Converter.  Ahora esto ya no es necesario para los escenarios en donde necesitamos formatear una cadena (una fecha por ejemplo).

Ejemplo

Dada la siguiente fuente de datos:

<local:Persona x:Key="persona1" Nombre="John" Apellido="Lennon" FechaNacimiento="10/09/1940" />

El siguiente código Xaml muestra las tres propiedades anteriormente explicadas en acción.  En el primer TextBox el atado es correcto y no hay ningún cambio.  En el segudo TextBox se establece la propiedad FallBackValue, y ya que la propiedad Apellidos no existe en la fuente de datos este será usado.  El tercer TextBox usa la propiedad TargetNullValue para poner el valor ‘No’ cuando la propiedad Email sea nula.  En el último TextBox se utiliza la propiedad StringFormat para formatear la fecha de manera adecuada (nombre del mes, día y año).

<TextBox Text="{Binding Nombre}" />
<TextBox Text="{Binding Apellidos, FallbackValue='ND'}" />
<TextBox Text="{Binding Email, TargetNullValue='No'}" />
<TextBox Text="{Binding FechaNacimiento, StringFormat='MMMM dd, yyyy'}" />

El resultado:

image

Puedes descargar el código aquí.

Silverlight 4 Beta – Soporte para Arrastrar y Soltar en Mac OS X

Posted on : 26-11-2009 | By : Rodrigo | In : Silverlight 4

Tags: , , , , , , , ,

7

En el artículo anterior vimos que una de las características nuevas de Silverlight 4 es su capacidad de recibir archivos que se estén arrastrando desde afuera de la aplicación.  No obstante, en Mac OS X es diferente el mecanismo para lograr esta funcionalidad ya que en ese sistema operativo, una acción de arrastrar y soltar no le avisa al API de Silverlight de lo ocurrido, a diferencia de lo que sucede en Windows.  En este artículo veremos la alternativa para implementar esta característica en Mac OS X.  Cabe mencionar que este mecanismo funcionará en Safari, mientras que en FireFox sea probable que no debido a la diferencia de arquitectura de este último en el sistema operativo de Apple.

El proyecto

Usaremos el proyecto anterior.

Modificaciones

Para implementar la funcionalidad de Arrastrar y Soltar en Mac OS X necesitaremos hacerlo a través de funciones de Javascript que le avisen al plugin que una acción de arrastrar-soltar ha sucedido.  Las funciones de Javascript deberán manejar alguno de los siguientes eventos del elemento <OBJECT> o <EMBED> (según lo que hayamos usado para instanciar Silverlight, en nuestro caso hemos usado <OBJECT>)

  • ondragenter
  • ondragleave
  • ondragover
  • ondrop

Según la documentación de Web Kit, de los eventos anteriores debemos como mínimo manejar ondragover, y dentro de él ejecutar preventDefault() en el objeto de evento.  Si no hacemos esto no recibiremos ninguno de los cuatro eventos anteriores.  Cabe mencionar que esto no está explicado en el SDK de Silverlight 4 Beta pero probablemente esto sea aclarado en sus futuras versiones.

Aclarado lo anterior, realizaremos las siguientes modificaciones al elemento <OBJECT> con el que estamos instanciando Silverlight:

<object id="sl" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%" ondragover="ManejaDragOver(event)" ondrop="ManejaDrop(event)">
            // los parámetros quedan igual
        </object>

Como podemos observar, las modificaciones son:

- Estamos agregando el atributo id, el cual nos permitirá invocar desde script al elemento.

- Estamos manejando ondragover a través de la función ManejaDragOver. Asimismo a la función le estamos pasando el objeto event el cual tiene información del evento.

- Estamos manejando ondrop a través de la función ManejaDrop. Asimismo a la función le estamos pasando el objeto event el cual tiene información del evento.

Ahora bien, debemos implementar las funciones recién explicadas.  Esto lo haremos en el bloque de <script> que ya tenemos presente en la página:

//El manejo de ondragover y la ejecución de preventDefault()
        //son requeridos según la documentación de WebKit localizada en:
        //http://tuvix.apple.com/mac/library/documentation/AppleApplications/Conceptual/SafariJSProgTopics/Tasks/DragAndDrop.html
        function ManejaDragOver(oEvent) {
            oEvent.preventDefault();
        }

        function ManejaDrop(evento) {

            // Previene las acciones por default en el DOM
            evento.preventDefault();
            // Obtiene el objeto de Silverlight
            var sl = document.getElementById("sl");
            // Delega el evento a Silverlight
            var manejado = sl.dragDrop(evento);

            // Si ha sido manejado, evita la propagación del evento en el DOM
            if (manejado) evento.stopPropagation();
            
        }

ondragover

La función ManejaDragOver únicamente hace una llamada a preventDefault() sobre el objeto de evento.  Esto para permitir que el resto de eventos puedan ser recibidos (ver en la parte de arriba la liga a la documentación de Web Kit). 

ondrop

La función ManejaDrop también hace una llamada a preventDefault() para evitar las que las acciones por default sucedan en el DOM (ya que nosotros queremos hacer nuestro propio manejo).  Posteriormente obtiene el objeto de Silverlight y ejecuta su función dragDrop() pasando como parámetro el objeto de evento.  Finalmente validamos que si el evento ha sido manejado, evite la propagación del evento en el DOM.

Resultado

Como podemos apreciar en las siguientes figuras, al arrastrar un archivo desde afuera de la aplicación Silverlight en Mac OS X, y después soltarlo el archivo se agregará al ListBox y comenzará su reproducción automáticamente, justo como en el artículo anterior.

image 

image 

Resumen

Para implementar la funcionalidad de Arrastrar y Soltar en Mac OS X debemos hacerlo a través de script en el DOM debido a las diferencias en la arquitectura de las plataformas; no obstante es pefectamente posible.  En este artículo detallamos los pasos necesarios para hacerlo.  Esto nos permitirá ofrecer a nuestros usuarios una experiencia estándar en el uso de los aplicativos Silverlight que construímos.

Puedes descargar el código aquí.

Puedes ver el demo en vivo aquí.  Requiere el runtime Silverlight 4, Apple Mac OS X y Safari.

Silverlight 4 Beta – Soporte para Arrastrar y Soltar

Posted on : 25-11-2009 | By : Rodrigo | In : Silverlight 4

Tags: , , ,

3

Silverlight 4 cuenta con soporte de arrastrar uno o varios archivos desde afuera de la aplicación hacia adentro de ella y soltarlo(s) para poder leer su información y contenido.  Esta característica nos permite como desarrolladores crear mejores Experiencias para los Usuarios ya que evitan el número de cajas de diálogo y clics para abrir o leer un archivo o conjunto de archivos.  En este artículo veremos a detalle esta nueva funcionalidad, por medio de una aplicación capaz de reproducir archivos de audio y video (MP3 y WMV respectivamente).

El proyecto

Iniciaremos creando un nuevo proyecto de Silverlight 4 por medio de Visual Studio 2010 Beta 2.  Al proyecto lo nombraremos Demo.SL4.ArrastrarSoltar y aceptaremos las opciones por default para crear un proyecto Web para probar la aplicación Silverlight.  En MainPage.xaml agregaremos algunos elementos y controles para tener la siguiente Interfaz de Usuario:

image 

De la interface podemos destacar lo siguiente:

-Implementamos un MediaElement llamado media (no visible en la figura anterior) el cual nos servirá para reproducir el audio o video.

-El área gris obscura es un Rectangle llamado rectangulo el cual nos servirá como área para poder arrastrar un archivo.

-El área gris clara es un ListBox, en donde se mostrarán el (los) archivo(s) de audio y/o video que se arrastren desde afuera de la aplicación.

-La barra de botones de abajo nos servirán para controlar el MediaElement en donde se reproducirán los archivos.

El código completo de MainPage.xaml es el siguiente:

<UserControl x:Class="Demo.SL4.ArrastrarSoltar.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"
    mc:Ignorable="d"
    d:DesignHeight="600" d:DesignWidth="800">
    <UserControl.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="Foreground"
                    Value="White" />
            <Setter Property="HorizontalAlignment"
                    Value="Center" />
            <Setter Property="VerticalAlignment"
                    Value="Center" />
        </Style>
        <Style TargetType="Button">
            <Setter Property="Width"
                    Value="100" />
            <Setter Property="Height"
                    Value="30" />
            <Setter Property="VerticalAlignment"
                    Value="Center" />
            <Setter Property="HorizontalAlignment"
                    Value="Center" />
            <Setter Property="Margin"
                    Value="3" />
        </Style>
        <Storyboard x:Name="VideoStoryboard">
            <DoubleAnimation Storyboard.TargetName="media"
                             Storyboard.TargetProperty="Width"
                             From="0"
                             To="400"
                             Duration="00:00:02">
                <DoubleAnimation.EasingFunction>
                    <BackEase EasingMode="EaseOut" />
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
            <DoubleAnimation Storyboard.TargetName="media"
                             Storyboard.TargetProperty="Height"
                             From="0"
                             To="300"
                             Duration="00:00:02">
                <DoubleAnimation.EasingFunction>
                    <BackEase EasingMode="EaseOut" />
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>
        <Storyboard x:Name="ImagenStoryboard">
            <DoubleAnimation Storyboard.TargetName="imagen"
                             Storyboard.TargetProperty="Width"
                             From="150"
                             To="200"
                             Duration="00:00:01"
                             AutoReverse="True"
                             RepeatBehavior="Forever"
                             >
                <DoubleAnimation.EasingFunction>
                    <BackEase EasingMode="EaseInOut" />
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
            <DoubleAnimation Storyboard.TargetName="imagen"
                             Storyboard.TargetProperty="Height"
                             From="50"
                             To="100"
                             Duration="00:00:01"
                             AutoReverse="True"
                             RepeatBehavior="Forever"
                             >
                <DoubleAnimation.EasingFunction>
                    <BackEase EasingMode="EaseInOut" />
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="Black">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="200" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="*" />
            <RowDefinition Height="60" />
        </Grid.RowDefinitions>
        <TextBlock Text="Silverlight 4 Beta – Arrastrar y Soltar" FontSize="18" FontWeight="Bold" Grid.ColumnSpan="2" />
        <Rectangle Grid.Row="1" Fill="Gray" x:Name="rectangulo" AllowDrop="True" />
        <MediaElement x:Name="media" Width="0" Height="0" Stretch="Uniform"
                      Grid.Row="1" AutoPlay="True"
                      Volume="1"
                      AllowDrop="True"
                      />
        
        <Image x:Name="imagen"
               Width="0"
               Height="0"
               Stretch="Uniform"
               Source="partitura.jpg"
               Grid.Row="1" />
        <ListBox x:Name="lista"
                 Grid.Row="1"
                 Grid.Column="1"
                 Background="LightGray"
                 AllowDrop="True"
                 >
        </ListBox>
            <StackPanel Grid.Row="2" Grid.ColumnSpan="2">
            <TextBlock x:Name="fileInfo" Text="Instrucciones:  Arrastra un archivo .MP3 o .WMV a las áreas grises" />
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
                <Button Content="Iniciar" x:Name="btnPlay" IsEnabled="False" />
                <Button Content="Pausa" x:Name="btnPause" IsEnabled="False" />
                <Button Content="Parar" x:Name="btnStop" IsEnabled="False"/>
                <Button Content="Limpiar lista"
                        x:Name="btnClear" IsEnabled="{Binding Items.Count, ElementName=lista}"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</UserControl>

 

Propiedad AllowDrop

La propiedad para determinar si algún elemento es capaz de recibir un archivo que se está arrastrando desde afuera de la aplicación es la propiedad AllowDrop.  Esta propiedad está habilitada en nuestra aplicación para el rectángulo, el ListBox y el MediaElement, ya que será cualquiera de esos elementos capaz de “recibir” un archivo que se esté soltando después de haberlo arrastrado desde afuera de la aplicación.  Podemos establecerla desde Xaml o programáticamente.

Evento Drop

El evento más importante para el mecanismo de "soltar” un archivo desde afuera de la aplicación es el evento Drop.  Este evento se disparará cuando efectivamente se haya soltado uno o varios archivos que provienen de afuera de la aplicación Silverlight.  Drop es de tipo RoutedEvent, lo que significa que el evento una vez que suceda escalará de lo más profundo a lo más alto del árbol Xaml, por lo que será buena idea establecer su propiedad Handled a true para evitar que escale.

El objeto de argumentos de este evento es de tipo DragEventArgs en donde encontraremos –además de la propiedad Handled- la propiedad Data.  Será en esta propiedad en donde podremos obtener la información del o los archivos que se han soltado en el elemento en cuestión.

Propiedad Data

Data es de tipo IDataObject.  Por medio de su evento GetData() podremos obtener la información del o los archivos que se han soltado, siempre y cuando pasemos el formato adecuado a ese método.  El formato para este mecanismo de Arrastrar y Soltar será “FileDrop” (también podremos usar DataFormas.FileDrop).  El siguiente código muestra el manejador de evento llamado ManejaDrop, el cual manejará efectivamente los eventos Drop del rectángulo, del ListBox y del MediaElement:

void ManejaDrop(object sender, DragEventArgs e)
        {
            //Obtiene el objeto que tiene la información del o los archivos soltados
            IDataObject dataObject = e.Data;

            //A través de GetData obtenemos un array de tipo FileInfo[]
            //Este array tendrá tantos elementos como archivos se hayan soltado en el elemento
            FileInfo[] fileInfoArray = dataObject.GetData("FileDrop") as FileInfo[];

            //Carga la lista de archivos en el ListBox
            CargarLista(fileInfoArray);

            //Indicamos que el evento no escale
            e.Handled = true;
        }

Podemos apreciar la invocación a un método llamado CargarLista().  Este método recibe el array de FileInfo[] para cargar cada objeto de tipo FileInfo en el ListBox.  El siguiente código muestra el método CargarLista():

void CargarLista(FileInfo[] archivos)
{
    //Establece FileInfo.Name como valor de cada elemento
    lista.SelectedValuePath = "Name";
    //Agrega los elementos a la lista
    foreach (FileInfo item in archivos)
    {
        lista.Items.Add(item);
    }
    //Selecciona el elemento en la lista
    lista.SelectedItem = lista.Items.Count == 0 ? lista.Items.First() : lista.Items.Last();
    //Reproduce el archivo seleccionado
    Play(lista.SelectedItem as FileInfo);
}

Finalmente, el método Play() se encarga de abrir el archivo en cuestión, establecerlo como fuente del MediaElement y por último reproducirlo.  Play() incluye la validación:

string info = string.Empty;
//Queremos obtener FullName si es que está ejecutando con Confianza Elevada
if (!Application.Current.HasElevatedPermissions)
    info = string.Format("Nombre: {0}  | Tamaño: {1}", archivo.Name, archivo.Length);

ya que es buena idea obtener la información de la ruta física real si es que la aplicación está ejecutando en Confianza Elevada.

Resultado

En las siguientes figuras vemos la aplicación en acción (dentro del navegador) en este caso estamos arrastrando 11 archivos .mp3 al ListBox:

image

Una vez soltados los archivos, se cargan en el ListBox y comienza la reproducción del archivo seleccionado:

image

Resumen

La propiedad AllowDrop permite que un elemento en Silverlight pueda ser capaz de recibir un archivo que se esté arrastrando desde afuera de la aplicación.  Esto nos evita el uso de la caja de diálogo OpenFileDialog() además de poder brindar una mejor experiencia a nuestros usuarios con características esperadas y naturales.

Pueden descargar el código aquí.

Pueden ver el demo en vivo aquí (requiere Silverlight 4).

Silverlight 4 Beta – Acceso al Portapapeles

Posted on : 25-11-2009 | By : Rodrigo | In : Silverlight 4

Tags: , , ,

3

Silverlight 4 incorpora la posibilidad de acceder programáticamente al Portapapeles desde nuestra aplicación.  En este artículo veremos esta pequeña pero importante funcionalidad.

El proyecto

Crearemos un proyecto de Silverlight 4 por medio de Visual Studio 2010 Beta 2.  Al proyecto lo nombraremos Demo.SL4.Portapapeles y aceptaremos las opciones por default de creación del proyecto Web para probar la aplicación.  La aplicación tendrá la siguiente Interface de Usuario:

image 

En el primer botón guardaremos el texto del TextBox en el Portapapeles.  Además, su propiedad IsEnabled estará atada a la ruta de propiedad Text.Length del TextBox.  Este es un buen ejemplo de Atado de Datos entre Elementos.  En el segundo botón leeremos el contenido del Portapapeles y lo pondremos en el TextBlock.

La aplicación estará configurada para poder correr en Confianza Elevada para mostrar los conceptos aquí demostrados.

Clase Clipboard

La clase que permite el acceso al Portapapeles es la clase System.Windows.Clipboard.  Es importante mencionar lo siguiente:

  • El método ContainsText() indica si el Portapapeles tiene contenido
  • Para aplicaciones que estén corriendo en el sandbox (aplicaciones dentro del Navegador o Aplicaciones Fuera del Navegador tradicionales), la ejecución de los métodos GetText() y SetText() provoca la siguiente caja de diálogo, para que el usuario dé permiso explícito a la aplicación para usar el Portapapeles.  Cabe mencionar que GetText() y SetText() en este caso deberán ser iniciadas por una acción del usuario (ejemplo: el Click de un botón):

image 

  • Para las aplicaciones que ejecuten en Confianza Elevada, la anterior caja de diálogo es omitida y tendremos acceso directo a los métodos de la clase.  Además los métodos podrán ser ejecutados desde cualquier evento.  Tal es el caso de nuestra aplicación de ejemplo en donde tenemos el siguiente código:
this.Loaded += (s, a) =>
            {
                if (Application.Current.HasElevatedPermissions)
                {
                    Clipboard.SetText("Corriendo en confianza elevada " + DateTime.Now.ToString());
                    textBox1.Text = Clipboard.GetText();
                }
            };

En el código podemos apreciar cómo estamos ejecutando el método SetText() siempre y cuando la aplicación esté corriendo en Confianza Elevada.

  • El Portapapeles en Silverlight 4 (en esta versión Beta por lo menos), únicamente soporta cadenas de texto tipo Unicode.

Resultado

La aplicación ejecutando en Confianza Elevada:

image

Resumen

La clase System.Windows.Clipboard nos permite tener acceso programático al Portapapeles.  Si la aplicación está ejecutando en el sandbox (Confianza Parcial), el usuario tendrá que dar permiso explícito a través de una caja de diálogo para poder usar el Portapapeles.  Si la aplicación ejecuta en Confianza Elevada, esta caja de diálogo es omitida.

Pueden descargar el código de aquí

Pueden ver el demo en vivo aquí (requiere Silverlight 4)

Silverlight 4 Beta – Soporte de Webcam y Micrófono

Posted on : 24-11-2009 | By : Rodrigo | In : Silverlight 4

Tags: , , , ,

3

Con la llegada de Silverlight 4 Beta también ha llegado a nosotros la posibilidad de implementar en nuestras aplicaciones la capacidad de tomar video a través de la Webcam y capturar audio por medio del micrófono del equipo.  Este fue una de las peticiones más fuertes en Silverlight 3 y la espera ha terminado.  En este artículo veremos cómo usar la Webcam en un aplicativo de Silverlight para mostrar el video y además capturar imágenes para posteriormente mostrarlas en una lista.

El proyecto

En esta demostración crearemos una aplicación que capture el video por medio de la WebCam del usuario y además que vaya tomando fotos según un intervalo de tiempo configurable.  Cada foto tomada se mostrará en la lista de fotos (control ListBox de color gris).

Crearemos un nuevo proyecto de Silverlight 4 desde Visual Studio 2010 Beta 2 y le nombraremos Demo.SL4.WebcamMic.  Aceptaremos las opciones por default para la creación del proyecto Web de prueba.  MainPage.xaml tendrá el siguiente código:

<UserControl
    x:Class="Demo.SL4.WebcamMic.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"
    mc:Ignorable="d"
             xmlns:tk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
    d:DesignHeight="300" d:DesignWidth="800">
    <UserControl.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="Foreground"
                    Value="White" />
            <Setter Property="VerticalAlignment"
                    Value="Center" />
            <Setter Property="Margin"
                    Value="5" />
        </Style>
        <Style TargetType="Button">
            <Setter Property="Width"
                    Value="100" />
            <Setter Property="Margin"
                    Value="5" />
            <Setter Property="FontSize"
                    Value="14" />
        </Style>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="Black">
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="150" />
            <RowDefinition Height="35" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Text="Silverlight 4 Beta – Webcam y Micrófono"
                   FontSize="30"
                   HorizontalAlignment="Center"
                   />
        <Rectangle x:Name="captura"
                   Grid.Row="1"
                   Width="120"
                   Height="120"
                   RadiusX="10"
                   RadiusY="10"
                   Stroke="White"
                   StrokeThickness="3">
            <Rectangle.Fill>
                <RadialGradientBrush>
                    <GradientStop Color="White"
                                  Offset="0.005" />
                    <GradientStop Color="Black"
                                  Offset="0.20" />
                </RadialGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
            <StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Center">
            <Button Content="Iniciar" Click="Button_Click" Tag="Iniciar" />
            <Button Content="Parar" Click="Button_Click" Tag="Parar" />
            <Button Content="Borrar" Click="Button_Click" Tag="Borrar" />
            <TextBlock Text="Intervalo:" />
            <Slider Width="100" Height="20" Minimum="1" Maximum="10" SmallChange="1" x:Name="intervalo" />
            <TextBlock Text="{Binding Value, ElementName=intervalo}" />
            <TextBlock Text="segundo(s)" />
        </StackPanel>
        
          <ListBox x:Name="listaFotos"
                 Grid.Row="3" Margin="5" Background="LightGray"
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                 >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Image Source="{Binding}"
                           Width="100">
                        <Image.Effect>
                            <DropShadowEffect />
                        </Image.Effect>
                    </Image>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <tk:WrapPanel />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>

    </Grid>
</UserControl>

image

Los elementos a destacar son los siguientes:

  • El rectángulo llamado captura nos servirá para mostrar el video de la WebCam.
  • Los botones Iniciar, Parar y Borrar nos servirán para iniciar la captura de video, parar la captura de video y borrar todas las fotos que se hayan tomado respectivamente.
  • El slider de intervalo de tiempo nos servirá para controlar el tiempo que transcurrirá entre foto y foto.

Detallado lo anterior, ahora explicaremos las clases para soportar esta funcionalidad.

Clase CaptureDeviceConfiguration

La clase CaptureDeviceConfiguration tiene diferentes objetivos.  El primero y el más importante es determinar si el usuario nos ha dado permiso para usar su Webcam / Micrófono.  Esto está indicado en la propiedad AllowedDeviceAccess.  Asimismo contamos con el método RequestDeviceAccess() el cuál mostrará una caja de diálogo al usuario indicándole que la aplicación en cuestión desea usar la Webcam y Micrófono de su equipo:

image

Si el usuario decide dar permiso a la aplicación, la propiedad AllowedDeviceAccess será true y estaremos listos para utilizar esos dispositivos.  Además, la clase CaptureDeviceConfiguration expone los siguientes métodos:

GetDefaultAudioCaptureDevice()

GetDefaultVideoCaptureDevice()

GetAvailableAudioCaptureDevice()

GetAvailableVideoCaptureDevice()

La diferencia entre los primeros dos y los últimos dos es que los primeros nos regresarán el dispositivos de audio o video respectivamente configurado como default en el equipo, mientras que los últimos dos nos regresarán una colección de dispositivos de audio y video disponibles.

Clase CaptureSource

Será en la clase CaptureSource donde encontraremos las funciones necesarias para comenzar a capturar el video y adicionalmente capturar imágenes de ese video.

Las propiedades VideoCaptureDevice y AudioCaptureDevice indican los dispositivos de video y audio respectivamente a usar, mientras que su método Start() inicia la captura de video.  Será a través de una brocha de tipo VideoBrush el medio por el cual mostraremos el video, pintándolo en el rectángulo explicado con anterioridad.

if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
                CaptureDeviceConfiguration.RequestDeviceAccess())
            {

                fuente.Stop();

                //Indica los dispositivos para captura de video y audio respectivamente
                fuente.VideoCaptureDevice = vcd;
                fuente.AudioCaptureDevice = acd;

                //Creamos una nueva brocha de video para 'pintar' con ella el rectángulo
                VideoBrush brocha = new VideoBrush() { Stretch = System.Windows.Media.Stretch.Uniform };
                brocha.SetSource(fuente);
                captura.Fill = brocha;

                //Inicia la captura de video
                fuente.Start();
                
                //Inicia el timer para capturar fotos
                timer.Start();

            }

Finalmente, para capturar imágenes del video utilizamos el método AsyncCaptureImage el cual recibe un delegado de tipo Action<WriteableBitmap> como parámetro.  En el delegado podemos obtener la imagen capturada para potencialmente guardarla en disco o simplemente mostrarla en un elemento de tipo Image.  En nuestro ejemplo estamos usando una colección de tipo ObservableCollection<WriteableBitmap> en donde iremos agregando todas las fotos tomadas automáticamente por la aplicación, y, debido a que esta colección está atada al Listbox y por tratarse de un ObservableCollection, la lista es actualizada de manera adecuada automáticamente sin código extra.  Las fotos son tomadas en el evento Tick de un DispatcherTimer en el intervalo de tiempo establecido por el usuario usando el control Slider (por default el valor es 1 segundo).

timer.Interval = TimeSpan.FromSeconds(intervalo.Value);
            timer.Tick += (s, a) =>
            {
                //Captura la imagen y la agrega a la colección de fotos
                fuente.AsyncCaptureImage((imagen) =>
                {
                    fotos.Add(imagen);
                });
            };

La siguiente figura muestra el resultado de la aplicación:

image

Resumen

El soporte de Webcam y Micrófono en Silverlight 4 abre un mundo de posibilidades para crear aplicaciones que hagan uso de este tipo de dispositivos, además de tener un modelo de programación bastante sencillo.

Puedes descargar el código aquí

Puedes ver el demo en vivo aquí (requiere Silverlight 4)

Silverlight 4 Beta – Control WebBrowser

Posted on : 23-11-2009 | By : Rodrigo | In : Silverlight 4

Tags: , ,

3

El control WebBrowser nos permite incluir contenido HTML dentro de nuestras aplicaciones de Silverlight Fuera del Navegador: una de las grandes características ausentes en la versión de Silverlight 3.  En este artículo veremos a detalle su funcionalidad y su modelo de programación presentes en Silverlight 4.

El proyecto

Iniciemos creando un nuevo proyecto de Silverlight 4 con Visual Studio 2010 Beta 2.  A este proyecto le pondremos el nombre de Demo.SL4.WebBrowser y aceptaremos las opciones por default de agregar un proyecto de Web para prueba.  A LayoutRoot en MainPage.xaml le agregaremos una instancia de WebBrowser, quedando el código de la siguiente manera:

<Grid x:Name="LayoutRoot" Background="White">
        <WebBrowser />
    </Grid>

Si ejecutamos la aplicación en este momento el resultado será el siguiente:

image

El control mostrará el mensaje “HTML is disabled” (HTML está deshabilitado).  Esto es debido a que el control WebBrowser está pensado para únicamente trabajar en aplicaciones Fuera del Navegador (por lo menos es así como trabaja en esta versión Beta de Silverlight 4).

El control WebBrowser únicamente funciona en Aplicaciones Fuera del Navegador.

Por lo anterior, configuraremos nuestra aplicación Silverlight para que pueda ejecutar Fuera del Navegador.  Esto lo logramos seleccionando la opción adecuada en las propiedades del proyecto:

image 

Además que agregaremos más Xaml que represente el resto de controles que usaremos en este ejemplo. 

Por otro lado debemos aclarar que el control WebBrowser únicamente puede mostrar contenido que está en el mismo dominio que la aplicación XAP (mismo subdominio, dominio, puerto y protocolo).  No obstante, podemos usar un elemento IFRAME para mostrar contenido que sea de otro dominio.

Desplegando contenido

Existen varios mecanismos para desplegar contenido en el control WebBrowser.

Método NavigateToString()

La manera más sencilla de mostrar contenido dentro del control WebBrowser es ejecutar su método NavigateToString() el cual recibe como parámetro una cadena que represente el HTML que deseamos mostrar (tal vez construido de manera manual, o sacado del XAP, o descargado de manera asíncrona con WebClient, etc.).  En nuestro ejemplo tendremos un método llamado CargarHtml() que genere una cadena de HTML y posteriormente que sirve como fuente para el WebBrowser.  El código será el siguiente:

void CargarHtml()
        {
            string html = "<html><h1>Demostración</h1><h2>Silverlight 4 Beta</h2><p>Esta es una demostración de cómo podemos mostrar contenido HTML usando el método NavigateToString()</p></html>";

            wb.NavigateToString(html);
        }

Y el resultado:

image

Método Navigate()

Otra manera de desplegar contenido HTML es establecer la propiedad Source del WebBrowser en XAML o usar el método Navigate(). En nuestro ejemplo agregaremos un nuevo archivo llamado Contenido.htm en el sitio de orígen de nuestra aplicación Silverlight (carpeta ClientBin por default). El archivo Contenido.htm tendrá el HTML que deseamos mostrar dentro del control WebBrowser.  Como comentamos anteriormente, si deseamos mostrar contenido que viene de otros dominios debemos mostrarlo dentro de un IFRAME.  Contenido.htm mostrará la página de búsqueda de Twitter dentro del control WebBrowser en nuestra aplicación Silverlight:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <iframe id="contenido" src="http://search.twitter.com" width="100%" height="600px" />
</body>
</html>

image

Comunicación entre WebBrowser y el contenido HTML

Podemos tener comunicación bi-direccional entre el control WebBrowser en Silverlight y la página o contenido que está mostrando.

Método InvokeScript()

El método InvokeScript() permite la invocación de una función declarada en la página que haya cargado el control WebBrowser.  A nuestra página Contenido.htm le agregaremos una función de búsqueda sobre el sitio de Twitter.  Para esto agregaremos algunos controles en nuestra aplicación: un TextBox para que el usuario pueda escribir la cadena deseada y un Button para ejecutar efectivamente el método InvokeScript().

<script type="text/javascript">
    function buscar(cadena) {
        var url = "http://search.twitter.com/search?q=" + cadena;
        document.getElementById("contenido").src = url;
    }
</script>

y el resultado:

image

window.external.Notify()

La función Notify() estará disponible en el objeto window.external cuando usemos WebBrowser.  Su objetivo es mandar mensajes al control, y cuando eso sucede, se disparará el evento ScriptNotify.   En los argumentos de este evento encontraremos la propiedad Value que representa el valor establecido como parámetro en window.external.Notify().  En este ejemplo invocaremos window.external.Notify en el evento body.onload() para enviar como cadena “Silverlight” al WebBrowser. El evento ScriptNotify se disparará y es en donde obtendremos la cadena que posteriormente pondremos en el TextBox.

wb.ScriptNotify += (s, a) =>
            {
                txtCadena.Text = a.Value;
            };

 

Evento LoadCompleted

El control WebBrowser expone el evento LoadCompleted el cual se disparará cuando el contenido efectivamente haya finalizado en cargarse por primera vez, es decir, cuando el método Navigate() haya ejecutado o cuando se establece la propiedad Source.  En nuestro ejemplo lo utilizaremos para crear una notificación (característica nueva en Silverlight 4) para avisarle al usuario que la carga de la página ha finalizado.

image

Resumen

El control WebBrowser nos permite cargar contenido HTML dentro de las Aplicaciones Fuera del Navegador.  El contenido a cargar deberá estar en el mismo dominio que la aplicación, no obstante si deseamos hospedar contenido HTML de otros dominios deberemos usar un elemento IFRAME.  Asimismo, el control permite interacción con las funciones de script presentes en el contenido y viceversa; siendo de esta manera una excelente opción para la construcción de poderosas aplicaciones que requieran interactuar con HTML.

Puedes descargar el código aquí

Puedes ver el demo en vivo aquí (requiere Silverlight 4)

Silverlight 4 Beta – Impresión, Modularización y Parámetros

Posted on : 23-11-2009 | By : Rodrigo | In : Silverlight 4

Tags: , ,

0

En el artículo anterior vimos cómo mandar a imprimir múltiples páginas dentro de nuestras aplicaciones de Silverlight 4.  En este artículo iremos un paso más allá, modularizando un poco más nuestro código e implementando la funcionalidad de parametrización de las opciones de impresión (encabezado y márgen principalmente).  Esto nos ofrecerá un código más robusto que permita escenarios de impresión más detallados.  Nota: esta implementación es una propuesta e idea para poder modularizar y de ninguna manera pretende ser el único mecanismo o camino para lograrlo.

La Solución

Utilizaremos la misma solución del proyecto del artículo anterior.  A esta solución le agregaremos un nuevo proyecto de tipo Silverlight Class Library para poder incorporar en él la funcionalidad de impresión, en vez de tenerlo directamente en el code-behind de MainPage.xaml del proyecto Silverlight.  Al nuevo proyecto le pondremos el nombre de SL4.Demo.Impresion.Util y agregaremos dos clases: Impresion.cs y Parametros.cs

image

image

Impresion.cs

Será en esta clase en donde implementaremos el código de impresión.  Esta clase tendrá un método público Imprimir() el cual nos servirá como manejador del evento clic del botón en donde deseemos invocar la funcionalidad (botón button2 en MainPage.xaml).  De esta manera estaremos manejando el evento clic en una instancia de Impresion en vez de hacerlo directamente en code-behind.

La clase Impresion será de tipo genérica, ya que necesitamos conocer de antemano el tipo de contenido que se mandará a imprimir para poder realizar el filtrado de los registros de manera adecuada.  En nuestro ejemplo es un objeto de tipo Albumes.

Por las razones descritas, el código de MainPage.xaml.cs quedará de la siguiente manera:

public partial class MainPage : UserControl
    {
        SL4.Impresion.Impresion<Album> util = null;

        public MainPage()
        {
            InitializeComponent();

            util = new Impresion.Impresion<Album>(this.Resources["albumes"] as Albumes,
                this.Resources["ImpresionDataTemplate"] as DataTemplate,
                40.0,
                "Documento de Prueba",
                "Lista de Álbumes – The Beatles");

            util.ImpresionFinalizada += new EventHandler(util_ImpresionFinalizada);
            this.button1.Click += new RoutedEventHandler(button1_Click);
            this.button2.Click += util.Imprimir;
        }

        void button1_Click(object sender, RoutedEventArgs e)
        {
            util.Configurar();
        }

        void util_ImpresionFinalizada(object sender, EventArgs e)
        {
            MessageBox.Show("ok!!!");
        }

    }

En el constructor de la clase Impresion mandamos los parámetros necesarios los cuales son la fuente de datos, el DataTemplate que será usado para definir la distribución de los elementos, el alto que tendrá cada línea (corresponde al alto del registro que tiene el Grid del DataTemplate), el nombre que deseamos que aparezca en el panel de la impresora y el título deseado.

Además, con la siguiente línea:

this.button2.Click += util.Imprimir;

Estamos definiendo como manejador del evento Click del botón al método público Imprimir implementado en la clase Impresion.  Será en ese método en donde implementaremos la funcionalidad para mandar a imprimir nuestro contenido.

También cabe resaltar:

util.ImpresionFinalizada += new EventHandler(util_ImpresionFinalizada);

Con la línea anterior estamos manejando el evento ImpresionFinalizada, evento que ha sido implementado también en la clase Impresion y que se disparará una vez finalizado el proceso de impresión (efectivamente, en el evento EndPrint del objeto PrintDocument).  Lo interesante de este mecanismo es que si no establecemos un manejador para el evento, la clase impresión tiene el suyo propio: muestra un MessageBox cuando se está ejecutando adentro del navegador o muestra una ventana de Notificación (característica nueva en Silverlight 4) indicando que la impresión ha concluído.  El siguiente fragmento de código muestra este concepto:

….
            pd.EndPrint += (s, a) =>
            {
                this.paginaActual = 0;

                if (a.Error != null)
                    throw a.Error;
                if (ImpresionFinalizada != null)
                {
                    ImpresionFinalizada(this, null);
                }
                else
                {
                    Notificar();
                }
            };
            pd.Print();
        }

        void Notificar()
        {
            if (Application.Current.IsRunningOutOfBrowser)
            {
                NotificationWindow nw = new NotificationWindow();
                nw.Content = new TextBlock() { Text = "Impresión finalizada" };
                nw.Show(1000);
            }
            else
            {
                MessageBox.Show("Impresión finalizada");
            }
        }

 

Si ejecutamos la aplicación Fuera del Navegador, la notificación sería mostrada (quitando el manejador de evento personalizado de nuestro código):

image

Parametros.cs

 

La clase Parametros nos servirá para permitir al usuario que modifique los parámetros para la impresión, principalmente para el encabezado y el márgen izquierdo para el ItemsControl.  El mecanismo seleccionado para este efecto será tener un ChildWindow (llamado Configuracion) el cual contenga un control de tipo DataForm; para permitir la edición de los valores se una manera sencilla y rápida.  La siguiente figura muestra la caja de diálogo de configuración en acción:

image

Esto brinda al usuario una manera de poder cambiar algunos parámetros a la hora de imprimir.  El resultado será el siguiente (impresión a un archivo .xps):

image

Pueden descargar el código de aquí

Pueden ver el demo en vivo aquí (requiere Silverlight 4)

Silverlight 4 Beta – Impresión de Múltiples Páginas

Posted on : 21-11-2009 | By : Rodrigo | In : Silverlight 4

Tags: , ,

5

En el artículo anterior, vimos los fundamentos del modelo de clases para imprimir desde nuestras aplicaciones de Silverlight 4.  Recordemos que la propiedad HasMorePages indica si nuestro documento tiene más páginas que imprimir; si HasMorePages es true entonces el evento PrintPage se disparará nuevamente hasta que HasMorePages sea false.

En este artículo veremos cómo aprovechar esta característica para poder mandar a imprimir un catálogo de artículos (en este ejemplo, un catálogo de álbumes de música) ya que este escenario será bastante común en las Aplicaciones de Línea de Negocio que construyamos con Silverlight 4.

Escenario

Dado el siguiente escenario:

1

Podemos apreciar que tenemos un ListBox el cual contiene una lista de álbumes de música.  Además estamos cambiando el DataTemplate para poder mostrar de una mejor manera cada uno de esos elementos.  El siguiente fragmento de código muestra el Xaml de la aplicación:

<UserControl x:Class="Demo.SL4.ImpresionMultiple.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:local="clr-namespace:Demo.SL4.ImpresionMultiple"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="500">
    <UserControl.Resources>
        <local:Albumes x:Key="albumes" />
        
        <DataTemplate x:Key="VistaDataTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <Border BorderBrush="Black" Padding="3" BorderThickness="2" CornerRadius="10">
                   <Image Source="{Binding Foto}"
                       Width="100" />
                    </Border>
                <StackPanel Grid.Column="1">
                    <TextBlock Text="{Binding Titulo}"
                           FontSize="20" />
                <TextBlock Text="{Binding Banda}" />
                     </StackPanel>
                   </Grid>
        </DataTemplate>
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource albumes}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <ListBox Grid.Column="1"
                 HorizontalAlignment="Left"
                 Margin="5"
                 Name="listBox1"
                 VerticalAlignment="Top"
                 Width="450"
                 Height="450"
                 ItemsSource="{Binding}"
                 ItemTemplate="{StaticResource VistaDataTemplate}" />
        <StackPanel Height="228"
                    HorizontalAlignment="Left"
                    Name="stackPanel1"
                    VerticalAlignment="Top"
                    Margin="5">
            <Button Height="50"
                    Name="button1"
                    Width="120">
                <Button.Content>
                    <StackPanel>
                        <TextBlock HorizontalAlignment="Center">Imprimir</TextBlock>
                        <TextBlock HorizontalAlignment="Center">ListBox</TextBlock>
                    </StackPanel>
                </Button.Content>
            </Button>
            <Button Height="50"
                    Name="button2"
                    Width="120">
                <Button.Content>
                    <StackPanel>
                        <TextBlock HorizontalAlignment="Center">Impresión</TextBlock>
                        <TextBlock HorizontalAlignment="Center">Personalizada</TextBlock>
                    </StackPanel>
                </Button.Content>
            </Button>
        </StackPanel>
    </Grid>
</UserControl>

 

Impresión de Elementos

La propiedad PageVisual del objeto de argumentos en el evento PrintPage indica ultimadamente cuál es el contenido a imprimir, siendo este contenido parte del árbol de Xaml o no.  Es decir que, si establecemos el ListBox como el PageVisual…

pd.PrintPage += (s, a) =>
            {
                //Establecemos el ListBox como el PageVisual
                a.PageVisual = listBox1;
            };

…nuestro resultado de impresión será el siguiente (impresión en XPS):

2

Tal vez este mecanismo no sea el adecuado para presentar correctamente la impresión, ya que se imprime el elemento tal y como aparece en pantalla; es decir, una “fotografía” del elemento (incluyendo las barras de scroll, los bordes, etc.).

Impresión Personalizada

Podemos lograr un grado más alto de personalización creando de manera dinámica el contenido Xaml que deseamos imprimir.  En el caso de este ejemplo generaremos dinámicamente un Canvas que contenga un encabezado, además de un ItemsControl que despliegue todos y cada uno de los elementos a imprimir. Además de lo anterior, nos aseguraremos que la impresión permita múltiples páginas y que se impriman los registros en cada página de manera correcta.

Canvas

Tal como en el artículo anterior, creamos un Canvas que tenga el mismo tamaño del área de impresión disponible, según la impresora seleccionada:

pd.PrintPage += (s, a) =>
{
    canvas = new Canvas() { Width = a.PrintableArea.Width, Height = a.PrintableArea.Height };
    …

Encabezado

Para cada página a imprimir, se agregará un encabezado que contenga un logo a la izquierda, el título del encabezado centrado, y un texto que indique el número de página actual.

Nota: Hay un issue con Silverlight 4 Beta que impide la impresión de imágenes creadas dinámicamente.

Lista de Elementos

Esta es la parte más importante de este escenario.  Necesitamos soportar el hecho de que habrá más de una página al momento de imprimir.  En el ejemplo de este artículo tenemos un objeto llamado Albumes (List<Album>) el cual tiene un total de 42 álbumes.

Para la impresión personalizada crearemos otro DataTemplate llamado ImpresionDataTemplate el cual desplegará de manera diferente los mismos elementos de la pantalla.  Por ejemplo, quitaremos la imagen de cada álbum, además desplegaremos los elementos dentro de un Grid con cuatro columnas.  Las columnas que mostraremos serán Titulo, Banda, Inventario y FechaLanzamiento.  El siguiente fragmento de Xaml muestra el DataTemplate para la impresión:

<DataTemplate x:Key="ImpresionDataTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="150" />
                    <ColumnDefinition Width="120"/>
                    <ColumnDefinition Width="100" />
                    <ColumnDefinition Width="100" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="40" />
                </Grid.RowDefinitions>
                    <TextBlock Text="{Binding Titulo}" FontWeight="Bold" Grid.Column="0" />
                    <TextBlock Text="{Binding Banda}" Grid.Column="1" />
                    <TextBlock Text="{Binding Inventario}" Grid.Column="2" />
                <TextBlock Text="{Binding FechaLanzamiento}"
                           Grid.Column="3" />
            </Grid>
        </DataTemplate>

En el método de creación del listado por página (método CrearLista()) crearemos un ItemsControl el cual su DataTemplate sea el descrito arriba.  Asimismo, estableceremos como su fuente de datos (propiedad ItemsSource) únicamente los registros que quepan en la página actual.  Con los métodos de extensión Skip() y Take() podemos fácilmente leer cierto número de registros del objeto Albumes según la página actual.  El siguiente fragmento de código muestra este concepto:

void CrearLista(int pagina)
        {
            ItemsControl lista = new ItemsControl();
            lista.Width = canvas.Width;
            lista.ItemTemplate = this.Resources["ImpresionDataTemplate"] as DataTemplate;

            //Obtiene únicamente los registros que van en la página actual
            //identificada en el argumento 'pagina' del método
            lista.ItemsSource = fuenteDatos.Skip((int)(pagina*lineasPorPagina)).Take((int)lineasPorPagina);
            
            //Agregamos la lista al Canvas
            canvas.Children.Add(lista);

            //Respetamos el alto del encabezado
            Canvas.SetTop(lista, altoEncabezado);

            //Incrementamos el contador
            paginaActual++;
        }

Finalmente, en el evento PrintPage invocamos los métodos de creación del encabezado y el de la creación de elementos por página:


CrearEncabezado("Lista de Álbumes");

CrearLista(paginaActual);

a.PageVisual = canvas;

a.HasMorePages = !(paginaActual == totalPaginas);

Muy importante en el código anterior es el valor de la propiedad HasMorePages.  En este ejemplo estamos indicando si hay más páginas siempre y cuando el contador (variable paginaActual) sea diferente al número total de páginas (variable totalPaginas).  Recordemos que si HasMorePages es true, el evento PrintPage nuevamente se disparará, provocando así la impresión de todas y cada una de las páginas del listado.

El resultado:

Impresión de dos páginas en Silverlight 4 Beta

Cabe mencionar que el DataTemplate específico para la impresión no es el único mecanismo para hacerlo de manera personalizada, también pudimos haber hecho un StackPanel o Grid de manera dinámica y leer programáticamente (un foreach tal vez) cada Album de Albumes programáticamente e ir agregando al contenedor los elementos manualmente.

Resumen

A través de la propiedad HasMorePages tenemos el control para indicar si nuestro documento a imprimir tiene múltiples páginas.  Asimismo vimos que un DataTemplate específico para la impresión es un mecanismo que nos facilita la manera en la que queremos que se imprima una lista de elementos en un reporte.

Pueden descargar el código fuente aquí

Pueden ver el demo en vivo aquí (requieres Silverlight 4 Beta instalado)

Silverlight 4 Beta – Impresión

Posted on : 21-11-2009 | By : Rodrigo | In : Silverlight 4

Tags: , ,

5

Una de las características más esperadas y solicitadas por la comunidad de Silverlight es el soporte de impresión dentro de las aplicaciones, esto es, la capacidad de poder mandar a imprimir el contenido que estemos viendo en la aplicación ya sea una lista de datos de un DataGrid o ListBox o simplemente tomarle una “foto” a la pantalla y enviarla a papel o alguna de las impresoras virtuales instaladas en nuestros equipos (por ejemplo: OneNote, XPS, PDF, etc.).  Silverlight 4 Beta incluye esta característica a través de un modelo de clases muy sencillo.  De hecho este modelo me recuerda al modelo de impresión que tenemos en las aplicaciones Windows Forms desde hace ya mucho tiempo.

Clase System.Windows.Printing.PrintDocument

Las clases para el soporte de impresión dentro de Silverlight han sido incorporadas al espacio de nombres System.Windows.Printing, el cual contiene entre otras cosas la clase PrintDocument.  Esta clase PrintDocument es la clase responsable de mandar a imprimir el contenido que deseemos.

La clase PrintDocument no expone alguna propiedad para que podamos determinar el contenido que deseemos imprimir.  No obstante, el método Print() al ser ejecutado abre la caja de diálogo de impresión de nuestro sistema operativo.

1

Una vez seleccionada la impresora destino (física o virtual), disparará la siguiente secuencia de eventos:

  1. StartPrint
  2. PrintPage
  3. EndPrint

Evento StartPrint

El evento StartPrint se dispara una vez cerrada la caja de diálogo de selección de impresora.  Inmediatamente después se dispara el siguiente evento: PrintPage.

Evento PrintPage

Seguido del evento StartPrint se disparará el evento PrintPage.  Este es el evento más importante en el modelo de impresión ya que es en los argumentos donde debemos establecer efectivamente el contenido que deseamos imprimir.  Esto es definido en la propiedad PageVisual que es de tipo UIElement.  Ya que es de tipo UIElement podemos establecer como contenido en realidad cualquier cosa: ya sea que esté presente en el árbol de Xaml o que sea contenido creado de manera dinámica (a través de código o usando XamlReader.Load(), etc.).  Por ejemplo, si deseamos tomar una “fotografía” a la aplicación actual e imprimirla, podemos establecer como PageVisual el objeto contenedor raíz (ej. LayoutRoot).  O de lo contrario, si queremos crear una impresión personalizada podemos generar contenedores y elementos de manera dinámica tal y como lo muestra este artículo.

Además de la propiedad PageVisual contamos con un par de propiedades: HasMorePages y PrintableArea.

HasMorePages es una propiedad que indica si todavía hay más páginas que imprimir, por ejemplo en el caso de documentos largos, listas, catálogos etc.  Si establecemos esta propiedad a true, entonces el evento PrintPage volverá a dispararse.

PrintableArea indica el tamaño que tiene el área de impresión según la impresora seleccionada.  Por ejemplo no es el mismo tamaño si mandamos a imprimir a OneNote que a un PDF o a una impresora física.  Esta propiedad es de tipo Size y nos servirá también para calcular márgenes, posiciones de elementos, etc.

Cabe mencionar que, si la impresora seleccionada requiere todavía de la interacción del usuario, esta continuará (para indicar el nombre de un archivo .xps o .pdf por ejemplo).

Evento EndPrint

El evento EndPrint se disparará una vez terminado el proceso de impresión.

Ejemplo

Comencemos con crear un nuevo proyecto de Silverlight 4 a través de Visual Studio 2010 (Beta 2 es la versión más reciente en el momento de escribir este artículo).  A este proyecto le pondremos de nombre Demo.SL4.Impresion.  Visual Studio creará la solución junto con el proyecto adecuado tal y como lo esperamos.

Agregaremos a nuestro MainPage.xaml el siguiente código:

<Grid x:Name="LayoutRoot" Background="White">
        <Button Content="Una página"
                Height="47"
                HorizontalAlignment="Left"
                Margin="12,12,0,0"
                Name="button1"
                VerticalAlignment="Top"
                Width="115"
                Click="button1_Click" />
        <Button Content="Muchas páginas"
                Height="47"
                HorizontalAlignment="Left"
                Margin="133,12,0,0"
                Name="button2"
                VerticalAlignment="Top"
                Width="115"
                Click="button2_Click" />
        <TextBlock Height="30"
                   HorizontalAlignment="Left"
                   Margin="14,70,0,0"
                   Name="textBlock1"
                   VerticalAlignment="Top"
                   Width="372"
                   FontSize="14" />
    </Grid>

Los botones nos servirán para mandar a imprimir por medio de la ejecución del método Print() de la clase PrintDocument y el TextBlock nos servirá para identificar el estatus de la impresión.

En el clic del primer botón crearemos una nueva instancia de PrintDocument y estableceremos su propiedad DocumentName de manera adecuada.  El valor de esta propiedad será lo que aparezca en la ventana de estado de la impresora.

PrintDocument pd = new PrintDocument() { DocumentName = "Ejemplo de Impresión" };

Ahora bien, manejaremos el evento PrintPage.  En este caso usaremos una expresión lambda pero pudiera ser también un método:

pd.PrintPage += (s, a) =>
            {
                //canvas es una variable a nivel de clase
                //Establecemos el ancho y alto de canvas a usando la propiedad PrintableArea
                canvas = new Canvas() { Width = a.PrintableArea.Width, Height = a.PrintableArea.Height };

                //Agregamos el título deseado para la página
                CrearTitulo("Esto es una Prueba de Impresión");

                //Establecemos el contenido en la propiedad PageVisual
                a.PageVisual = canvas;
            };

En el código anterior suceden varias cosas:

Creamos un objeto de tipo Canvas, el cual nos servirá como lienzo para dibujar en él los elementos visuales que queramos imprimir (en este ejemplo el contenido será un TextBlock creado en el método CrearTitulo()).  El ancho y alto del objeto Canvas los establecemos a partir de la propiedad PrintableArea, la cual, nos indica el tamaño de impresión según la impresora seleccionada (por ejemplo, el área de impresión es diferente en OneNote que un .xps).

Finalmente, establecemos este contenido que creamos dinámicamente en la propiedad PageVisual.  Esta propiedad indica el contenido visual que vamos a imprimir.  El siguiente fragmento de código contiene todo el código completo de este primer ejemplo:

Canvas canvas = null;
int pagina = 0;

private void button1_Click(object sender, RoutedEventArgs e)
{            
    PrintDocument pd = new PrintDocument() { DocumentName = "Ejemplo de Impresión" };
    pd.PrintPage += (s, a) =>
    {
        //canvas es una variable a nivel de clase
        //Establecemos el ancho y alto de canvas a usando la propiedad PrintableArea
        canvas = new Canvas() { Width = a.PrintableArea.Width, Height = a.PrintableArea.Height };

        //Agregamos el título deseado para la página
        CrearTitulo("Esto es una Prueba de Impresión");

        //Establecemos el contenido en la propiedad PageVisual
        a.PageVisual = canvas;
    };
    pd.StartPrint += (s, a) => textBlock1.Text = "Iniciando impresión…";
    pd.EndPrint += (s, a) => textBlock1.Text = "Impresión finalizada";
    pd.Print();
}

void CrearTitulo(string titulo)
{
    //Crea un TextBlock que servirá como encabezado
    TextBlock contenido = new TextBlock() { Text = titulo, FontSize = 20 };
    contenido.Effect = new DropShadowEffect();
    canvas.Children.Add(contenido);
    Canvas.SetLeft(contenido, canvas.Width / 2 – (contenido.ActualWidth / 2));
}

Y el resultado, tanto en un archivo .xps como en OneNote:

2 3

Noten cómo también soporta Efectos visuales.  Tal es el caso del DropShadowEffect que se está estableciendo en el título.

En el segundo botón tendremos una ligera variante: mandaremos a imprimir varias páginas.  Esto es logrado a partir de establecer la propiedad HasMorePages = true.  En el caso de este ejemplo hemos establecido una variable a nivel de clase llamada pagina que nos servirá de bandera.  Esta bandera la estaremos revisando hasta que se completen 4 páginas en total.

pd.PrintPage += (s, a) =>
            {
                canvas = new Canvas() { Width = a.PrintableArea.Width, Height = a.PrintableArea.Height };
                CrearTitulo("Esto es una Prueba de Impresión");
                CrearSubtitulo(string.Format("Página: {0}", pagina+1));
                a.PageVisual = canvas;

                a.HasMorePages = !(pagina == 3);
                pagina++;

            };

Cabe recordar que al establecer la propiedad HasMorePages a true, el evento PrintPage se vuelve a disparar, de esta manera, podemos tener el control del contenido a imprimir en cada página.  El código completo se muestra a continuación:

void CrearSubtitulo(string titulo)
        {
            //Crea un TextBlock que servirá como subtitulo
            TextBlock contenido = new TextBlock() { Text = titulo, FontSize = 14 };
            canvas.Children.Add(contenido);
            Canvas.SetLeft(contenido, canvas.Width / 2 – (contenido.ActualWidth / 2));
            Canvas.SetTop(contenido, 40);
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            PrintDocument pd = new PrintDocument() { DocumentName = "Ejemplo de Impresión" };
            pd.PrintPage += (s, a) =>
            {
                canvas = new Canvas() { Width = a.PrintableArea.Width, Height = a.PrintableArea.Height };
                CrearTitulo("Esto es una Prueba de Impresión");
                CrearSubtitulo(string.Format("Página: {0}", pagina+1));
                a.PageVisual = canvas;

                a.HasMorePages = !(pagina == 3);
                pagina++;

            };
            pd.StartPrint += (s, a) => textBlock1.Text = "Iniciando impresión…";
            pd.EndPrint += (s, a) => textBlock1.Text = "Impresión finalizada";
            pd.Print();
        }

El resultado final de la impresión de este segundo botón se muestra en la siguiente figura (las flechas rojas se añadieron posteriormente para indicar el número de cada página):

4

Resumen

Silverlight 4 Beta incluye la funcionalidad de impresión, a través de la clase System.Windows.Printing.PrintDocument.  Esta clase incluye el método Print(), que al ser ejecutado dispara una serie de eventos, entre ellos el evento PrintPage en donde podemos establecer el contenido deseado para imprimir, conocer el tamaño del área de impresión así como determinar si hay más páginas para imprimir (para escenarios de documentos de más de 1 página).

Puedes descargar el código aquí.

Puedes ver el demo en vivo aquí (requiere Silverlight 4 Beta instalado).

Silverlight 4 – Estilos Implícitos

Posted on : 19-11-2009 | By : Rodrigo | In : Silverlight 4

Tags:

4

Nuevo en Silverlight 4 son los Estilos Implícitos, los cuales, nos permiten definir un estilo <Style> en el diccionario de recursos para que todos los elementos del tipo especificado en el atributo TargetType del estilo lo apliquen de manera automática sin necesidad de establecer la propiedad Style en cada uno de ellos.

Ejemplo

Tomemos en cuenta el siguiente Xaml:

<StackPanel>
<TextBox />
<TextBox />
<TextBox />
<TextBox />
</StackPanel>

En términos normales, los 4 TextBox se dibujarían con su comportamiento normal, es decir, se estirarían al ancho del StackPanel que los contiene, tendrían un alto fijo, etc.

Ahora bien, si definimos en el diccionario de recursos un Style que tenga el TargetType de tipo TextBox, entonces *todos* esos controles se estilizarían según las propiedades que definamos:

<UserControl.Resources>
<Style TargetType=”TextBox”>
<Setter Property=”Width”
Value=”250″ />
<Setter Property=”Height”
Value=”30″ />
<Setter Property=”Margin”
Value=”0 0 0 5″ />
<Setter Property=”Background”
Value=”Azure” />
</Style>
</UserControl.Resources>

Noten cómo no estoy establiendo x:Key ni x:Name para el estilo.  Esto es requisito precisamente para los Estilos Implícitos.  El resultado es el siguiente:

… sin necesidad de establecer el atributo Style=”….” en cada TextBox o utilizar el ImplicitStyleManager del Silverlight Toolkit! :)

Asimismo, podemos programáticamente obtener los datos del estilo en cuestión obteniéndolo del diccionario de recursos usando el tipo relacionado (el tipo que usamos en el atributo TargetType):

//Obtiene todos los valores del estilo en cuestión
Style nuevoEstilo = this.Resources[typeof(TextBox)] as Style;

StringBuilder sb = new StringBuilder();
foreach (Setter item in nuevoEstilo.Setters)
{
sb.Append(string.Format(“{0}”, item.Value.ToString()));
sb.Append(Environment.NewLine);
}
MessageBox.Show(sb.ToString());

Resumen

Los Estilos Implícitos en Silverlight 4 nos permiten aplicar automáticamente (o ímplicitamente, de ahi su nombre) un estilo en todos los elementos de un mismo tipo, sin necesidad de usar el control ImplicitStyleManager (incluído en el Silverlight Toolkit) o establecer el atributo Style en cada elemento.

Silverlight 4 Beta – Iniciando

Posted on : 19-11-2009 | By : Rodrigo | In : Silverlight 4

Tags:

0

Como les había comentado en un post anterior el día de ayer 18 de Noviembre 2009 fue liberado Silverlight 4 Beta con lo cual se abre un abanico inmenso de posibilidades a las ya existentes con Silverlight 3, ya que esta última versión incorpora nuevas características bastante poderosas.

Para iniciarte como desarrollador en Silverlight 4 Beta debes tener instalado por lo menos Visual Studio 2010 Beta 2 o Visual Web Developer Express 2010 y las Herramientas de Silverlight 4 Beta para Visual Studio 2010 Beta 2.  Estas herramientas instalarán en tu equipo el SDK de Silverlight 4 Beta, el runtime de desarrollador (compatible con versiones anteriores de Silverlight) y las plantillas de Visual Studio 2010 para poder crear aplicaciones en la plataforma.

Ya había mencionado antes que una de las cosas notables en Visual Studio 2010 es su capacidad de ser multi-destino, es decir, poder desarrollar tu aplicación que apunte a diferentes versiones del Framework.

Asimismo, los proyectos de Silverlight también obtienen esta cualidad.  En la siguiente figura podemos apreciar cómo a un proyecto nuevo de Silverlight le podemos indicar cuál versión de la plataforma usar.  Además de lo anterior, se agrega la posibilidad de crear un proyecto de tipo ASP.NET MVC como proyecto Web para probar nuestra aplicación de Silverlight.

La versión Silverlight 4 Beta liberada en el PDC es 4.0.41108.0.  Una pestaña nueva ha sido agregada a la caja de diálogo de configuración del plugin: la pestaña “Webcam / Mic” en donde podemos seleccionar la cámara Web y/o el micrófono que nuestro equipo tenga disponible para que Silverlight los utilice como entradas de video y audio respectivamente (más de esto en otros artículos aquí en este blog).

De manera opcional, también puedes bajar la versión de Expression Blend para Silverlight 4 Beta para complementar el ambiente de desarrollo para construir aplicaciones con esta plataforma.