Taskbar de Windows 7 desde WPF

Ahora que se acerca el lanzamiento de Windows 7 tenemos que intentar que nuestras aplicaciones se integren de la mejor manera con Windows 7, y esto pasa por hacer que nuestras aplicaciones se lleven bien con la nueva Taskbar.

Como comentaba en mi artículo anterior http://geeks.ms/blogs/luisguerrero/archive/2009/04/05/windows-7-taskbar.aspx se pueden trabajar con varias características de la Taskbar, nosotros vamos a ver cómo crear una tarea personalizada y como añadir un pequeño botón a la pre visualización de la ventana como la del Windows Media Player. Todo esto lo vamos a hacer para nuestra aplicación de WPF, así que como programadores de .net no tenemos una API directamente para hacer esto así que tenemos que hacer P/Invoke desde .NET. Pero estamos de suerte porque Microsoft ha creado varias librerías para que los programadores de .NET usemos las nuevas características del SO directamente desde nuestro lenguaje favorito de .NET.

Para ello tenemos que descargar dos librerías

Nosotros vamos a trabajar con la librería de la Taskbar, pero está referencia al proyecto de Vista Bridge Library. Dentro del proyecto de la Taskbar encontramos una clase llamada Windows7Taskbar que nos va a permitir hacer una serie de cosas básicas con la taskbar como:

  • Habilitar o deshabilitar la pre visualización de miniatura de nuestra aplicación
    • public static void EnableCustomWindowPreview(IntPtr hwnd)
    • public static void DisableCustomWindowPreview(IntPtr hwnd)
  • ·Establecer el appId de nuestra aplicación para que así se agrupen las ventanas en la Taskbar:
    • public static void SetCurrentProcessAppId(string appId)
  • Establecer el appId para una de las ventanas de nuestra aplicación
    • public static void SetWindowAppId(IntPtr hwnd, string appId)

También vamos a poder trabajar con los JumpList y con los ThumbButton.

JumpList

Para trabajar con los JumpList tenemos una clase dentro del proyecto de Windows 7 Taskbar llamada JumpListManager, aquí podemos ver qué métodos, propiedades y eventos tiene.

Para crear una instancia de esta clase tenemos dos constructores en el que se nos pide o el HANDLE de la ventana o el AppId de la ventana.

image

Obtener el Handle de una ventana en Windows Forms es muy sencillo solo tenemos que acceder a la propiedad Handle de la clase Form y ya lo tenemos, pero ¿cómo se accede al manejador de una ventana de WPF?, si intentamos buscar esa propiedad en la clase System.Windows.Window de WPF observamos que no tenemos esa propiedad, es más, que ningún control de WPF tiene una propiedad Handle.

Esto es así porque como ya sabemos WPF es una tecnología nueva en la que no se utiliza nada de lo anterior para trabajar, y de hecho no es como Windows Forms que es un wrapper muy grande de la API de Win32, aquí en WPF se ha empezado desde cero. Pues bien tiene que haber alguna manera de por lo menos, obtener el Handle de la ventana porque de lo que estamos seguros es de que por mucho que el contenido de una ventana sea WPF o WF al final se llamada a la función CreateWindowEx que en alguno de los casos nos devolverá el Handle de la ventana.

Para obtener el Handle de la ventana en WPF tenemos que ir hasta el namespace System.Windows.Interop para encontrarnos con la clase WindowInteropHelper que una vez creada una instancia, pasándole por argumento la ventana (System.Windows.Window) podemos obtener el Handler (IntPtr) de la ventana y incluso su propietario Owner (o establecerlo). Una vez superado esta pequeña traba podemos continuar con nuestra integración de la Taskbar.

El siguiente problema que se nos plantea es cuando es el mejor momento para crear la instancia de esta clase, porque si lo hacemos antes de que el Handle de la ventana se haya construido, la llamada a la función fallará. Tenemos que elegir el mejor momento para hacerlo, ese momento puede ser cuando se lanza el evento Loaded de la ventana porque es cuando ya se ha construido el árbol visual, se ha hecho measure, arrange y se va a mostrar la ventana al usuario. Pero también el mejor momento puede ser cuando la Taskbar nos avista de que se va a proceder a colocar el icono de la aplicación en la propia Taskbar, este es el momento justo para hacerlo.

Ese momento del que estoy hablando es cuando la aplicación recibe el mensaje “TaskbarButtonCreated” que previamente ha sido registrado llamado a RegisterWindowMessage(L” TaskbarButtonCreated”). Teniendo en cuenta que ya hemos llamado a esa función como podemos escuchar los mensajes de la API de Win32 desde una aplicación WPF. En Windows Forms es muy sencillo porque tenemos que sobrescribir la función virtual WndProc en la que se nos pasa por referencia el Mensaje que se está procesado en ese momento, pero de la misma manera si vamos a WPF vemos que no tenemos esa función disponible para sobrescribir.

