martes, 13 de abril de 2010

.NET 4 WPF: Reutilizando nuestro trabajo en Windows Forms

Hola a todos!

WPF es un nuevo y estupendo paradigma de programación que Microsoft a puesto en nuestras manos. Las posibilidades que ofrece este nuevo sistema son excepcionales, abstracción de la capa visual de la funcional, total libertad para modificar el “Look & Feel” de nuestras aplicaciones, un enlace a datos prodigioso, y muchas cosas más, pero llega un momento en la vida de todo programador WPF en que te preguntas… ¿Como abro un OpenFileDialog, un SaveFileDialog o un ColorDialog? ¿Donde está ese control tan chulo de windows forms llamado PropertyGrid o MaskedTextBox?
Bueno, la respuesta a todas estas preguntas es… WPF no tiene integrada ninguna de estas funcionalidades… ¿Como? ¡PAIN! Tranquilos, antes de que cunda el pánico y empiecen los saqueos, en estos casos, es políticamente correcto usar las versiones correspondientes de estos controles disponibles en Windows Forms.
Pero, ¿Porque no se incluyen en WPF? En el caso de los Dialog Box, estos pertenecen al propio sistema operativo, y están implementados a través de Windows Forms, por lo que reescribirlos en WPF sería perder la reutilización de código y duplicar funcionalidad. En cuanto a los controles como propertyGrid y MaskedTextBox, simplemente, al igual que otros controles, no están disponibles, pero en MS han tenido en cuenta este hecho y nos han dado un control de wpf llamado WindowsFormsHost este control es un contenedor que nos permite incrustar controles de windows forms dentro de el y usarlos en nuestra aplicación WPF, tanto controles standard de windows forms como controles creados por nosotros, con lo que se nos abre una puerta para reutilizar ese control que llevamos años usando y mejorando en Windows Forms.
Para trabajar con cualquier control o Dialog box de Windows Forms en nuestra aplicación WPF, lo primero que debemos hacer es añadir una referencia en nuestro proyecto al ensamblado System.Windows.Forms.

Usando Dialog box de sistema en nuestra aplicación WPF

Una de las situaciones más normales en la programación es tener que pedir al usuario que seleccione un archivo de su sistema, una carpeta o un archivo en el que guardar información. Para esto en Windows Forms tenemos los diálogos del sistema, OpenFileDialog, SaveFileDialog o FolderBrowserDialog, y en WPF podemos acceder a ellos de una manera muy sencilla y transparente:
Para Acceder a cualquiera de los tres mencionados arriba, OpenFileDialog, SaveFileDialog y FolderBrowserDialog podemos hacerlo así:
'Usar OpenFile Dialog
Private Sub btnMostrarOpenFile_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnMostrarOpenFile.Click
    Dim AbrirArchivo As New System.Windows.Forms.OpenFileDialog()
    Dim Resultado As System.Windows.Forms.DialogResult
    Resultado = AbrirArchivo.ShowDialog()
    If Resultado = Forms.DialogResult.OK Then

    End If
End Sub

'Usar SaveFile Dialog
Private Sub btnMostrarSaveFile_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnMostrarSaveFile.Click
    Dim GuardarArchivo As New System.Windows.Forms.SaveFileDialog()
    Dim Resultado As System.Windows.Forms.DialogResult
    Resultado = GuardarArchivo.ShowDialog()
    If Resultado = Forms.DialogResult.OK Then

    End If
End Sub

'Usar DirectoryBrowser Dialog
Private Sub btnMostrarDirBrowser_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnMostrarDirBrowser.Click
    Dim BuscarDirectorio As New System.Windows.Forms.FolderBrowserDialog()
    Dim Resultado As System.Windows.Forms.DialogResult
    Resultado = BuscarDirectorio.ShowDialog()
    If Resultado = Forms.DialogResult.OK Then

    End If
End Sub

Como se puede observar, en vez de importar el namespace Windows.Forms estoy usando la ruta completa, esto se debe a que existen varios tipos cuyo nombre entraría en conflicto entre WPF y WindowsForms, por lo que es recomendable usar la ruta de ensamblado completa en vez de importar el namespace.
Estos Diálogos son sencillos, pues el valor que nos interesa es una cadena, que puede ser usada directamente, todo se complica un poco más al usar el ColorDialog, pues en Windows Forms se usa la estructura de color System.Drawing.Color mientras que WPF usa System.Windows.Media.Color y no existe un método directo de convertir el valor de una en la otra, por lo que hay que  convertirlo con un método propio, antes que nada necesitaremos incluir una referencia a System.Drawing en nuestro proyecto.

