APLICACIONES MDI

[APLICACIONES DE INTERFAZ CON MÚLTIPLES DOCUMENTOS ]

 
Fecha: 03/Sep/2005 (24/08/2005)
Autor: Arbis Percy Reyes Paredes 
Email: bigpercynet@hotmail.com
http://percyreyes.blogspot.com 

Joven desarrollador de aplicaciones Web y Servicios Web XML en Visual Basic .NET y Visual C# .NET con bases de datos SQL Server 2000 - 2005.
¡ Los peruanos Sí podemos !

Hola amigos visitantes de este sitio del Guille, nuevamente me hago presente con esta colaboración con la finalidad de compartir con ustedes cosillas interesantes. Para esta vez he preparado una sencillo ejemplo de aplicación para comprender con mayor facilidad cómo es la funcionalidad de una aplicación MDI, además conoceremos conceptos relacionados a este tema. Empezaré detallando los conceptos respectivos al tema y finalmente usted podrá revisar el código del ejemplo, como también podrá descargarlo.

Debo recordarle que previamente debe conocer muchos conceptos sumamente importantes en la construcción de aplicaciones MDI y SDI. Bueno, si no entiende esto, no corra!...porque a continuación le explicaré estas cosillas. También debe conocer todo respecto a cuadros de diálogos comunes, todo esto le explico a continuación.

Empezemos primero por entender que es una aplicación MDI y una aplicación SDI.

PATRÓN SINGLETÓN CON FORMULARIOS MDI

En dias anteriores estaba creando una aplicación en la cual requería crear una sola instancia de los formularios hijos de una MDI padre. Buscando en la documentación de Microsoft encontré este Pattern llamado Singleton. Acá les muestro una sencilla implementación en formularios MDI para tener solamente una instancia abierta por formulario. Lo primero que hay que hacer es modificar el constructor del formulario hijo, o sea, el formulario que queremos que solo tenga una instancia en la aplicación. En este constructor inicialmente aparece como publico (public).

 Código 

#Region " Windows Form Designer generated code "
    Public Sub New()
        MyBase.New()
        'This call is required by the Windows Form Designer.
        InitializeComponent()
        'Add any initialization after the InitializeComponent() call
    End Sub
    .....
Lo modificaremos y lo haremos privado (private) para evitar llamados a este formulario.
#Region " Windows Form Designer generated code "
    Private Sub New()
        MyBase.New()
        'This call is required by the Windows Form Designer.
        InitializeComponent()
        'Add any initialization after the InitializeComponent() call
    End Sub
    .....   

 Luego lo que haremos será crear una variable global en este mismo formulario hijo para que controle
 la instancia y la haremos shared para que pueda ser accesada desde el formulario padre.


'Variable global
Private Shared frmInstance As frmReport = Nothing

Finalmente crearemos una función shared que será la que finalmente controlara la creación o manejo
de la instancia del formulario, logrando obtener solo un formulario abierto para este form1.
   
   
   
   
   
    
'Funcion que crea una sola instancia del form1
Public Shared Function Instance() As form1
   
   
   
   
   
    If frmInstance Is Nothing OrElse frmInstance.IsDisposed = True Then
        frmInstance = New form1
   
   
   
   
   
    End If
   
    frmInstance.BringToFront()
   
   
   
   
   
    Return frmInstance
End Function

Ahora en el padre escribiremos el llamado al hijo para crear la instancia, o si ya esta creada, entonces pondrá el formulario hijo en
frente para que lo podamos ver.
Dim frmHijo as form1 = form1.Instance
frmHijo.MdiParent = Me
frmHijo.Show

APLICACIONES DE INTERFAZ CON MÚLTIPLES DOCUMENTOS (MDI)

Estas aplicaciones le permiten mostrar múltiples documentos al mismo tiempo con cada documento que aparece en su ventana propia. Las aplicaciones MDI con frecuencia tienen un elemento del menú de Windows con submenús para cambiar entre ventanas y documentos. El fundamento de una Aplicación con interfaz de múltiples documentos (MDI) es el formulario primario MDI. Este es el formulario que contiene las ventanas secundarias MDI, que son las "sub-ventanas" en donde el usuario interactúa con la aplicación MDI.

Para establecer un formulario como primario MDI, configure la propiedad IsMdiContainer = True.
Para crear un formulario secundario MDI nuevo tiene que configurar la propiedad a MdiParent.

Las aplicaciones con interfaz de un único documento (SDI) están formadas por una o más ventanas independientes, cada una de las cuales aparece como ventana independiente en el escritorio de Windows. Microsoft Exchange es un ejemplo de una aplicación SDI, en la que cada mensaje que abra aparece en su propia ventana independiente.