La cosa es un poco más complicada porque si alguno recuerda de otros post anteriores, en WPF tenemos un objeto llamado Dispatcher (System.Windows.Threading) que se encarga de procesar los mensajes de la API de Win32, pero de alguna manera queremos que después de que este Dispatcher procese los mensajes lo hagamos nosotros, básicamente lo que queremos hacer en un WindowHook, pero un poco más elegante. No tenemos que llamar a SetWindowHookEx sino que tenemos que explorar de nuevo System.Windows.Interop para encontrar la clave.

La clase que estamos buscando ahora es: HwndSource, que según la documentación presenta el contenido de WPF en una ventana de Win32, que es justamente lo que estamos buscando, pero el caso es que esta clase ya deberá de esta creada porque ya tenemos nuestra ventana creada, así que tenemos que obtener el HwndSource para nuestra ventana actual.

h = new WindowInteropHelper(Application.Current.MainWindow);

HwndSource source = HwndSource.FromHwnd(h.Handle);

Esta clase tiene un método llamado AddHook que acepta por parámetro un delegado que será la función que se llamará cada vez que haya un mensaje disponible para procesarse. Esta es la signatura de la función a ejecutar:

image

public IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)

Lo último que nos queda es filtrar todos los mensajes que nos van a llegar por el mensaje que nos enviara la Taskbar diciendo que ya está lista. Podemos ver el código completo de todo esto aquí.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Windows7.DesktopIntegration;
using System.Windows;
using System.Windows.Interop;
using System.Diagnostics;
using System.Drawing;


namespace MainClient
{
    public class Taskbar
    {
        private static Taskbar instance = new Taskbar();
        public static Taskbar Instance
        {
            get
            {
                return instance;
            }
        }
        private IntPtr idWindow;
        private JumpListManager manager;
        private ThumbButtonManager thumbButtonManager;
        private WindowInteropHelper h;
        public Taskbar()
        {
            HwndSourceHook hook = new HwndSourceHook(HwndSourceHook);
            h = new WindowInteropHelper(Application.Current.MainWindow);
            HwndSource source = HwndSource.FromHwnd(h.Handle);
            source.AddHook(hook);
        }

        public IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == Windows7Taskbar.TaskbarButtonCreatedMessage)
            {
                manager = new JumpListManager(h.Handle);
                manager.UserRemovedItems += new EventHandler<UserRemovedItemsEventArgs>(OnUserRemoveItems);
                manager.Refresh();
                if (manager.CustomDestinationCategories.Count() == 0)
                {
                    // create list
                    manager.AddUserTask(new ShellLink()
                    {
                        Title = "Enviar mensaje",
                        Path = Process.GetCurrentProcess().MainModule.FileName,
                        Arguments = " /send"
                    });
                }

                manager.Refresh();

                thumbButtonManager = new ThumbButtonManager(h.Handle);
                ThumbButton button = thumbButtonManager.CreateThumbButton(101, SystemIcons.Information, "No molestar!!");
                button.Clicked += new EventHandler(OnChangeNotificationAlerts);
                thumbButtonManager.AddThumbButtons(button);
            }
            if (thumbButtonManager != null)
            {
                System.Windows.Forms.Message m = new System.Windows.Forms.Message();
                m.HWnd = hwnd;
                m.LParam = lParam;
                m.Msg = msg;
                m.WParam = wParam;
                thumbButtonManager.DispatchMessage(ref m);
            }
            return IntPtr.Zero;
        }

        void OnChangeNotificationAlerts(object sender, EventArgs e)
        {
            Services.Instace.IsNotificationEnabled = !Services.Instace.IsNotificationEnabled;
            if (!Services.Instace.IsNotificationEnabled)
            {
                Windows7Taskbar.SetTaskbarOverlayIcon(h.Handle, SystemIcons.Error, "Sin notificaciones");
            }
            else
            {
                Windows7Taskbar.SetTaskbarOverlayIcon(h.Handle, null, null);
            }
        }


        void OnUserRemoveItems(object sender, UserRemovedItemsEventArgs e)
        {

        }
    }
}

 

Una vez que ya tenemos la instancia del JumpListManager ya podemos agregar tareas a la Taskbar y asociarles una acción. En el código de arriba podemos ver que se añade una acción que es enviar mensaje que, básicamente, ejecuta la aplicación actual pero con el parámetro /send.

Otra posibilidad que hay con la Taskbar es poder insertar botones dentro de la previsualización de la ventana, como hace el Windows Media Player. A través de la clase ThumbButtonManager podemos agregar botones a la Taskbar y realizar una acción cuando se haga clic. En el ejemplo de arriba podemos ver como añadimos un botón configuramos el Icono a mostrar y nos suscribimos al evento Click.

