el Guille, la Web del Visual Basic, C#, .NET y más...

Equivalencias entre las clases de WPF y las de .NET (1)

 
Publicado el 16/Ago/2007
Actualizado el 21/Ago/2007
Autor: Guillermo 'guille' Som

WinFX .NET 3.0: Equivalencias entre las clases de WPF y las de .NET. Algunas cosas que debes tener en cuenta si quieres usar código de .NET 2.0 en aplicaciones WinFX (.NET 3.0). Pulsa aquí para ir a la segunda parte.



 

Introducción:

Pues... hasta ayer, todo lo que he hecho con las aplicaciones para WPF (Windows Presentation Foundation), es decir, las aplicaciones basadas en .NET Framework 3.0 (antes conocido como WinFX), y que como seguramente sabrás están basadas en la nueva tecnología que Microsoft introdujo en Windows Vista, aunque actualmente esta versión (3.0) de .NET Framework también se puede usar en Windows XP con SP2 o en Windows 2003 con SP1 (o superior), pues... (es que siempre me aparto de lo que tengo que decir para dar explicaciones, en fin...), pues como te decía, hasta ayer todo lo que he hecho con WinFX es para "jugar" un poco, es decir, saber cómo funciona, cómo se pueden hacer algunas cosas, etc. Algunas de esa pruebas ya las he publicado en esta sección de WinFX (no me decido a dejar de llamarlo WinFX, lo siento), y algunas otras que iré publicando... no solo para que a ti te sirva lo que publico, que también, pero es que como tengo la cabeza que tengo y tengo tantas pruebas en mi disco duro, pues algunas veces me cuesta trabajo encontrar la forma de hacer "algunas" cosas, así que... esto que publico en esta sección (y en muchas otras de mi sitio), me sirven de "chuletario", es decir, me sirven para encontrar cómo hacer aquello que quiero hacer... es decir, ¡el Guille también usa el sitio del Guille! (hay que practicar con el ejemplo, que dicen... je, je).

 

La batallita del agüelo

Te cuento la historia... si no quieres leer este "rollo"; puedes seguir leyendo más abajo.

Yo tengo una utilidad que uso para reproducir ficheros MP3, en realidad (teóricamente) reproduce cualquier tipo de fichero musical que el Windows sea capaz de reproducir, aunque yo lo uso para reproducir listas de ficheros MP3, normalmente con la extensión .m3u, aunque acepta hasta ficheros .txt siempre y cuando sean "ficheros reproducibles" (musicalmente hablando).

Esa utilidad la tengo desde hace muuuchos años, de hecho, en mi sitio publiqué una de esas "reencarnaciones", y está en la sección de VB6: gsPlayWMP Una utilidad para reproducir listas de ficheros de audio (WAV y MP3), usando el Windows Media Player del IE5

La que uso actualmente es una versión mejorada con respecto a esa que publiqué. Y mi versión ahora está hecha con Visual Basic 2005, pero antes lo estuvo con Visual Basic .NET (2002) y con Visual Basic .NET 2003, es decir, la voy actualizando con cada nueva versión de Visual Basic.

Hace unos meses (según la fecha de los ficheros, fue el 7 de Febrero de este año), pensé de convertirla para usar todo lo que WPF ofrece, sobre todo por el tema de los "colorines" y el uso de los Expander, con idea de contraer o mostrar algunas partes de la aplicación, más que nada para tener solo visible aquella parte que en realidad voy a usar en cada momento.

La verdad es que empecé a hacer el diseño del "formulario" o ventana principal con la Beta 2 de Expression Blend, ya que las betas que tenía por esas fechas de Visual Studio Orcas no permitían hacer muchas cosas con el tema del diseño de las "ventanas de WPF", ahora con la Beta 2 de Visual Studio 2008 tampoco, pero... ese es otro tema. Aunque es un tema importante... ya que ayer se me colgó como 10 veces el Visual Studio 2008 Beta 2 al trabajar con la aplicación esta que te comento... en fin... es una beta, que se puede esperar...

Pero ayer (y anteayer) me puse "en serio" a hacer la aplicación, en realidad a hacer la conversión de mi aplicación de Visual Basic 2005 a Visual Basic 2008 con WPF, que en realidad no hace nada que no se pueda hacer ahora, ya que en el fondo lo que uso son las clases de WinFX, que se pueden usar actualmente sin necesidad de usar ninguna beta, pero... como el Visual Studio 2008 Beta 2 soporta trabajar con ese tipo de aplicaciones, pues es más fácil que hacerlo todo "a pelo".