VB .NET

Protected Sub   MDIChildNew_Click(
   
   
   
   
   
    
    ByVal sender As   System.Object,
   
   
   
   
   
    
    ByVal e As System.EventArgs) Handles _
    MenuItem2.Click
   
   
   
   
    
    Dim NewMDIChild As New Form2
   
   
   
   
   
    
    'Configurar el Formulario primario de la Ventana secundaria. 
    




   



   


   

   
    NewMDIChild.MdiParent = Me 
    




   



   


   

   
   
   
   
   
    
    'Mostrar el nuevo formulario. 
    




   



   


   

   
   
    NewMDIChild.Show()

End Sub 































...

C# .NET

... 































protected void MDIChildNew_Click(object sender, System.EventArgs e)
{
    Form2 newMDIChild = new Form2();
   
   
   
   
   
    
    // Set the Parent Form of the Child window. 
    




   



   


   

   
    newMDIChild.MdiParent = this;
   
   
   
   
   
    
    // Display the new form. 
    




   



   


   

   
    newMDIChild.Show();
}
...
 

EXPLICANDO EL EJEMPLO

Recuerde que el objetivo de esta aplicación no es la complejidad de esta, sino mas bien, la dinámica de funcionamiento de una aplicación MDI, con un sólo formulario padre y dos formularios hijos. Quedando claro esto, ahora explicaré la funcionalidad del ejemplo: Trabajaremos prácticamente con tres formularios hijos (y un formulario de presentación, que estará activa 5 segundos y luego no lo usaremos más) que simulará una aplicación que nos ayudará en la conversión de divisas y finalmente reportar todas las conversiones realizadas. Además haremos uso de una dataset, la cual será nuestro almacén de registros de las operaciones de conversión. La aplicación expone opciones mediante la cual usted podrá guardar y abrir los registros(en formato XML) con las conversiones realizadas, como también, visualizar el reporte de operaciones. La opciones de menú son las que se aprecian en la siguientes capturas de imágenes.

                     

 

Cuando corra la aplicación, esta empezará dándole la bienvenida con un formulario denominado Splash, que por cierto es la siguiente:                 

 

Formulario Splash

Creo que detallar más sobre el ejemplo sería redundar (la aplicación es sencilla), que le parece si revisa el código y así comprenderá mejor acerca de una aplicación MDI. Cualquier duda, puede hacérmelo llegar al correo.

Este es el código para nuestro formulario padre, desde aquí administraremos los demás formularios hijos.

Imports System.Drawing.Drawing2D