Para terminar el ejemplo tenemos que “alimentar” al ThumbButtonManager con los mensajes que recibimos de Win32, para que pueda realizar sus tareas, es por eso que tenemos que llamar al método DispatchMessage para que pueda comprobar que se ha hecho clic en un botón.

Pixel Shaders in Siverlight 3.0 and WPF !!

Ahora que ha salido Siverlight 3.0 Beta 1, podemos ver que una de las caracteristicas que incluye es soporte para Pixel Shaders, pero realmente, ¿Qué son los Pixel Shaders?

image

Pixel Shader es unas de las partes de un Shader. Shader es un conjunto de instrucciones que la tarjeta gráfica ejecuta, estas instrucciones están muy optimizadas para el trabajo con colores, números de coma flotante y triangulos. Eso significa que los Shaders son como una especie de ensamblador que se ejecuta en la tarjeta gráfica, son muy parecidos a las extensiones multimedia de los procesadores, (MMX SSE).

Como podemos ver en esta grafica la imagen en 3D que se procesa en una tarjeta grafica pasa por diferentes pasos hasta que se muestra en la pantalla, cada uno de estas gráficas se pueden llamar técnicas, que están compuestas de un Verter Shader y de un Pixel Shader.

Verter Shader trabaja con la información de la malla (Mesh) de los objetos 3D que hay en la memoria de la tarjeta grafica a través de matrices.

Una vez que esta información se ha procesado se rellena el objeto con una textura, y ahora se puede procesar un Pixel Shader, que permite aplicar un efecto a cada uno de los pixeles finales de la imagen proyectada del objeto 3D, eso significa que los Pixel Shaders están pensados para imágenes 2D.

Pues bien en todo este conjunto de funcionalidades, en WPF y Silverlight 3.0 Beta1 tenemos soporte para Pixel Shaders.

¿Eso qué significa?, Pues bien, que podemos definir un algoritmo para una imagen 2D que se ejecutará en la tarjeta gráfica.

¿Qué tiene esto de bueno?, Una de las cosas buenas que tiene, es que evidentemente la tarjeta gráfica es mucho más potente en este tipo de cálculos que el procesador, porque está pensado para esto, así que significa que podemos tener efectos espectaculares que ejecutan en tiempo real en WPF y Silverlight 3.0 Beta 1.

Vale, me has convencido, como puedo empezar a escribir uno de estos estupendos shaders?

Vamos a ver como se integran los Pixel Shaders dentro de WPF y de Silverlight 3.0 Beta 1.

Voy a suponer que ya tenemos el Pixel Shader escrito, aquí tenemos uno de ejemplo:

sampler2D input : register(s0);
float factor : register(c0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float4 color = 0;
    float angle = -3.14;
    float yDist = uv.y - 0.5;
    float xDist = uv.x - 0.5;
    float dist = sqrt(xDist * xDist + yDist * yDist);
    angle *= dist * factor;
    float xN = cos(angle) * xDist - sin(angle) * yDist;
    float yN = sin(angle) * xDist + cos(angle) * yDist;
    uv.x = xN + 0.5;
    uv.y = yN + 0.5;
    color = tex2D(input, uv);
    return(color);
}

La sintaxis es muy parecida a C, solo que ahora tenemos una función que acepta por parámetro un float2, que es un vector de dos posiciones de tipo float, que además está decorado con TEXCOORD, lo que indica que nos están pasando por parámetro la coordenada x,y del pixel entre 0 y 1, y si nos fijamos la función devuelve un float4, vector de 4 posiciones de tipo float, que está decorado con COLOR, lo que nos indica que tenemos que devolver el color del pixel de esa posición, como bien sabréis son 4 posiciones para RGBA (Red, Green, Blue, Alpha).

Las funciones que se utilizan dentro del cuerpo de la función soy muy sencillas e intuitivas para cualquier programador, así que no las vamos a comentar. La única que merece la pena es tex2D(sampler2D, float2) que acepta por parámetro un sampler, una imagen de origen, y una coordenada x,y y te devuelve el color del pixel en esa posición (float4), esta es la función que tenemos que llamar para saber cuál es el color del pixel del control o UIElement al que le estamos aplicando el efecto.

Como podéis observar esto está definido fuera del cuerpo de la función, en la que tenemos dos variables que son parámetros de entrada para el Pixel Shader, se definen el tipo, el nombre y luego el registro de entrada, en nuestro caso el registro s0 y c0, que dependiendo de la versión del Shader que soporte el hardware tendremos más registros o menos.

Pues bien para poder compilar este fichero tenemos dos opciones, hacerlo con el compilador de Shaders del SDK de DirectX usando este comando:

fxc /T ps_2_0 /E main /Fo<name of HLSL file>.ps <name of HLSL file>.fx

O podemos instalarnos un plug-in de msbuild para que desde Visual Studio compilemos el fichero .fx que contiene el Shader, para hacerlo tenemos que ir a esta web http://www.codeplex.com/wpf/ y descargar “Shader Effects BuildTask and Templates.zip”.