Aunque todo hay que decirlo, seguramente será porque esto de crear aplicaciones para WPF está pensado para que san los "diseñadores" los que se encarguen de preparar el "formulario" o la interfaz de presentación, pero es un "peñazo" trabajar con ese tipo de aplicaciones... sobre todo para los que estamos acostumbrados a trabajar con las "bondades" de un buen diseñador de formularios... y es que esto de crear la interfaz del usuario de una aplicación para WPF con Visual Studio 2008 Beta 2 e incluso con Expression Blend es... bueno... me reservo el adjetivo para no herir susceptibilidades, je, je...

Al final he acabado haciendo a mano casi todo... pero al menos el Visual Studio 2008 tiene un buen soporte para manejar las propiedades y demás cosas que hay que usar en el código XAML de la ventana (antes formulario) en la que se mostrará la interfaz de usuario.

En esto le gana al Expression Blend por goleada, ya que el diseñador "ese", que se maneja muy bien con las cosas que ofrece el WPF está muy bien... aunque no me gusta el "aspecto de la interfaz" del programa, pero... ese también es otro tema... Ya que el IntelliSense de Visual Studio 2008 es "casi perfecto" (no soporta todo, pero se le puede perdonar), ya que el Expression Blend... bueno, creo que no conoce ni de lejos que es eso de IntelliSense, al menos cuando vamos a trabajar con el código XAML.

Pues bien... y ya termino con la batallita, no solo han sido "problemas" con el diseño del interfaz gráfico, que al final casi he conseguido lo que me proponía, con mucho trabajo y mucho escribir código a mano directamente en el código XAML, pero... al menos casi he logrado lo que me proponía... ya te iré contando también algunas de esas cosas que he "aprendido" por el camino... pero esas cosas te las contaré en otro momento, ya que lo que te voy a contar hoy es otro de los problemas con los que me he topado al convertir el código... algunos, (la mayoría) los he resuelto, aunque aún me quedan muchos otros que no tengo ni repajolera idea de cómo solucionarlos.... pero no cejaré en el intento... je, je... A lo que iba, además de los problemas del interfaz, me he topado con problemas de código... es decir, las cosas que "yo sé hacer" con una aplicación para .NET Framework 2.0 no me sirven... bueno, si me sirven, pero no son iguales que para .NET Framework 3.0, al menos si las clases que estoy usando son las propias de esa versión.

Y a partir de la siguiente sección te contaré algunas de esas "incompatibilidades" y lo que puedes hacer para remediarlas.

 

Algunas equivalencias entre las clases de WPF y las de .NET

Como te comentaba antes en "la batallita del agüelo", cuando quieres usar en una aplicación de WinFX algunas cosas a las que estás acostumbrado en .NET Framework 2.0, puedes encontrarte con algunas sorpresas.

Seguramente las podrás solucionar fácilmente, pero... para que te sea más cómodo, aquí te explico algunas.
En otras actualizaciones de este artículo (o de uno nuevo que continúe este) te explicaré más cosas.

1- No existe la propiedad Text de los controles

Debido a como funcionan los controles de WPF, la propiedad Text a la que estamos acostumbrados ya no está, ahora se puede usar la propiedad Content, que aunque es mucho más "potente" que el mero hecho de mostrar texto, al menos será la que usemos para asignar los textos a mostrar en algunos controles.

LabelStatus.Content = "¡Hola, Mundo!"

2- La ventana principal tampoco tiene Text

Algo que me gustaba de la versión de .NET es que se consiguió eliminar la "multipliciidad" de propiedades que servían para mostrar un texto, ahora con WPF volvemos "pa trás" y... en fin... Bueno, decirte que la ventana principal (siempre que sea una ventana basada en la clase Window), el texto a mostrar en la barra del título, se asigna por medio de la propiedad Title:

Me.Title = "El título de la ventana"

3- No existe la propiedad Checked (ahora es un evento)

Ahora, la propiedad Checked de los controles que aceptan esa propiedad se llama IsChecked.
Por otra parte, Checked es un evento que se produce cuando el control en cuestión se "chekea".

