domingo, 10 de febrero de 2013

[WinForms] Verificar si el form esta abierto (instancia única)

 

Introducción


Determinar si un form ha sido abierto, evitando que una nueva instancia sea creada.

 

Validar instancia entorno SDI


Para validar la existencia de la instancia de un formulario se hará uso del Application.OpenForms, con este y la ayuda de linq podremos buscar la existencia del form abierto.

image

La selección de botón abrirá una instancia, pero las siguientes pulsaciones solo harán que el mismo form pase al frente.

 

private void btnAbrirFormclientes_Click(object sender, EventArgs e)
{
    //se localiza el formulario buscandolo entre los forms abiertos 
    Form frm = Application.OpenForms.Cast<Form>().FirstOrDefault(x => x is frmEdicionCliente);

    if (frm != null)
    {
        //si la instancia existe la pongo en primer plano
        frm.BringToFront();
        return;
    }
    
    //sino existe la instancia se crea una nueva
    frm = new frmEdicionCliente();
    frm.Show();
}

El truco esta en poder determinar la lista de forma abiertos

image

 

Validar instancia entorno MDI


En un entorno MDI la validación es similar solo cambia la propiedad usada para determinar la lista de forms abiertos

image

 

private void abrirFormClienteToolStripMenuItem_Click(object sender, EventArgs e)
{
    //se localiza el formulario buscandolo entre los forms abiertos 
    Form frm = this.MdiChildren.FirstOrDefault(x => x is frmEdicionCliente);

    if (frm != null)
    {
        //si la instancia existe la pongo en primer plano
        frm.BringToFront();
        return;
    }
    
    //se abre el form de clientes
    frm = new frmEdicionCliente();
    frm.MdiParent = this;
    frm.Show();

}

 

Código