Una vez que tenemos el Pixel Shader compilado lo que tenemos que hacer es generar una clase que sea el ShadeEffect que podremos usar tanto en WPF como en Silverlight 3.0 Beta 1.

Aquí tenemos el ejemplo:

using System;
using System.Windows.Media.Effects;
using System.Windows;
using System.Windows.Media;
namespace MyVideoProject
{
    public class SwirlEffect : ShaderEffect
    {
        private static PixelShader _pixelShader = new PixelShader()
        {
            UriSource = new Uri("MyVideoProject;component/Swirl.ps",
            UriKind.Relative)
        };
        public SwirlEffect()
        {
            PixelShader = _pixelShader;
            UpdateShaderValue(InputProperty);
            UpdateShaderValue(FactorProperty);
        }
        public static readonly DependencyProperty InputProperty =
            ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(SwirlEffect), 0);
        public Brush Input
        {
            get { return (Brush)GetValue(InputProperty); }
            set { SetValue(InputProperty, value); }
        }
        public static readonly DependencyProperty FactorProperty =
            DependencyProperty.Register("Factor",
            typeof(double), typeof(SwirlEffect),
            new PropertyMetadata(0.0, PixelShaderConstantCallback(0)));
        public double Factor
        {
            get { return (double)GetValue(FactorProperty); }
            set { SetValue(FactorProperty, value); }
            }
        }
    }

Lo que estamos definiendo en esta clase es el fichero .ps, que contiene los bytes codes compilados del shader que se ejecutará en la tarjeta gráfica, y además dos Dependency Property especiales que serán los dos parámetros que definimos en el código fuente de nuestro Pixel Shader.

Por un lado se define FactorProperty de tipo double, (r0) y por el otro tenemos un registro del Dependency Property, solo que en vez de llamar a DependecyProperty.Register hemos llamado a ShaderEffect.RegisterPixelShaderSamplerProperty indicando el nombre de la DP y el índice del sampler (s0).

Una vez que se tiene definidos estas dos Dependency Property tenemos que indicar de alguna manera a la clase ShaderEffect que queremos que esos dos valores los actualice desde WPF/SL3.0 al Pixel Shader, eso es lo que se define en el constructor de la clase con las llamadas a UpdateShaderValue(InputProperty) y UpdateShaderValue(FactorProperty).

Así se cierra la definición de un Pixel Shader para su definición, si hubiéramos usado algún otro sampler o registro para nuestros Pixel Shader tendríamos que registrarlo para poder usarlo dentro del Pixel Shader.

Una vez definido todo esto ahora podemos usar nuestro Pixel Shader en WFP como en Silverlight 3.0 Beta 1.

Aquí tenemos un ejemplo de cómo usar nuestro Pixel Shader:

<Border
Margin="24">
    <Border.Effect>
        <local:SwirlEffect
        x:Name="mySwirlEffect"
        Factor="0" />
    </Border.Effect>
<Border>

Y este es el resultado:

clip_image004

Adjunto el proyecto de ejemplo que viene con Silverlight 3.0 Beta 1 Demos. Aquí.

Espero que os guste!!

Luis.

Hola Mundo Surface con WPF

Como comente en mi anterior post el SDK del Surface incluye dos plantillas del proyecto para desarrollar en Surface, WPF y XNA. Pues bien vamos a ver como se hace una aplicación para Surface con WPF.

clip_image002

Si creamos el proyecto de Surface esto es lo que tenemos en la ventana de solución de proyecto.

clip_image004

Al ser una aplicación de WPF tenemos el clásico App.xaml que el objeto Application y la ventana principal. La ventana SurfaceWindow1.xaml se le ha cambiado la clase base y ahora en vez de ser Window es SurfaceWindow. También se ha cambiado en el xaml la clase base de esta manera.

<s:SurfaceWindow x:Class="SurfaceApplication1.SurfaceWindow1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"
    Title="SurfaceApplication1"
    >
  <s:SurfaceWindow.Resources>
    <ImageBrush x:Key="WindowBackground" Stretch="None" Opacity="0.6" ImageSource="pack://application:,,,/Resources/WindowBackground.jpg"/>
  </s:SurfaceWindow.Resources>

  <Grid Background="{StaticResource WindowBackground}" >

  </Grid>
</s:SurfaceWindow>

De esta manera el Shell de Surface sabe que aplicaciones son aplicaciones de Surface.

A partir de este momento tenemos un proyecto de WPF normal y podemos empezar a programar de manera normal.

Como ya dije en mi post anterior, una de las características más atractivas de hacer aplicaciones para Surface en WFP es, además del hecho de que tenemos una serie de características propias de WPF (controles, layout, hardware accelerated), que también tenemos una serie de controles que Microsoft ha hecho que se pueden utilizar dentro de WPF.