If chkMinimizar.IsChecked Then

4- No existe el evento CheckedChanged

Ese evento es el que normalmente solemos usar para comprobar si se ha cambiado el estado de chequeado o no de un control que soporta ese estado de si "IsChecked" o no. Bueno, podemos usar tanto CheckedChanged como CheckStateChanged, cualquiera de los dos nos avisa de que ha cambiado el estado "chequeado" del control.

Pero en los controles de WinFX ahora hay dos eventos para averiguar ese cambio, por un lado tenemos el evento Checked para indicar que el control está "marcado" y por otro tenemos el evento UnChecked para indicarnos que no está marcado. Pero debido a que tanto uno como el otro usan los mismos parámetros en el método del evento, podemos unificarlos los dos y seguir usando el mismo código que teníamos, por ejemplo, en las versiones anteriores a .NET Framework 3.0 (WinFX) el código sería algo así:

Private Sub chkMezclar_CheckedChanged( _
            ByVal sender As Object, _
            ByVal e As EventArgs) Handles chkMezclar.CheckedChanged
    If Me.inicializando Then Exit Sub
    '
    Me.mezclarCanciones = Me.chkMezclar.Checked
    If Me.chkMezclar.Checked Then
        mINI.IniWrite(sFicIni, "General", "Mezclar canciones", "1")
    Else
        mINI.IniWrite(sFicIni, "General", "Mezclar canciones", "0")
    End If
End Sub

En una aplicación para WPF sería de esta otra forma: (usando los dos eventos)

Private Sub chkMezclar_Checked(ByVal sender As Object, _
                               ByVal e As RoutedEventArgs) _
                               Handles chkMezclar.Checked
    If Me.inicializando Then Exit Sub

    Me.mezclarCanciones = True
    mINI.IniWrite(sFicIni, "General", "Mezclar canciones", "1")
End Sub

Private Sub chkMezclar_Unchecked(ByVal sender As Object, _
                                 ByVal e As RoutedEventArgs) _
                                 Handles chkMezclar.Unchecked
    If Me.inicializando Then Exit Sub

    Me.mezclarCanciones = False
    mINI.IniWrite(sFicIni, "General", "Mezclar canciones", "0")
End Sub

Que podemos "unificar" en un solo método y usarlo tal como hacemos hasta ahora:

Private Sub chkMezclar_CheckedChanged(ByVal sender As Object, _
                                      ByVal e As RoutedEventArgs) _
                                      Handles chkMezclar.Checked, chkMezclar.Checked
    If Me.inicializando Then Exit Sub

    Me.mezclarCanciones = Me.chkMezclar.IsChecked.Value

    If Me.chkMezclar.IsChecked Then
        mINI.IniWrite(sFicIni, "General", "Mezclar canciones", "1")
    Else
        mINI.IniWrite(sFicIni, "General", "Mezclar canciones", "0")
    End If
End Sub

 

5- Muchas de los tipos habituales de Windows.Forms están definidos en Windows

Pues eso... que muchos de eventos habituales, clases, enumeraciones, etc., que usamos en nuestros proyectos par aplicaciones de escritorio de .NET 2.0 y que suelen estar definidos en el espacio de nombres System.Windows.Forms, ahora están definidos en System.Windows.

Debes tener en cuenta que aunque el espacio "raíz" sea el mismo (System.Windows), en realidad son librerías distintas.

Si quieres usar las cosas "viejas" de System.Windows.Forms, debes agregar una referencia a la librería System.Windows.Forms.dll.

Pero NO TE RECOMIENDO que agregues una importación al espacio de nombres System.Windows.Forms, ya que si lo haces, pues tendrás "inconsistencias" entre esas clases o tipos que se llaman igual en los dos espacios de nombres.

 

6- La clase MessageBox y las enumeraciones de esa clase

Un caso concreto de las clases que están definidas en los dos sitios es la "habitual" MessageBox. La clase en sí no da problemas, pero si los parámetros.

Por ejemplo, este código que funciona bien en una aplicación de .NET 2.0 (o anterior), fallará en una aplicación para WPF:

MessageBox.Show("ERROR: " & ex.Message, _
            "Error al tocar", _
            MessageBoxButtons.OK, 
            MessageBoxIcon.Exclamation)

Tendrás que usar el siguiente código:

MessageBox.Show("ERROR: " & ex.Message, _
                "Error al tocar", _
                MessageBoxButton.OK, _
                MessageBoxImage.Exclamation)

A lo mejor piensas que son iguales. Pero no te fíes... esto no es uno de esos juegos de buscar las diferencias.

Esto es algo "común" en las clases de WPF, el que se inventó los nombres seguro que estaba "fumao" o recibió una inspiración divina... en serio, seguramente pensó que es más "guay" (cool, molón, chido, etc.)  usar nuevos nombres "más adecuados", ya que si te fijas bien, los tipos de botones a mostrar en las aplicaciones "actuales" están definidos en la enumeración MessageBoxButtons, mientras que en WPF están en MessageBoxButton, es decir, lo mismo, pero sin la "recomendada" ese del plural al final.
Lo mismo ocurre con la enumeración de los iconos a mostrar, en las aplicaciones de Windows.Forms se usa MessageBoxIcon y en WPF es MessageBoxImage.

Y como esto... muchos casos parecidos, por ejemplo... (sigue leyendo).

7- Cambios en Drag & Drop

En este caso, hay varios cambios... de nombres, no de forma de usarlo.

Por ejemplo, hay que tener en cuenta que cuando definimos el método para interceptar los eventos de arrastrar y soltar (Drag & Drop), en nuestras aplicaciones de .NET "normal" el código será más o menos como este:

Private Sub fgsPlayMCIAPI_DragDrop(ByVal sender As Object, _
                                   ByVal e As System.Windows.Forms.DragEventArgs) _
                                   Handles MyBase.DragDrop
    addDrop2List(e.Data)
End Sub

Private Sub fgsPlayMCIAPI_DragOver(ByVal sender As Object, _
                                   ByVal e As System.Windows.Forms.DragEventArgs) _
                                   Handles MyBase.DragOver
    e.Effect = DragDropEffects.Copy
End Sub

Y en una aplicación de WPF, eso mismo se haría así:

Private Sub fgsPlayMCIAPI_DragDrop(ByVal sender As Object, _
                                   ByVal e As DragEventArgs) _
                                   Handles MyBase.Drop
    addDrop2List(e.Data)
End Sub

Private Sub fgsPlayMCIAPI_DragOver(ByVal sender As Object, _
                                   ByVal e As DragEventArgs) _
                                   Handles MyBase.DragOver
    e.Effects = DragDropEffects.Copy
End Sub

Busca las diferencias (que son las que te he marcado en el primer código).

En el caso del parámetro, volvemos a lo que te he comentado en el punto 5.
Se llaman igual, pero están definidos en espacios de nombres distintos.

El segundo cambio es el nombre de la propiedad a la que hay que asignar el "efecto", y es que ahora está en plural, mientras que antes estaba en singular... sin más comentarios de los que ya hice antes.

El tercero, y más importante, es el cambio del nombre del evento que "acepta" la operación de soltar, hasta ahora se llamaba DragDrop, pero en WinFX se llama Drop. ¡Que lo sepas!

8- Cambios en las teclas pulsadas en los eventos KeyDown y KeyUp

Otro follón con los nombres de las propiedades y demás... en este caso, si desde el evento KeyDown o KeyUp quieres averiguar que la tecla pulsada es una en concreto, pues... debes usar otros nombres.

Por ejemplo, este sería el código para .NET "normal":

Private Sub ListView1_KeyDown(ByVal sender As Object, _
            ByVal e As System.Windows.Forms.KeyEventArgs) _
            Handles lvCanciones.KeyDown
    ' Si se pulsa la tecla DEL, borrar el seleccionado
    If e.KeyCode = System.Windows.Forms.Keys.Delete Then
        If lvCanciones.SelectedItems.Count = 0 Then Return
        ' Borrar
        lvCanciones.Items.RemoveAt(lvCanciones.SelectedItems(0).Index)
        guardarListaActual()
    End If
End Sub

No te resalto las diferencias para que mejores tu capacidad visual, je, je, (esto como lo de "Brain trainer ese").
Este sería el código equivalente para WPF:

Private Sub ListView1_KeyDown(ByVal sender As Object, _
                              ByVal e As KeyEventArgs) _
                              Handles lvCanciones.KeyDown
    ' Si se pulsa la tecla DEL, borrar el seleccionado
    If e.Key = Key.Delete Then
        If lvCanciones.SelectedItems.Count = 0 Then Return
        ' Borrar
        lvCanciones.Items.RemoveAt(lvCanciones.SelectedItems(0).Index)
        guardarListaActual()
    End If