20 comentarios:

  1. Hola Leandro,gracias por tu tiempo, me sirvió mucho el método para abrir UN formulario MDI, private void abrirFormClienteToolStripMenuItem_Click(object sender, EventArgs e), como puedo hacerlo público? , y no tener que escribir el mismo código para todos los formularios a abrir, probé algo así como el siguiente código y no logro hacerlo funcionar :
    Llamo al método abrirForm (“NombreFormulario”)

    Public void abrirForm(Form nombreFormulario)
    {
    //se localiza el formulario buscandolo entre los forms abiertos
    Form frm = this.MdiChildren.FirstOrDefault(x => x is nombreFormulario);

    if (frm != null)
    {
    //si la instancia existe la pongo en primer plano
    frm.BringToFront();
    return;
    }

    //se abre el form
    frm = new NombreFormulario();
    frm.MdiParent = this;
    frm.Show();

    }

    Una ayudita si es posible, Gracias.

    ResponderEliminar
  2. hola CESAR

    no evaluaste crear una clase form base para que hereden de ella los forms?

    en la clase base podrias poner esta funcionalidad para usarla de todos los forms que hereden de este FormBase

    saludos

    ResponderEliminar
  3. Hola Leandro, no considere esa opción pero leyendo tus trabajos (muy buenos e ilustrativos, gracias) hice una mezcla de tus métodos y logre que funcione, te aclaro soy nuevo en esto y me gustaría saber tu opinión si mi solución está en el rango de "buenas prácticas", gracias.

    public void AbrirForm(string nombreForm)
    {
    Form frm = this.MdiChildren.FirstOrDefault(x => x.Name == nombreForm);
    if (frm != null)
    {
    frm.BringToFront();
    return;
    }
    // Assembly asm = Assembly.GetEntryAssembly();
    Assembly asm = Assembly.GetExecutingAssembly();
    Type formtype = asm.GetType("Sistemas." + nombreForm);
    Form f = (Form)Activator.CreateInstance(formtype);
    f.MdiParent = this;
    f.WindowState = FormWindowState.Maximized;
    f.BringToFront();
    f.Show();
    }

    ResponderEliminar
    Respuestas
    1. Muy bien Cesar me ha servido tu Código solo hice unas Modificaciones, Solo que no entendí un poco sobre la instancia que creaste de Assembly me imagino para ejecutar código ensamblador

      Eliminar
    2. Muy bien Cesar me ha servido tu Código solo hice unas Modificaciones, Solo que no entendí un poco sobre la instancia que creaste de Assembly me imagino para ejecutar código ensamblador

      Eliminar
  4. hola CESAR

    Si parece estar correcto
    Cuando no encuentra el form dentro de la coleccion instancia por el nombre y lo asigna como parte del mdi child, eso esta bien

    saludos

    ResponderEliminar
  5. Hola Leandro, muchas gracias por el código. Muchas veces leo cosas tuyas y nunca tuve la amabilidad de agradecerte.
    Te hago una consulta, yo tengo un switch con todos los id de menú que traigo de la base, cómo debería hacer, le paso el formulario que corresponda en cada case como parámetro?

    ResponderEliminar
  6. Ya lo pude resolver, muchas gracias!

    ResponderEliminar
  7. Hola, te agradezco mucho por la lógica y el código, que te vaya bien !

    ResponderEliminar
  8. hoal muey bueno tu aporte me funciono bien hasta un punto donde quiero enviar al formulario que estoy validando el valor de una variable que el tiene declarada pero al inentar enviarsela usando tu codigo nunca me envia la infor hacia el formulario como podria hacer esto y siempre validar si el formulario esta o no abierto yo lo hago de esta forma y me funciona

    frm_agregar_abonados ventana = new frm_agregar_abonados();
    ventana.accion = true;
    ventana.MdiParent = this.MdiParent;
    ventana.Show();

    pero ahi no valido si el formuario esta abierto siempre me lo abre infinitamente quisiera poder hacer esto mismo pero usar tu codigo para validar que no me vuelva a abrir otra ventana si esta ya esta abierta

    ResponderEliminar
    Respuestas
    1. hola
      validar que el form este abierto lo realizas con la tecnica que explico en este mismo articulo, o sea validas las instancia de los forms abiertos antes de hacer el show() de una nueva instancia

      ahora me llama la atencion lo que comentas de enviar informacion, pareciera que la propiedad deberia asignarse correctamente, sino podrias pasar el dato por medio del constructor usando la tecnica de este otro articulo
      Comunicar Formularios

      saludos

      Eliminar
  9. Hola Leandro... muy buena explicacion... como siempre sacando de un apuro...

    ResponderEliminar
  10. Me funciono perfecto muchisimas gracias,ahora lo que yo quiero hacer es:

    Una vez comprobada la instancia, actualizar algunos variables publicos que tengo en ese formulario pero al declararlo como tipo "Form" no me saldrian mis variables, tendria que declararlo como frmPrincipal frm = Application.OpenForms.Cast().FirstOrDefault(x => x is frmPrincipal); mi problema es como podria adecuarlo para cualquier formulario pues tendria que ingresar un parametro de tipo "frmPrincipal" en mi metodo... Espero me deje entender , llevo poco tiempo en c# :(

    ResponderEliminar
    Respuestas
    1. hola
      Pero en ese linq solo estas devolviendo como respuesta si es form es del tipo frmPrincipal por eso usas
      FirstOrDefault(x => x is frmPrincipal)
      que sentido tiene definirlo como form simple si el filtro devuelve un form concreto

      Quizas para hacerlo generico podrias usar una interfaz, pero debes exponer propiedades no variables
      podria ser

      public interface IForm{
      string Prop1 {get;set;}
      }

      y la usarias

      IForm frm = Application.OpenForms.Cast<IForm>().FirstOrDefault(x => x is IForm);

      if(frm!=null){
      string valor = frm.Prop1;
      }


      de esta forma si el form implementa la interfaz podras obtenerlo y esto sera generico
      saludos

      Eliminar
  11. Muchas gracias Leandro, me sirvio de mucho

    ResponderEliminar
  12. Muchas gracias por su ayuda me sirvió de maravilla al igual que las de msdn.

    ResponderEliminar
  13. Hola Leandro,

    Muy buen artículo, la solución para los que quieren abrir una sola instancia de un formulario.

    Yo uso un formulario, pero abro varias instancias según el valor de un parámetro, o sea, quiere decir que ese formulario lo uso para dar mantenimiento a varias tablas y según el parámetro se habilitan algunos controles.

    En este caso, yo abro la instancia para dar mantenimiento a la tabla alumnos, luego abro esa misma instancia para dar mantenimiento a la tabla profesores, así mismo para la tabla directivos. O sea tengo 3 botones y cada uno me abre la misma instancia, pero con características distintas, el problema es si presiono dos veces un botón, me abre dos veces la instancia pero con una sola característica, ¿como hago para evitar esa carga?

    el código es esto:


    En el botón Alumno:

    private void btnAlumno_Click(object sender, EventArgs e)
    {
    clsModulo.nRolSujeto = 1;
    clsModulo.sRolSujeto = "ALUMNO";
    frmBusinessSubject frmAlumno = new frmBusinessSubject();
    frmAlumno.Text = ".: Mantenimiento Alumnos :.";
    frmAlumno.lblTitulo.Text = frmAlumno.Text;
    frmAlumno.MdiParent = this;
    frmAlumno.Show();
    }

    En el botón Docente:

    private void btnDocente_Click(object sender, EventArgs e)
    {
    clsModulo.nRolSujeto = 2;
    clsModulo.sRolSujeto = "DOCENTE";
    frmBusinessSubject frmDocente = new frmBusinessSubject();
    frmDocente.Text = ".: Mantenimiento Docentes :.";
    frmDocente.lblTitulo.Text = frmDocente.Text;
    frmDocente.MdiParent = this;
    frmDocente.Show();
    }

    En el botón Directivo:

    private void btnDirectivo_Click(object sender, EventArgs e)
    {
    clsModulo.nRolSujeto = 3;
    clsModulo.sRolSujeto = "DIRECTIVO";
    frmBusinessSubject frmDirectivo = new frmBusinessSubject();
    frmDirectivo.Text = ".: Mantenimiento Directivo :.";
    frmDirectivo.lblTitulo.Text = frmDirectivo.Text;
    frmDirectivo.MdiParent = this;
    frmDirectivo.Show();
    }

    es la misma instancia.

    ResponderEliminar
  14. Este comentario ha sido eliminado por el autor.

    ResponderEliminar