Estos controles a los que me refiero son los common controls de WPF pero que ahora se han “surfeizado”, es decir si antes teníamos Button ahora tenemos SurfaceButton que permite tener un botón que es sensible a los contactos del dedo.

clip_image006clip_image008

Estos contactos (Contacts) son uno de los conceptos usados por Surface para reconocer que hay algo encima de la mesa. Estos contactos son parecidos a los eventos de raton, y podemos tener tres tipos de contactos, Finger, Blog, Tagged-object, que permiten reconocer si se ha puesto un dedo encima, un objeto mayor, o un objeto con un Tag.

Así que siguiendo con la filosofía de eventos propios del ratón ahora para los contactos tenemos eventos como estos: ContactEnter, ContactLeave, ContactChanged y demás.

Los controles mencionados anteriormente, son controles que ya tienen la funcionalidad propiea de los controles de Windows pero que responden a los eventos de contactos de Surface así que nosotros no tenemos que, en cada proyecto, implementar esa funcionalidad sino que podemos usar los que ya vienen, además de que todos los controles tiene soporte de tiempo de diseño, lo que significa que se visualizan correctamente en Expression Blend.

Podemos ver una captura aquí.

clip_image010

 

Así que ya podemos empezar a usar los controles de Surface para desarrollar nuestras aplicaciones de Surface. Además todos los controles están muy bien diseñados, es decir, que todos tienen un árbol visual bien definido, lo que permite que los diseñadores cambien el aspecto visual del control si que eso afecte al comportamiento del control.

Saludos desde Londres. Luis.

Microsoft Surface, SDK y WPF

Como todos sabréis esta semana se ha presentado el Microsoft Surface en Microsoft España. No se pueden decir más cosas sobre el proyMicrosoftSurfaceecto que vosotros sepáis, pero de lo que podemos hablar es del desarrollo de aplicaciones para Surface.

El Surface como cualquier otro gadget, el desarrollo de aplicaciones es una de las partes más importantes del dispositivo, pues las aplicaciones hacen que el Surface brille. Pues bien esta semana el día 2 de Marzo hice una presentación en Microsoft España sobre el desarrollo de aplicaciones para WPF y quiero compartir con vosotros esa experiencia.

¿Cómo se desarrollan aplicaciones para el Surface?, pues bien hay dos tipos de tecnología por el cual nosotros podemos desarrolla aplicaciones para el Surface que vienen incluidos en el SDK.

  • WPF (Windows Presentation Foundation)
  • XNA

XNA está más centrada en el desarrollo de videojuegos para Xbox360, Windows y Zune. Es una tecnología basada en .NET, lo que significa que podemos desarrollar nuestros videojuegos con C# y VB.NET.

Sobre WPF que decir… además de que es la caña, que nos permite que tanto diseñador como programador interaccionen y crear aplicaciones muy impactantes.

Ahora bien ¿que tecnología usar?, depende del tipo de aplicación. Con WPF vamos a tener muchas más cosas listas para poder trabajar con Surface, que con XNA, aunque WPF no tiene (por ahora) un soporte para 3D tan bueno como XNA. Eso significa que si queremos hacer una aplicación con un uso intensivo de 3D deberemos de usar XNA sin pensarlo. De todos modos en WPF tenemos soporte para 3D y tenemos todo lo bueno de WPF, layout, controles, tiempo de diseño, acelerado por hardware, ect.

Así que la decisión en principio puede ser fácil, nosotros en Plain Concepts hemos cosas en las dos tecnologías, pero usamos más WPF que XNA.

Feliz desarrollo de Surface.

[Conferencia] Desarrollo de aplicaciones para Microsoft Surface en DevDays09

Hola a todos, el 18 y 19 de Febrero en Portugal se celebra el DevDay09 de Microsoft en la cual voy a dar con mi compañero Ricardo una charla sobre desarrollo de aplicaciones en Microsoft Surface, así que si estáis por la zona nos veremos allí.

Saludos. Luis.

WUX203 – Desenvolvimento Aplicações para Microsoft Surface

 

Datos:

Instituto Superior Técnico

Campus do TagusPark

Porto Salvo

 

Map picture

Primera aplicación de Microsoft Surface

Este fin de semana, y gracias a un proyecto que tenemos ente unos amigos, hemos hecho la primera aplicación de Microsoft Surface de PlainConcepts.com

 

Es un videojuego, porque claro no vamos a hacer una aplicación comercial, (de esas hacemos muchas en el día a día), y el videojuego es para jugar al hockey sobre hielo, os dejo unas capturas de pantalla y un vídeo en mp4.

El videojuego está hecho en WPF (Windows Presentation Foundation) + API de Surface  + Pixel Shaders, y está hecho a 1080P con una resolución de 1920×1200

Flowkey Flowkey2

Flowkey3 Flowkey4

