Saturday, September 08, 2007

WPF Video

Es la 1:30 am, y el sueño ya no me venció. Así que decidí escribir esta entrada sobre WPF y una tonta comparación con GDI+.

Supongamos que quiero dibujar un cubo con algo de pintura en un windows form.

private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath miPath = new GraphicsPath();LinearGradientBrush miBrochaGradiente = new LinearGradientBrush(new Rectangle(20, 20, 200, 150),Color.Blue,Color.Black,LinearGradientMode.ForwardDiagonal);Graphics miGrafico = this.CreateGraphics();//Iniciar una figuramiPath.StartFigure();miPath.AddRectangle(new Rectangle(20, 20, 200, 150));miGrafico.FillRectangle(miBrochaGradiente,20,20,200,150);miPath.AddRectangle(new Rectangle(50, 50, 200, 150));miPath.AddLine(20, 20, 50, 50);miPath.StartFigure();miPath.AddLine(220, 20, 250, 50);miPath.StartFigure();miPath.AddLine(220, 170, 250, 200);miPath.StartFigure();miPath.AddLine(20, 170, 50, 200);//Dibujando el pathPen miLapiz = new Pen(Color.Red, 3);miGrafico.DrawPath(miLapiz, miPath);miPath.Dispose();miLapiz.Dispose();miBrochaGradiente.Dispose();
}


Sencillo cierto? un path, que me permite construir una figura a partir de figuras primitivas, las cuales pueden ser: líneas, arcos, rectángulos, elipses, polígonos, y cuantas más figuras se les puedan ocurrir como clases trae el framework para esto. Bien la cosa es que todo esto forma una sola entidad, y en mi sencillo ejemplo, es cuestión de rectángulos y líneas. Oh claro no olviden hacerle Dispose a cada una de sus instancias gráficas, en este caso al objeto que permite hacer el dibujado sobre la forma (miGrafico), el objeto (miPath) quien representa los rectángulos y líneas, y los objetos encargados de ponerle color al asunto (miLapiz y miBrochaGradiente).
Bueno hasta este punto esto ya se pone aburridor y me comienza a dar sueño. Pero se me ocurre que puedo rotar mi cubo. Qué!! Rotar este cubo a estas horas de la madrugada, no señor, y con GDI+, esto no lo acepto. Y que tal si además de rotarlo, en cada una de sus caras muestro un video. Pues yo creo que esto es una pesadilla. Pues no señores es aquí cuando WPF (Windows presentation foundation) llega al rescate.
Dejando la tontería atrás quiero mostrarles como se puede lograr esto. Qué necesitan? Framework 3.0 o 3.5 y de ahí en adelante mucha pero mucha creatividad. Aquí va y que tengan lindos sueños.