End Sub

Aparte de que en lugar de la propiedad KeyCode hay que usar Key y de que en lugar de comprobar si es un valor de la enumeración Keys ahora esa enumeración se llama Key, debemos recordar que los espacios de nombres usados en WPF son distintos a los de las aplicaciones de Windows.Forms de .NET 2.0, sí, ya sé que te lo he dicho varias veces, pero...

Además, me he dado cuenta de que ese código solo funcionará si Option Strict está desconectado. Ya que... mira el siguiente punto.

9- La colección SelectedItems de un ListView es de tipo Object (IList)

En este caso, voy a hablar de la colección SelectedItems del control ListView. Pero estos cambios también afectan a los controles ComboBox y ListBox, aunque menos.

En .NET 2.0, la colección SelectedItems del ListView devuelve una colección de tipo ListView.SelectedListViewItemCollection (definida en System.Windows.Forms). Esa colección devuelve valores del tipo ListViewItem, por tanto, podemos acceder a ciertos valores o propiedades del elemento seleccionado que estamos "observando".

Pero en WPF no es lo mismo, ya que esa colección es de tipo IList, por tanto, devuelve valores de tipo Object.

 ¿Por qué? Por lo que ya te comenté antes, porque ahora en WPF el contenido de los controles pueden ser... ¡cualquier cosa! Es decir, un control ListView puede tener más cosas que "simples" objetos del tipo ListViewItem, así que... hay que tener en cuenta esto.

En el código que te mostré antes, para saber el índice del primer elemento que estaba seleccionado usaba este código:

lvCanciones.SelectedItems(0).Index 

Para ese caso concreto en el que quiero averiguar el índice del primer elemento seleccionado, lo puedes hacer así:

lvCanciones.SelectedIndex 

Y si lo que quieres es recuperar el texto del elemento seleccionado... mira el punto 10:

10- El elemento seleccionado de un ListView es de tipo Object

Siguiendo con el tema del punto anterior, decirte que el valor devuelto por la propiedad SelectedItem de la clase ListView ahora en WPF es de tipo Object (por aquello de que puede contener cualquier cosa, bla, bla, bla).

Pero si sabes que lo que contiene es simplemente texto, y el código que usabas hasta ahora es algo como esto:

toolTip1.SetToolTip(lvCanciones, _
                    "Seleccionado: " & lvCanciones.SelectedItems(0).Text)

Aquí tienes que "ver" dos cosas, una es que el texto se accede con SelectedItems(0).Text, es decir, el texto del primer elemento que está seleccionado se asigna al "tool tip" relacionado con el ListView.

En WPF yo lo he hecho así:

lvCanciones.ToolTip = "Seleccionado: " & lvCanciones.SelectedItem.ToString

Es decir, ahora SelectedItem nos indica el elemento que está seleccionado en el control ListView, y como resulta que ese "objeto" devuelto es de tipo Object, y "yo sé" que en realidad es una cadena de texto, pues simplemente lo convierto en texto usando el método ToString.

La segunda cosa es que en WPF todos los controles (creo que todos) tienen la propiedad Tooltip, por tanto, a esa propiedad es a la que asigno el "tool tip" que quiero mostrar.

11- El evento SelectedIndexChanged ahora es SelectionChanged

Al menos en el control ListView (en los otros controles no lo he comprobado, pero al menos en el control ComboBox también se llama así).

La cosa es que el "efecto" conseguido es el mismo... saber si se ha seleccionado otro elemento distinto al que antes estaba seleccionado, pero... para que sepas que ahora se llama de otra forma.

12- Los eventos de inicio y cierre de la ventana principal

Seguramente de estos dos eventos, en todas tus aplicaciones usas el que "inicia" el formulario principal, que en las aplicaciones "normales" es el evento producido por el evento Load.

En WPF ese evento ya no existe.

No te asustes, je, je, que no existe con ese nombre, ya que ahora se llama Loaded, que seguramente le habrán cambiado el nombre para que quede claro que el formulario (perdón, la ventana) ya está cargado.