http://www.luisguerrero.net/downloads/flowkey.zip [Video MP4, 13MB]

Personas que han participado en este 12Meses12Proyectos, una oportunidad de hacerse rico, TM:

  • Luis Guerrero
  • Ricardo Acosta
  • Anton Molleda
  • Pedro Laguna
  • Olmo del corral
  • Vicente Cartas

Espero que os guste. Saludos. Luis.

[Evento] Windows Presentation Foundation y Silverlight para programadores (Recursos)

Del 1 al 5 de Diciembre en Vic (Barcelona), se celebró un curso sobre Windows Presentation Foundation y Silverlight, en este post pondré los ejemplos de codigo que se generarón en el curso, además enlaces de intenrés y libros recomendados.

image

En el Zip que os poideis descargar desde aquí, encontrareis ejemplos sobre:

  • UnhandledException, evento que se lanza en el dominio de aplicación cuando una excepción no es filtrada y se propaga hacia abajo en la pila de llamadas.
  • Un ejemplo de EntityFramework
  • El ejemplo de la pelota moviéndose (sin colisiones), hecha con ContenContent
  • Dos aplicaciones Silverlight
  • Un ejemplo de concatenación de cadenas con += y con StringBuilder (nunca usar +=).
  • El código de la aplicación de WFP con los ejemplos de DependencyProperty y demás cosas de WPF.

 

Saludos. Luis.

[Curso] WPF para programadores de Windows Forms 7

Hola a todos de nuevo, volvemos a la carga con otro artículo sobre WFP para programadores de Windows Forms. En este articulo voy a hablar sobre los diferentes tipos de controles que hay en WPF y como encontramos similitud con los controles de WF.

En artículos anteriores estuvimos hablando sobre el árbol de herencia de WPF y sobre las diferentes clases de las que heredaba un control normal de WPF, pusimos como ejemplo Label, al ser un control que vamos a usar mucho en nuestras aplicaciones.

System.Object
  System.Windows.Threading.DispatcherObject
    System.Windows.DependencyObject
      System.Windows.Media.Visual
        System.Windows.UIElement
          System.Windows.FrameworkElement
            System.Windows.Controls.Control
              System.Windows.Controls.ContentControl
                System.Windows.Controls.Label

Ahora en este curso nos vamos a posicionar en System.Windows.UIElement y vamos a ver las diferentes ramas de las cuales salen los demás controles de WPF y ver que funcionalidad nos proporciona.

UIElement es la primera clase de WPF que tiene soporte para dibujado (heredado de Visual), soporte para entrada (Input, heredado de DispatcherObject) y soporte para RouteEvents. Así que técnicamente esta es la clase que tendríamos que usar si queremos empezar a dibujar algo en la pantalla, es la clase que da un soporte más primitivo para el dibujado. En la clase UIElement tenemos un método virtual OnRender que podemos sobreescribir en nuestras clase para poder implementar la funcionalidad personalizada de dibujado de esa clase, en la que se nos pasa un DrawingContext con el que dibujamos la UI del control. DrawingContextes muy parecido a la clase Graphics de GDI y de hecho tiene métodos muy parecidos.

Aquí podemos ver un ejemplo de un control que se dibuja a si mismo con este método.

public class WigoVisual : UIElement
    {
        private Pen pincel;
        public WigoVisual()
        {
            pincel = new Pen(new SolidColorBrush(Colors.Red), 20);
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            drawingContext.DrawLine(pincel, new Point(0, 0), new Point(100, 100));
            drawingContext.DrawRectangle(new SolidColorBrush(Colors.Green), new Pen(new SolidColorBrush(Colors.Blue), 5),
                new Rect(0, 0, 20, 20));
        }
    }

El control dibuja una línea desde (0,0) hasta (100,100) con el bolígrafo especificado, evidentemente esta clase no tiene soporte para Layout de ninguna manera porque nosotros al dibujar el control no tenemos en cuenta propiedades con Width o ActualWidth para dibujar la línea.

Como podréis comprender esta clase es muy básica en cuanto al soporte de Layout pero muy ponente para un dibujado más personalizado, si queremos ganar en funcionalidad existente tenemos que bajar en el árbol de herencia. La única clase pública del framework que hereda de UIElement es FrameworkElement, y es en esta clase donde se define mucha de la funcionalidad común de todos los controles del Framework, (es por eso que creo que se llama FrameworkElement).

FrameworkElement añade una serie de funcionalidad para que el soporte de Layout de la clase sea mucho más rico que con UIElement. Veámosla.

· Definición del sistema de Layout: define una serie de métodos que eran virtuales en UIElement para implementar el sistema de Layout de WPF.

· Define el árbol lógico de una aplicación WPF y deja la responsabilidad de definir cuál es a las clases herederas.

· Define los eventos del ciclo de vida de un control, eventos como Initialized, Loaded, Unloaded.