<Window
  x:Class="RichContent.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="RichContent">
  <Grid MouseLeftButtonDown="Evento">
  <Viewport3D>
    <Viewport3D.Camera>
      <PerspectiveCamera
        FarPlaneDistance="20"
        LookDirection="0,-0.65,-1"
        UpDirection="0,1,0"
        NearPlaneDistance="1"
        Position="0,2,3" FieldOfView="40" x:Name="Camara" />
    </Viewport3D.Camera>
    <ModelVisual3D>
      <ModelVisual3D.Content>
        <Model3DGroup>
          <AmbientLight Color="White" />
          <GeometryModel3D>
            <GeometryModel3D.Geometry>
              <MeshGeometry3D
              TriangleIndices="0,1,2 3,4,5 6,7,8 9,10,11 12,13,14 15,16,17 18,19,20 21,22,23 24,25,26 27,28,29 30,31,32 33,34,35 "
              Normals="0,0,-1 0,0,-1 0,0,-1 0,0,-1 0,0,-1 0,0,-1 0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 0,-1,0 0,-1,0 0,-1,0 0,-1,0 0,-1,0 0,-1,0 1,0,0 1,0,0 1,0,0 1,0,0 1,0,0 1,0,0 0,1,0 0,1,0 0,1,0 0,1,0 0,1,0 0,1,0 -1,0,0 -1,0,0 -1,0,0 -1,0,0 -1,0,0 -1,0,0 "
              TextureCoordinates="1,1 1,0 0,0 0,0 0,1 1,1 0,1 1,1 1,0 1,0 0,0 0,1 0,1 1,1 1,0 1,0 0,0 0,1 1,1 1,0 0,0 0,0 0,1 1,1 1,0 0,0 0,1 0,1 1,1 1,0 0,0 0,1 1,1 1,1 1,0 0,0 "
              Positions="-0.5,-0.5,-0.5 -0.5,0.5,-0.5 0.5,0.5,-0.5 0.5,0.5,-0.5 0.5,-0.5,-0.5 -0.5,-0.5,-0.5 -0.5,-0.5,0.5 0.5,-0.5,0.5 0.5,0.5,0.5 0.5,0.5,0.5 -0.5,0.5,0.5 -0.5,-0.5,0.5 -0.5,-0.5,-0.5 0.5,-0.5,-0.5 0.5,-0.5,0.5 0.5,-0.5,0.5 -0.5,-0.5,0.5 -0.5,-0.5,-0.5 0.5,-0.5,-0.5 0.5,0.5,-0.5 0.5,0.5,0.5 0.5,0.5,0.5 0.5,-0.5,0.5 0.5,-0.5,-0.5 0.5,0.5,-0.5 -0.5,0.5,-0.5 -0.5,0.5,0.5 -0.5,0.5,0.5 0.5,0.5,0.5 0.5,0.5,-0.5 -0.5,0.5,-0.5 -0.5,-0.5,-0.5 -0.5,-0.5,0.5 -0.5,-0.5,0.5 -0.5,0.5,0.5 -0.5,0.5,-0.5 "/>
            </GeometryModel3D.Geometry>
            <GeometryModel3D.Transform>
              <RotateTransform3D>
                <RotateTransform3D.Rotation>
                  <AxisAngleRotation3D
             x:Name="MyRotation3D" Angle="45" Axis="0 1 0"/>
                </RotateTransform3D.Rotation>
              </RotateTransform3D>
            </GeometryModel3D.Transform>
            <GeometryModel3D.Material>
              <DiffuseMaterial>
                <DiffuseMaterial.Brush>
                  <VisualBrush>
                    <VisualBrush.Visual>
                      <MediaElement LoadedBehavior="Play" UnloadedBehavior="Close" Source = "D:\Música\Radiohead\Radiohead live at Glastonbury 2003.avi" Name="Media" />
                    </VisualBrush.Visual>
                  </VisualBrush>
                </DiffuseMaterial.Brush>
              </DiffuseMaterial>
            </GeometryModel3D.Material>
          </GeometryModel3D>
        </Model3DGroup>
      </ModelVisual3D.Content>
    </ModelVisual3D>

    <Viewport3D.Triggers>
      <EventTrigger RoutedEvent="Viewport3D.Loaded">
        <EventTrigger.Actions>
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimation From="0" To="360" Duration="0:0:5"
              Storyboard.TargetName="MyRotation3D"
              Storyboard.TargetProperty="Angle" RepeatBehavior="Forever" Name="Animacion"/>
            </Storyboard>
          </BeginStoryboard>           
        </EventTrigger.Actions>
      </EventTrigger>
    </Viewport3D.Triggers>

  </Viewport3D>
    <Button HorizontalAlignment="Left" Margin="7,10,0,236" Name="button1" Width="58"  Click="Evento" >Button</Button>
    <TextBox Height="20" Margin="11,42,0,0" Name="textBox1"  VerticalAlignment="Top" HorizontalAlignment="Left" Width="20" TextChanged="Cambio"></TextBox>
    <TextBox Height="20" Margin="11,72,0,0" Name="textBox2" VerticalAlignment="Top" HorizontalAlignment="Left" Width="20" TextChanged="Cambio"></TextBox>
    <TextBox Height="20" Margin="11,102,0,0" Name="textBox3"  VerticalAlignment="Top" HorizontalAlignment="Left" Width="20" TextChanged="Cambio"></TextBox>
    <TextBox Height="20" Margin="11,132,0,0" Name="textBox4"  VerticalAlignment="Top" HorizontalAlignment="Left" Width="20" TextChanged="Cambio"></TextBox>
    <Slider Height="23" Margin="78,9,83,0" Name="slider1" VerticalAlignment="Top" Maximum="59" ValueChanged="Cambio" />
    <Label Height="24.2766666666667" HorizontalAlignment="Right" Margin="0,2.72333333333333,7,0" Name="label1" VerticalAlignment="Top" Width="72.63">Label</Label>
  </Grid>
</Window>



Lo anterior damas y caballeros, es el pequeño fragmento de código XAML que hace toda la tarea que yo quise hacer con GDI+. La verdad es para mí un fragmento de código muy corto, comparado con todo lo que esto hace por nosotros, clases muy potentes que nos proporcionan estos lujos.

Hay unos pocos controles que adicioné para controlar la rotación del cubo, lo cual es otra característica más, además de producir XAML (si créanme, producir, y a cargo de diseñadores gráficos), nosotros como desarrolladores C#, seguimos escribiendo nuestro code behind como veníamos haciéndolo. Y al ritmo de Radio Head aquí está:

// Sample event handler:
private void Evento(object sender, EventArgs e)
{
// Media.Source = new Uri("D:\\Música\\James Morrison - The Last Goodbye.mp3");
}

private void Cambio(object sender, EventArgs e)
{

if (textBox1.Text.Length > 0 && textBox2.Text.Length > 0 && textBox3.Text.Length > 0)
{
Vector3D miV3D = new Vector3D(Double.Parse(textBox1.Text), Double.Parse(textBox2.Text), Double.Parse(textBox3.Text)); MyRotation3D.Axis = miV3D; } if (textBox4.Text.Length > 0) MyRotation3D.Angle = Double.Parse(textBox4.Text); TimeSpan miTs = new TimeSpan(0,0,Convert.ToInt32(slider1.Value)); Duration miDuracion = new Duration(miTs); Animacion.Duration = miDuracion; label1.Content = miTs.Hours.ToString() + ":" + miTs.Minutes.ToString() + ":" + miTs.Seconds.ToString(); Camara.UpDirection = new Vector3D(0, 1, slider1.Value);
}
}

Bueno recueden, deberan apuntar a su propio video, tengo comentada la línea del manejador Evento(), porque ahí muestro cómo puedo cargar por ejemplo un archivo de audio. Claro señores, no solo de video vive el hombre.

Bueno solo dejo a su imaginación todo lo que puede llegar hacer con esto, no siendo más… WPF los mató a todos!!

La imaginación es más importante que el conocimiento
Albert Einstein

Saturday, September 01, 2007

The .NET Show: The Final Episode - Silverlight

Es tiempo de decirle adiós a una de las mejores iniciativas de Microsoft, en pro de los desarrolladores. Lastimosamente veo anunciado el episodio final del .NET Show, que lástima pero bueno sus razones tendrán.

http://blogs.msdn.com/theshow/archive/2007/08/14/the-net-show-the-final-episode-silverlight.aspx