Public Class Form1
   
   
   
   
   
    Inherits System.Windows.Forms.Form

   

   

   

   

   

    Public Shared Sub main()
       
       
       
       
       
        'cargamos el formulario de presentación.
       
       
       
       
       
        Dim oSplash As New Splash
        oSplash.ShowDialog()
       
       
       
       
       
        'luego cargamos el formulario principal
       
       
       
       
       
        Dim oPrincipal As New Form1
        oPrincipal.ShowDialog()
   
   
   
   
   
    End Sub

   

   

   

   

   

    Private Sub MenuClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
       
       
       
       
       
        Handles MenuClose.Click
       
       
       
       
       
        End
   
   
   
   
   
    End Sub

   

   

   

   

   

    Public NroArchivo As Integer = 1

   

   

   

   

   

    Private Sub MenuAbierto_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
       
       
       
       
       
        Handles MenuAbierto.Click

       

       

       

       

       

        Try
           
           
           
           
           
            With Me.OpenFileDialog1
                .Filter = "Archivo XML|*.xml"
                .Title = "Abrir archivo XML"
                .RestoreDirectory = True
               
               
               
               
               
                If (.ShowDialog() = DialogResult.OK) Then
                    MisDatos.Clear()
                    MisDatos.ReadXml(.FileName)
                    MenuReportar_Click(sender, e)
               
               
               
               
               
                End If
           
           
           
           
           
            End With
       
       
       
       
       
        Catch ex As Exception
            MessageBox.Show(ex.Message)
       
       
       
       
       
        End Try

   

   

   

   

   

    End Sub

   

   

   

   

   

    Private Sub MenuHacerCambio_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
       
       
       
       
       
        Handles MenuHacerCambio.Click

       

       

       

       

       

        Dim frmHijoConvertir As Convertir = Convertir.Instance
        frmHijoConvertir.MdiParent = Me
        frmHijoConvertir.Show()
        frmHijoConvertir.BringToFront()
       
       
       
       
       
        Me.LayoutMdi(MdiLayout.TileHorizontal)

       

       

       

       

       

        'If nc = 0 Then
       
       
       
       
       
        '    Dim oFrmConversion As New Convertir
       
       
       
       
       
        '    oFrmConversion.MdiParent = Me
       
       
       
       
       
        '    oFrmConversion.Show()
       
       
       
       
       
        '    oFrmConversion.BringToFront()
       
       
       
       
       
        '    nc = 1
       
       
       
       
       
        'End If

   

   

   

   

   

    End Sub

   

   

   

   

   

    Private Sub MenuReportar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
       
       
       
       
       
        Handles MenuReportar.Click
       
       
       
       
       
        Dim frmHijoReporte As Reporte = Reporte.Instance
        frmHijoReporte.MdiParent = Me
        frmHijoReporte.Show()
        frmHijoReporte.BringToFront()
       
       
       
       
       
        Me.LayoutMdi(MdiLayout.TileHorizontal)

       

       

       

       

       

        'If nr = 0 Then

       

       

       

       

       

        '    Dim oFrmreporte As New Reporte
       
       
       
       
       
        '    oFrmreporte.MdiParent = Me
       
       
       
       
       
        '    oFrmreporte.Show()
       
       
       
       
       
        '    oFrmreporte.BringToFront()
       
       
       
       
       
        '    nr = 1
       
       
       
       
       
        'End If
       
       
       
       
       
        'Me.LayoutMdi(MdiLayout.TileHorizontal)


   


   


   


   


   


    End Sub

   

   

   

   

   

    Private Sub MenuItem2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
       
       
       
       
       
        Handles MenuItem2.Click

       

       

       

       

       

        Dim f As Form
       
       
       
       
       
        For Each f In Me.MdiChildren
            f.Close()
       
       
       
       
       
        Next

   

   

   

   

   

    End Sub

   

   

   

   

   

    Private Sub MenuGuardarComo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
       
       
       
       
       
        Handles MenuGuardarComo.Click
       
       
       
       
       
        Try
           
           
           
           
           
            With Me.SaveFileDialog1
                .Filter = "Archivo XML|*.xml"
                .Title = "Guardar archivo XML"
                .FileName = "Archivo XML" & NroArchivo
                NroArchivo += 1
                .RestoreDirectory = True

               

               

               

               

               

                If (.ShowDialog() = DialogResult.OK) Then
                   
                   
                   
                   
                   
                    If .FileName.Length <> Nothing Then
                        MisDatos.WriteXml(.FileName)
                        MsgBox("Datos guardados")
                   
                   
                   
                   
                   
                    Else
                       
                        MsgBox("error, no se guardaron los datos !!!")
                   
                   
                   
                   
                   
                    End If
               
               
               
               
               
                End If
           
           
           
           
           
            End With
       
       
       
       
       
        Catch ex As Exception
            MessageBox.Show(ex.Message)
       
       
       
       
       
        End Try
   
   
   
   
   
    End Sub
End Class

y este es el código para nuestro formulario  de presentación del proyecto. Este formulario nos dará la bienvenida por 5 segundos y desaparecerá fugazmente.


Imports System.Drawing.Drawing2D

Public Class Splash
   
   
   
   
   
    Inherits System.Windows.Forms.Form

   

   

   

   

   

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) _
       Handles Timer1.Tick

       

       

       

       

       

        Me.Timer1.Enabled = False
       
       
       
       
       
        Me.Close()
   
   
   
   
   
    End Sub

   

   

   

   

   

    Private Sub Splash_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
                   Handles MyBase.Load

       

       

       

       

       

        ' Me.BackColor = Color.Silver
       
       
       
       
       
        Dim gp As New GraphicsPath

        gp.AddEllipse(New Rectangle(New Point(50, 50), New Size(500, 300)))
       
       
       
       
       
        'gp.AddCurve(New Point() {New Point(50, 199), New Point(299, 50), _
        ' New Point(298, 548), New Point(299, 348)})

       

       

       

       

       

        Dim reg As New Region(gp)
       
       
       
       
       
        Me.Region = reg
       
       
       
       
       
        Me.Timer1.Enabled = True
       
       
       
       
       
        Me.Timer1.Interval = 4000
       
       
       
       
       
        Me.Timer1.Start()

  

  

  

  

  

   End Sub

   

   

   

   

   

    Private Sub Splash_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) _
       
       
       
       
       
        Handles MyBase.Paint

       

       

       

       

       

        Dim GradientePanel As New LinearGradientBrush(New RectangleF(0, 0, Me.Width, Me.Height), _
        Color.Orange, Color.Olive, LinearGradientMode.ForwardDiagonal)
       
       
       
       
       
        Me.CreateGraphics.DrawEllipse(New Pen(Color.Blue, 4), New Rectangle(New Point(50, 50), _
                                           New Size(498, 298)))
       
       
       
       
       
        Me.CreateGraphics.FillRectangle(GradientePanel, New RectangleF(0, 0, Me.Width, Me.Height))

   

   

   

   

   

    End Sub