· Soporte para enlace a datos dinámico y búsqueda dinámica de recursos estáticos dentro del control.

· Define los estilos (Style) para los controles.

· Define el soporte para las animaciones de los controles.

FrameworkElement define una serie de propiedades básicas con las que podremos empezar a definir el Layout y los demás propiedades de la UI, a partir de esta clase podemos empezar a realizar Layout más complicados sin tener que explícitamente programarlos.

WPF permite que los programadores puedan sacar todo el partido del Layout a partir de una serie de clases que añaden funcionalidad especifica del nivel en el que se encuentran. Si volvemos atrás en el tiempo y recordamos como en Visual Basic 6 se hacían las cosas, los programadores más avanzados se hacían los cálculos ellos mismos para hacer que el control se moviese o fluyera a través del formulario, pero todos los cálculos tenían que ser hechos a mano, después con Windows Forms Microsoft nos dio dos propiedades muy interesantes de la clase Control Anchor y Dock, que permiten que un control se puedan quedar pillado a alguna de sus caras, o lo que es lo mismo que mantenga siempre la misma distancia. Todo este trabajo que antes se hacía a mano en Visual Basic ahora lo tenemos de “serie” dentro de Windows Forms, pues si seguimos avanzando en WPF ese cálculo de distancias se ha llevado al extremo, eso no significa que no se calcule internamente sino que no somos nosotros los responsables de hacer esos cálculos sino que WPF los hace por sí mismo.

Saludos. Luis.

[Evento] Taller de Nuevos interfaces para aplicaciones ( WPF/SILVERLIGHT)

El lunes de la semana que viene en Pamplona, se va a celebrar un evento para programadores sobre WPF / Silverlight.

Introducción al WPF / Silverlight

La mayoría de desarrollos actuales son como los de hace 15 o 20 años: La información se presenta de la misma manera, no se tiene en cuenta para nada la usabilidad de la UI y la experiencia de usuario (UX). Con Windows Presentation Foundation se pueden crear aplicaciones para Windows y la Web de una manera mucho más flexible, los controles son mucho más potentes que los Common Controls de Win32. Ahora podemos integrar multimedia, animaciones, 3D, databinding avanzado, efectos (PixelShaders), etc. Todos los controles de WPF están acelerados por el hardware de tarjeta gráfica gracias a Microsoft DirectX, así que ahora vamos a sacar potencia de la tarjeta gráfica para la composición de la UI de nuestras aplicaciones. WPF supone una revolución en la manera de hacer aplicaciones, además con XAML podemos integrar dentro del flujo de creación a los diseñadores. Ahora tanto programadores como diseñadores comparten XAML a través de Microsoft Blend y Visual Studio 2008.

Con este curso, se pretende exponer de manera general todas las características que hace de WPF una de las tecnologías más rompedoras de Microsoft para UX.

El Centro de Excelencia Microsoft .NET (CES) quiere poner en marcha una  intensa jornada sobre WPF / Silverlight en la que contaremos con uno de los mayores expertos  en la materia: Luis Guerrero.

  • Audiencia: Desarrolladores, Jefe de Proyecto,
  • Requisitos previos: Conocimientos del entorno Visual Studio 2005 / Visual Studio 2008, UI

Temario:

· Introducción a WPF ¿Por qué otra herramienta para componer aplicaciones? Historia

· XAML, herramientas para WPF / Silverlight

· Layout

· Controles

· Dependency properties

· Routed Input Events

· Animaciones

· Databinding

· Estilos (Style)

 

 

http://www.cesnavarra.net/Lists/Eventos%20CES/DispForm1.aspx?List=05834153%2Deec1%2D4089%2Da40e%2D5355dbd305f4&ID=90&Source=http%3A%2F%2Fwww%2Ecesnavarra%2Enet%2Fdefault%2Easpx

WPF Advanced – ISealable

Recientemente en un cliente me ha ocurrido un caso muy extraño con las Dependecy Property de WPF y quiero escribir sobre el tema. Es un poco enrevesado pero creo que puede ser útil. Veamos.

WPF tiene un sistema de Binding muy potente que hace que se puedan hacer enlaces a datos a entidades de negocio. Pues bien el caso es que para que una clase pueda tener Dependency Property (DP a partir de ahora), es necesario que heredemos como mínimo de la clase DependencyObject que permite que una clase almacene DP, pues bien el caso es que mis entidades de negocio heredan de esta clase para que las propiedades que lo define sean DP y poder utilizar toda la potencia de WPF, hasta aquí todo bien. Para lo que no estéis tan al tanto de DP es un sistema que permite tener propiedades con metadatos y un sistema centralizado de almacenamiento de valores. Para definir un DP es así.

public static readonly DependencyProperty CurrentBusinessEntityProperty =
    DependencyProperty.Register("CurrentBusinessEntity", typeof(BusinesEntity), typeof(BusinessManager), new UIPropertyMetadata(new BusinesEntity()));