Primero creamos nuestro método para traducir las estructuras de color, básicamente obtenemos la representación argb del color y la descomponemos en sus componentes a,r,g,b y ya con ellos creamos el color de WPF:
'Convertimos el color de WindowsForms en un color WPF:
Private Function ColorFromWFToWPF(ByVal ColorOriginal As System.Drawing.Color) As Color
    Dim ColorDestino As Color
    Dim i As Integer = ColorOriginal.ToArgb()
    Dim a, r, g, b As Byte
    a = CType(i >> 24 And 255, Byte)
    r = CType(i >> 16 And 255, Byte)
    g = CType(i >> 8 And 255, Byte)
    b = CType(i And 255, Byte)
    ColorDestino = Color.FromArgb(a, r, g, b)
    Return ColorDestino
End Function

Ahora ya podemos usar el ColorDialog:

'Usar ColorDialog
Private Sub btnMostrarColorPicker_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnMostrarColorPicker.Click
    Dim ColorPicker As New System.Windows.Forms.ColorDialog()
    Dim Resultado As System.Windows.Forms.DialogResult
    Resultado = ColorPicker.ShowDialog()
    If Resultado = Forms.DialogResult.OK Then
        Dim ColorWPF As System.Windows.Media.Color
        ColorWPF = ColorFromWFToWPF(ColorPicker.Color)
    End If
End Sub

Como ves, en general es muy sencillo poder usar los dialogos del sistema en nuestra aplicación WPF sin tener que reinventar la rueda.

Usando controles WinForm en WPF

Tenemos que reconocer que WPF integra una suite de elementos increíble y muy completa, pero sería una pena no poder aprovechar esos controles propios que llevamos años desarrollando y usando, o aprovecharnos de la gran base de controles gratuitos que existe para Windows forms, el equipo de WPF ha tenido esto en cuenta y nos ha dado una estupenda herramienta, que nos permite incrustar cualquier tipo de control Windows Forms en nuestra aplicación WPF con un mínimo esfuerzo, hablamos del elemento WindowsFormsHost.

Para usarlo, solo debemos incluirlo en nuestro código xaml:
<WindowsFormsHost Height="25" Name="WindowsFormsHost1" Margin="5">

</WindowsFormsHost>

Y dentro podremos incluir cualquier control Windows Forms que deseemos, para tener acceso a los controles winform desde nuestro xaml, debemos registrar el namespace System.Windows.Forms en los namespaces de nuestro xaml:
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"

Y con esto ya podemos incluir nuestro control de windows forms dentro del WindowsFormsHost:
<WindowsFormsHost Height="25" Name="WindowsFormsHost1" Margin="5">
    <wf:MaskedTextBox Name="MaskTelefono" Mask="(000)-00-00-00"></wf:MaskedTextBox>
</WindowsFormsHost>

Como ves en el ejemplo de arriba, incluso es posible acceder a las propiedades del control winform desde nuestro código xaml, pero lo normal es que necesitemos usar nuestro control en code behind, para ello hay que crear una nueva variable del mismo tipo que nuestro control y asignarle la propiedad Child del control WindowsFormsHost, una vez hecho esto tendremos acceso a los métodos y propiedades de nuestro control:
Private Sub btnMuestraTelefono_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnMuestraTelefono.Click
    Dim unMasked As System.Windows.Forms.MaskedTextBox
    unMasked = CType(WindowsFormsHost1.Child, System.Windows.Forms.MaskedTextBox)
    'Mostramos el telefono.
    MessageBox.Show(unMasked.Text)
    'Ahora cambiamos el texto:
    unMasked.Mask = "(000)-(00)-(00)-(00)"
End Sub
Podemos obtener el valor de las propiedades, pero también podemos modificarlas, con lo que nuestro control es totalmente funcional.
Aquí podeis ver una imagen de la aplicación hecha para este post y más abajo teneis el código fuente para descargarlo, hecho en Visual Studio 2010 RC:

4 comentarios:

  1. Pero que es WPF, acaso es como basi y c#?, se necesita aprender otro lenguaje?, se parece a basic, me gustaria saber mas de el, saludos.-

    ResponderEliminar
  2. Hola DON HARD
    WPF es una nueva forma de crear aplicaciones, tanto para escritorio como para ser ejecutadas en Navegadores, se pueden realizar tanto en Visual Basic como en c#, pero cambian muchas cosas, por ejemplo la parte visual de la pantalla se realiza en lenguaje xaml, (todavía existe el diseñador de visual studio, que crea el código xaml por ti) y muchas cosas más...

    ResponderEliminar
  3. hola amigo, sabes busco una ayudita para un proyecto de clase es sobre una caja registradora pero me pierdo en la parte del pago, me podrias ayudar con esto?

    ResponderEliminar
  4. Hola Josué,

    Cualquier duda que tengas, yo y otros compañeros podemos ir ayudandote en los foros de msdn:

    El foro de msdn de Visual Basic.NET (español):
    http://social.msdn.microsoft.com/Forums/es-ES/vbes/threads

    El foro de msdn de Windows Forms (español):
    http://social.msdn.microsoft.com/Forums/es-ES/winformses/threads

    El foro de msdn de Windows Presentation Foundation:
    http://social.msdn.microsoft.com/Forums/es-ES/wpfes/threads

    Un saludo!

    ResponderEliminar