Asegurando una sola instancia de una forma hija en un MDI
Típico te piden que desean que, en tu aplicación MDI, cada vez que escogen abrir un formulario hijo este se abra en la misma ventana, mas no en ventanas separadas. Es decir, si la forma hija ya está abierta, elegir esa, sino ahí sí abrirla. Bueno, es un requerimiento válido, aunque no necesariamente siempre deba ser así. Dado que me gusta hacer cosas un poco fuera de lo común, en mi aplicación MDI yo no tengo una barra de menús, sino más bien un TreeView desde el cual los usuarios eligen el formulario a abrir. Entonces, lo que yo quisiera poder hacer es esto:
Private Sub RecursosHumanosTreeView_NodeMouseClick(...) Handles ...
Select Case e.Node.Name
Case "Forma1Node"
MostrarForma("Mi forma 1")
Case "Forma2Node"
MostrarForma("Mi forma 2")
Case "Forma3Node"
MostrarForma("Mi forma 3")
End Select
End Sub
¿Cuál sería la forma ideal de lograr un código así de sencillo? Recuerden que la función MostrarForma() debe instanciar la forma correcta según el parámetro enviado. Eso si es que no está ya abierta, porque en ese caso debe más bien activarla. Dado que estoy usando VB 2005, se me ocurrió sacarle provecho a los nuevos Generics. Así, logré hacer a una pequeña función que me redujo el código de una manera increíble y elegante. Esta es mi pequeña función:
Private Sub MostrarForma(Of T As {Form, New})()
For Each formaAbierta As Form In My.Application.OpenForms
If TypeOf (formaAbierta) Is T Then
formaAbierta.Activate()
Exit Sub
End If
Next
Dim forma As New T
forma.MdiParent = Me
forma.Show()
End Sub
Lo interesante aquí, como podrán notar, está en la declaración de la función. Básicamente al decir MostrarForma(Of T...) estoy indicando que el método recibe un parámetro de un tipo T, es decir, un tipo que solo será conocido en tiempo de ejecución. Pero, dado que yo sé de antemano que lo que recibiré será siemrpe un formulario, pues apliqué un constraint que solo permita formas: MostrarForma(Of T As {Form...}). Así mismo, dado que yo sé que, si la forma hija aún no está abierta deberé instanciarla, agrego otro constraint de tipo New. Así, mi declaración quedó: MostrarForma(Of T As {Form, New})(). De allí creo que lo demás es obvio. Simplemente busco de entre todas las formas abiertas si alguna de ellas es del tipo T (el tipo de la forma solicitada). Si es así, pues simplemente la activo. Caso contrario creo una nueva instancia de ella y la muestro. Con esta función lista, la llamada queda entonces así:
Private Sub RecursosHumanosTreeView_NodeMouseClick(...) Handles ...
Select Case e.Node.Name
Case "Forma1Node"
MostrarForma(Of MiForma1)
Case "Forma2Node"
MostrarForma(Of MiForma2)
Case "Forma3Node"
MostrarForma(Of MiForma3)
End Select
End Sub
¿Simple no? Por supuesto en estas cosas siempre hay una forma más simple de hacerlo. ¿Saben de una mejor forma? Please, ¡háganmela conocer!