public BusinesEntity CurrentBusinessEntity
{
    get { return (BusinesEntity)GetValue(CurrentBusinessEntityProperty); }
    set { SetValue(CurrentBusinessEntityProperty, value); }
}

Si os fijáis en el primer parámetro del UIPropertyMetadata es el valor predeterminado del DP, hay que tener en cuenta que esto se inicializa en el constructor de tipo (static en C#) y que por tanto es seguro frente a subprocesos. Pues bien si el valor predeterminado que nosotros usamos es un objeto que hereda de DependencyObject WPF lanza una excepción. La excepción es:

clip_image002

Default value for the ‘CurrentBusinessEntity’ property cannot be bound to a specific thread.

Es algo bastante raro, porque si pongo null funciona si hago otro tipo de DP funciona correctamente y solamente si utilizo como valor predeterminado una entidad de negocio mía (que hereda de DependencyObject) la no cosa funciona. Pero la excepción que se nos lanza no nos da mucha información. Utilizando el código fuente del framework y depurando todo el proceso de inicialización del DP, llego hasta este bonito trozo del código.

if (checkThreadAffinity) 
{
// If the default value is a DispatcherObject with thread affinity 
// we cannot accept it as a default value. If it implements ISealable
// we attempt to seal it; if not we throw  an exception. Types not
// deriving from DispatcherObject are allowed - it is up to the user to
// make any custom types free-threaded. 

DispatcherObject dispatcherObject = defaultValue as DispatcherObject; 

if (dispatcherObject != null && dispatcherObject.Dispatcher != null)
{ 
    // Try to make the DispatcherObject free-threaded if it's an
    // ISealable.

    ISealable valueAsISealable = dispatcherObject as ISealable; 

    if (valueAsISealable != null && valueAsISealable.CanSeal) 
    { 
        Invariant.Assert (!valueAsISealable.IsSealed,
               "A Sealed ISealable must not have dispatcher affinity"); 

        valueAsISealable.Seal();

        Invariant.Assert(dispatcherObject.Dispatcher == null, 
            "ISealable.Seal() failed after ISealable.CanSeal returned true");
    } 
    else 
    {
        throw new ArgumentException(SR.Get(SRID.DefaultValueMustBeFreeThreaded, propertyName)); 
    }
}
}

En el que básicamente los señores que prograron WPF vienen a decir algo así como que el valor predeterminado es un DispathcerObject (en mi caso lo es) no pueden acceptar solo si implementa ISealable entonces lo sellan y si no pueden lanzan una excepción.

Una bonita manera de encontrar el error, continuando la investiagación, me dispongo pues a que mio clase implemente esta interfaz ISealable, pero cual es mi sorpresa que la interfaz no existe. La busco en el MSDN nada, abro el reflecto y para mi sorpresa me encuentro con esto

[FriendAccessAllowed]

internal interface ISealable

{

// Methods

void Seal();

// Properties

bool CanSeal { get; }

bool IsSealed { get; }

}

Que la internfaz es internal y encima se permite el acceso de los ensamblado amigos, pues con el mismo reflecto abierto me dispongo pues a ver que clases dentro del framework (por curiosidad mas que nada) implementan esta fatídica interfaz, y me encuentro con esto:

clip_image004

Que las tres únicas clases que implementan esa funcionalidad son FrameworkTemplate, Freezable y Style.

Los que halláis leído otras entradas de WPF sabréis que Freezable está presente en el árbol de algunos tipos de controles de WPF permitiendo que el contenido del control se pueda de alguna manera congelar para aumentar el rendimiento de la aplicación. Pues bien parece que para que pueda poner como valor predeterminado una entidad mia de negocio tengo que heredar no de DependencyObject sino de Freezable.

Resumen:

Esta una bonita historia sobre como WPF es a veces un sistema muy complejo que creo que ni la gente de Microsoft a veces entiende, no porque esté mal diseñada, sino por lo compleja que es. Así que me pareció un exceso tener que heredar de Freezable únicamente por poder poner una entidad de negocio mía como valor predeterminado de un DP, así que lo deje como estaba antes.

Ahora bien porque WPF hace esto?, al ser la DP usadas por una clase que hereda de DependencyObject esta clase tiene una propiedad muy importante el Dispatcher que es el gestor de threads dentro de WPF así que supongo que la comprobación vendrá por si el objeto se está iniciando en un thread que no contiene un Dispatcher o se tendrá que hacer invocación. Y por *intentar* hacer la vida más sencilla a los programadores Microsoft ofrece la clase Freezable, pero por lo menos lanza una excepción explicando el tema o algo porque sino la gente se vuelve loca.

 

Aquí teneis el codigo fuente de ejemplo [SealedExample.rar]

Saludos. Luis.