El evento de cierre de los formularios de nuestras aplicaciones clásicas (de .NET 2.0) utilizan el evento FormClosing para avisar de que el formulario se está cerrando. En ese evento se avisa de varias cosas que podemos usar para saber más sobre cómo se cierra.

En WPF se llama como "antes", es decir, es Closing y el tipo de parámetro que recibe es del mismo tipo: System.ComponentModel.CancelEventArgs. Osea... casi un paso atrás, je, je.

 

 

Hay más, sobre todo cosas que me quedan por averiguar

Bueno, pues ya está bien por hoy... aunque hay más cosas que debes saber...

A ver si me apunto las cosas que vaya encontrando (que tengo más cosas que han cambiado, pero como no las he ido apuntando, pues pasa lo que pasa, que solo he "encontrado" las que más o menos me he ido encontrando en el repaso que he hecho, pero hay más "incompatibilidades", sobre todo en cosas "comunes" o al menos comunes para lo que yo suelo hacer, ya que ahora, pues... en fin... que ya no se puede hacer como antes... y en algunos casos, aún no he averiguado cómo hacerlo... seguramente porque no me he puesto "en serio", je, je... o lo mismo nunca averiguo cómo hacerlo, que... ya sabes que el Guille no lo sabe todo, je, je...

Por si te sientes "animado" a ayudarme, te digo las cosas que me gustaría saber cómo hacer:

  • Cómo agregar "subitems" a un item de un ListView
  • Cómo saber en qué columna se ha pulsado (en el control ListView)
  • Cómo clasificar los elementos del ListView
  • Cómo asignar los valores de FullRowSelect, GridLines, MultiSelect, etc. del modo "View" de un ListView
  • Cómo crear menús del tipo ContextMenu de WPF "al vuelo", ya que he tenido que usar los de Windows.Forms, que en VB no sé cómo asignar los eventos de los submenús (pero todo por código) o en modo de diseño... ya que tampoco he visto cómo hacerlo
  • Bueno... y más cosas, pero por ahora está bien... de todas formas, dudo que me lo aclare alguien, je, je.
    (las cosas tachadas es porque ya sé cómo hacerlas... por desgracia nadie me ha contado cómo hacerlas y he tenido que "comerme el coco" para averiguarlo, con el problema de casi no saber por dónde buscar), un día de estos te contaré cómo)

 

Si te animas a "ayudarme" a solucionar mis dudas o simplemente quieres hacer un comentario sobre este artículo, puedes usar este hilo de mis foros (recuerda que para postear debes estar registrado):
http://foros.elguille.info/Mensajes.aspx?ID=32500 

 

Un vistazo rápido a las equivalencias mostradas en este artículo

Estos son los links a las distintas equivalencias que te he mostrado aquí.
En otros artículos posteriores te mostraré más... conforme los vaya descubriendo...

  1. No existe la propiedad Text de los controles
  2. La ventana principal tampoco tiene Text
  3. No existe la propiedad Checked (ahora es un evento)
  4. No existe el evento CheckedChanged
  5. Muchas de los tipos habituales de Windows.Forms están definidos en Windows
  6. La clase MessageBox y las enumeraciones de esa clase
  7. Cambios en Drag & Drop
  8. Cambios en las teclas pulsadas en los eventos KeyDown y KeyUp
  9. La colección SelectedItems de un ListView es de tipo Object (IList)
  10. El elemento seleccionado de un ListView es de tipo Object
  11. El evento SelectedIndexChanged ahora es SelectionChanged
  12. Los eventos de inicio y cierre de la ventana principal

 

A ver si le pregunto a la gente del "TEAM de VB" para ver si existe algún tipo de documento "oficial" sobre estas equivalencias entre los programas hechos con .NET 2.0 (Windows.Forms) y los creados con .NET 3.0 (WinFX).
Si me responden afirmativamente, te lo diré. 

 

Espero que todo esto te sea de utilidad como lo ha sido (y será) para mí, je, je.

Nos vemos.
Guillermo

Nota del 21/Ago/07:
He publicado la segunda parte de las equivalencias entre WPF y .NET normal.

 


 

Código de ejemplo (comprimido):

No hay ningún código que mostrar... todo es texto...



La fecha/hora en el servidor es: 24/11/2024 1:13:37

La fecha actual GMT (UTC) es: 

©Guillermo 'guille' Som, 1996-2024