End Class

El objeto denominado MisDatos es una dataset la cual será el refugio para registrar las operaciones efectuadas. 

Module VariablesGlobales
   
   
   
   
   
    Public MisDatos As New Dataset1
End Module

La construcción de nuestra dataset será agregando un nuevo elemento al proyecto denominado esquema XML. Vaya al Menú proyecto, luego Agregar nuevo elemento.... nuestra esquema debe tener el siguiente formato:

 

También podra hacerlo mediante código con la ayuda de un bloc de notas. El código en XML sería:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="Dataset1" targetNamespace="http://tempuri.org/Dataset1.xsd" elementFormDefault="qualified"
   attributeFormDefault="qualified" xmlns="http:"//tempuri.org/Dataset1.xsd" xmlns:mstns="http://tempuri.org/Dataset1.xsd"
   xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" >
  <xs:element name="Dataset1" msdata:IsDataSet="true" >
     <xs:complexType>
        <xs:choice maxOccurs="unbounded" >
           <xs:element name="MiTabla" >
              <xs:complexType>
                  <xs:sequence>
                        <xs:element name="CurrencyFrom" type="xs:string" minOccurs="0" />
                        <xs:element name="CurrencyTo" type="xs:string" minOccurs="0" />
                        <xs:element name="AmountFrom" type="xs:double" minOccurs="0" />
                        <xs:element name="AmountTo" type="xs:double" minOccurs="0" />
                        <xs:element name="Fecha" type="xs:dateTime" minOccurs="0" />
                  </xs:sequence>
             </xs:complexType>
          </xs:element>
        </xs:choice>
     </xs:complexType>
  </xs:element>
</xs:schema>

Finalmente este es el código de lógica para realizar las conversiones:
NOTA: Sea cuidadoso al revisar el código.
         

Public Class Convertir
   
   
   
   
   
    Inherits System.Windows.Forms.Form

   

   

   

   

   

    'CÓDIGO GENERADOR POR EL DISEÑADOR DE WINDOWS
   
   
   
   
   
    Private Shared frmInstanceConvertir As Convertir = Nothing

   

   

   

   

   

    'Funcion que crea una sola instancia del Form Convertir
   
   
   
   
   
    Public Shared Function Instance() As Convertir
       
       
       
       
       
        If frmInstanceConvertir Is Nothing OrElse frmInstanceConvertir.IsDisposed = True Then
            frmInstanceConvertir = New Convertir
       
       
       
       
       
        End If
       
        frmInstanceConvertir.BringToFront()
       
       
       
       
       
        Return frmInstanceConvertir
   
   
   
   
   
    End Function

   

   

   

   

   

    Private Sub BtnConvertir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
       
       
       
       
       
        Handles BtnConvertir.Click
       
       
       
       
       
        Try
           
           
           
           
           
            If Not IsNumeric(Me.TxtCantidad.Text) Then
               
               
               
               
               
                Throw New Exception
           
           
           
           
           
            Else
               
               
               
               
               
                Try
                   
                   
                   
                   
                   
                    Dim resul As Double

                       

                       

                       

                       

                       

                        Select Case LstFrom.SelectedIndex
                           
                           
                           
                           
                           
                            Case 0 : resul = CType(Me.TxtCantidad.Text, Double) / Peso
                           
                           
                           
                           
                           
                            Case 1 : resul = CType(Me.TxtCantidad.Text, Double) / Dolar
                           
                           
                           
                           
                           
                            Case 2 : resul = CType(Me.TxtCantidad.Text, Double) / Euro
                       
                       
                       
                       
                       
                        End Select

                       

                       

                       

                       

                       

                        Select Case LstTo.SelectedIndex
                           
                           
                           
                           
                           
                            Case 0 : resul *= Peso
                                resul *= Dolar
                               
                               
                               
                               
                               
                                Case 2 : resul *= Euro
                           
                           
                           
                           
                           
                            End Select

                           

                           

                           

                           

                           

                            'creamos una fila 
                            




                           



                           


                           

                           
                           
                           
                           
                           
                           
                            Dim oRow As DataRow
                            oRow = MisDatos.Tables(0).NewRow
                            oRow(0) = LstFrom.SelectedItem
                            oRow(1) = LstTo.SelectedItem
                            oRow(2) = CType(Me.TxtCantidad.Text, Double)
                               
                               
                               
                               
                               
                                Me.LblResultado.Text = Format(resul, "#,#.00")
                                oRow(3) = CType(Me.LblResultado.Text, Double)
                                    oRow(4) = Now.ToShortDateString
                                   
                                   
                                   
                                   
                                   
                                    'agregamos la fila llenada 
                                    




                                   



                                   


                                   

                                   
                                   
                                    MisDatos.Tables(0).Rows.Add(oRow)
                                   
                                   
                                   
                                   
                                   
                                    Me.ErrorProvider1.SetError(TxtCantidad, "")

                               

                               

                               

                               

                               

                                Catch ex As Exception
                                    MsgBox(ex.Message)
                               
                               
                               
                               
                               
                                End Try
                           
                           
                           
                           
                           
                            End If

                       

                       

                       

                       

                       

                        Catch ex As Exception
                           
                           
                           
                           
                           
                            Me.ErrorProvider1.SetError(TxtCantidad, "Debe ingresar un valor númerico")
                       
                       
                       
                       
                       
                        End Try

                   

                   

                   

                   

                   

                    End Sub

                   

                   

                   

                   

                   

                    Private Sub Convertir_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
                       
                       
                       
                       
                       
                        Handles MyBase.Load
                       
                       
                       
                       
                       
                        Me.LstTo.SelectedIndex = 1
                       
                       
                       
                       
                       
                        Me.LstFrom.SelectedIndex = 2
                   
                   
                   
                   
                   
                    End Sub
               
               
               
               
               
                End Class
Algo adicional a este artículo, pero muy importante, es el siguiente código para pintar nuestro formulario MDI distinto al color por defecto, ya que por defecto un formulario MDI tiene un color algo oscuro.  
#Region "PintarMDI"

   

   

   

   

   

    Dim oControlMDI As MdiClient

   

   

   

   

   

    Public Sub FrmLoad(ByVal sender As Object, ByVal e As System.EventArgs)

       

       

       

       

       

        Dim oControl As Control
       
       
       
       
       
        For Each oControl In Me.Controls
           
           
           
           
           
            Try
                oControlMDI = CType(oControl, MdiClient)
                oControlMDI.BackColor = Color.AntiqueWhite
               
               
               
               
               
                AddHandler oControlMDI.Paint, AddressOf PintarFondoAreaCliente
           
           
           
           
           
            Catch ex As InvalidCastException
           
           
           
           
           
            End Try
       
       
       
       
       
        Next

   

   

   

   

   

    End Sub

   

   

   

   

   

    Private Sub PintarFondoAreaCliente(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)

       

       

       

       

       

        Dim GradientePanel As New LinearGradientBrush(New RectangleF(0, 0, oControlMDI.Width, _
        oControlMDI.Height), Color.Blue, Color.White, LinearGradientMode.ForwardDiagonal)
        e.Graphics.FillRectangle(GradientePanel, New RectangleF(0, 0, oControlMDI.Width, oControlMDI.Height))

   

   

   

   

   

    End Sub

   

   

   

   

   

    Private Sub TfrmMain_Resize(ByVal sender As Object, ByVal e As System.EventArgs) _
       
       
       
       
       
        Handles MyBase.Resize

       

       

       

       

       

        If Not (Me.oControlMDI Is Nothing) Then
           
           
           
           
           
            Me.PintarFondoAreaCliente(Me.oControlMDI, New PaintEventArgs(Me.oControlMDI.CreateGraphics, _
           
           
           
           
           
            New Rectangle(Me.oControlMDI.Location, Me.oControlMDI.Size)))
       
       
       
       
       
        End If

   

   

   

   

   

    End Sub

   

   

   

   

   

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
       
       
       
       
       
        Handles MyBase.Load

        FrmLoad(sender, e)
       
       
       
       
       
        Me.Size = New Size(800, 600)

   

   

   

   

   

    End Sub

#End Region

Sinceramente deseo que os haya sido de ayuda para usted. No olvide de darme su voto en PanoramaBox.

Web Developer PERCY REYES PAREDES

Saludos desde Trujillo  - Perú


Espacios de nombres usados en el código de este artículo:

System.Drawing.Drawing2D


Fichero con el código de ejemplo: Percynet_ConvertirDivisas - Tamaño 24.6 KB


ir al índice