Introducción
Este post representa la continuación de Comunicar formularios de forma desacoplada
Pero a diferencia del anterior en esta oportunidad se trabajara con formularios MDI
Diferencias
Al hacerse uso de formularios MDI, algo que ya no podrá ser utilizado es el parámetro Owner en el método Show(), al realizar la apertura del formulario hijo.
Es por ello que será necesito hacer uso de una propiedad en el formulario hijo para salvar este inconveniente, y poder así determinar que formulario esta ejecutando la acción de apertura.
Primer Paso – Definición de la interfaz
A diferencia el ejemplo anterior en este oportunidad se hará uso de un valor algo mas complejo que simple texto en la comunicación, es por ello el uso de un objeto DataTable como medio de transporte de datos entre los formularios.
Aunque nada impediría que se utilices clases custom creadas por uno.
public interface IForm { bool LoadDataGridView(DataTable dataTableParam); }
En este caso la interfaz también retorna un valor que indicará si el procesamiento se realizó correctamente.
Segundo Paso – Definición del formulario padre
Al igual que en el anterior post el formulario padre deberá implementar una interfaz, la cual permitirá desacoplar la dependencia durante la comunicación.
public partial class Form1 : Form, IForm { public Form1() { InitializeComponent(); } #region IForm Members public bool LoadDataGridView(DataTable dataTableParam) { DataGridView1.DataSource = dataTableParam; return true; } #endregion private void Button1_Click(object sender, EventArgs e) { Form2 form2 = new Form2(); form2.MdiParent = this.MdiParent; form2.Opener = this; form2.Show(); } }
Debe remarcarse algunos detalles, como es en este caso el uso de la propiedad MdiParent, que por supuesto siempre hará referencia al FormPrincipal, y que este ha sido declarado como MdiContainer
También hay que marcar la línea 26, en donde se visualiza el uso de la propiedad adicional, en este caso llamada Opener, es en esta donde se asignara el form que realizo la apertura del formulario.
Tercer paso – Definición del formulario hijo
Este formulario simplemente tendrá un botón que realizará el cierre de si mismo, pero durante esta operación se generarán los datos que serán pasados al formulario que se registro en la propiedad Opener.
Si bien este es un ejemplo, la misma técnica podría ser utilizada para realizar distintas acciones y pasaje de datos entre formularios.
public partial class Form2 : Form { public IForm Opener { get; set; } public Form2() { InitializeComponent(); } private DataTable LoadDataTable() { DataTable dt = new DataTable(); dt.Columns.Add("Id"); dt.Columns.Add("Nombre"); for(int i=0 ; i <= 4; i++){ DataRow row = dt.NewRow(); row["Id"] = i; row["Nombre"] = String.Format("Nombre {0}", i); dt.Rows.Add(row); } return dt; } private void Button1_Click(object sender, EventArgs e) { this.Close(); } private void Form2_FormClosing(object sender, FormClosingEventArgs e) { DataTable dataTable = LoadDataTable(); bool estadoOperacion = this.Opener.LoadDataGridView(dataTable); e.Cancel = !estadoOperacion; } }
La línea 3 define la propiedad Opener cuya utilización se visualizo en el código del Form1, la declaración de la misma utiliza el concepto de propiedades autoimplementadas.
La interacción entre formularios retorna un resultado que el formulario hijo utilizara para saber si debe proseguir con el cierre del formulario o cancelarlo.
En este simple ejemplo el formulario padre retorna siempre un estado satisfactorio de la operación, pero podría ser utilizado para capturar errores e informarlo al formulario hijo de esta situación.
Conclusión
La interacción entre formulario en un entorno MDI requiere de ciertas modificaciones al uso normal de iteración entre estos, igualmente la idea básica no es afectada.
Solo la imposibilidad de definir un formulario como owner añadió cierta complejidad al ejemplo, pero fue salvada con el uso de propiedades.
[C#] |
[VB.NET] |
esta muy bien explicado. me sirvio mucho, a un q lo apliquede otra forma.
ResponderEliminarZac-Nicte
Muchas gracias de nuevo Leandro.
ResponderEliminarTu Blog es mi referencia en el desarrollo y nuevas ideas.
Excelente aporte, me fue de gran ayuda
Saludos leandro, me sirvio de mucho esa forma de comunicacion entre forms desde un MDI, he aplicado correctamente esta forma y funciona, pero tengo un problemita, por decir: al abrir un formulario hijo llamado "form1" desde MDI principal en estado MAXIMIZED, y luego al abrir otro hijo "form2" en estado NORMAL desde el "form1", el "form2" se maxima automaticamente, como podria solucionar eso.
ResponderEliminarhola Fernando
ResponderEliminarel tema hasta donde se es que los forms en un entorno MDI se comportan de esa forma
quzias para evitarlo deberias abrir ese form por fuera del mdi, usando el ShowDialog() para que se vea modal
es mas sino recuerdo mal cuando estas en un entorno mdi las opciones de ControlBox para quitarle las acciones de maximizado y minimizado no te deja hacerlo, igual podrias probar de ver si va por ese lado, quita las propiedades de MaximizeBox, pero estoy seguro que no tendra efecto
saludos
Hola Leandro, muchas gracias por tus aportes.
ResponderEliminarEn un entorno de pruebas la comunicacion de ambos formularios con la interfaz me funciona correctamente, pero luego cuando lo quiero aplicar en mi primer proyecto vb.net en el formulario hijo en esta linea:
Public Property Opener() As IForm
Me da este mensaje de error:'Opener' no puede exponer el tipo 'IForm' fuera del proyecto a través de class 'Buscar_cliente'.
Por mas que busco, no me doy cuenta como solucionarlo. Podras guiarme para ver por donde puede venir el problema.
Muchas gracias, saludos, Hugo.
hola hugodoy
ResponderEliminarla interfaz IForm donde la defienes?
recuerda que deberia estar por fuera de la clase Buscar_cliente
o sea a nivel de namespace es que defines la interfaz
veras que yo alli defino un archivo IForm.vb donde defino la interfaz
saludos
Hola Leandro !
ResponderEliminarLa defino a nivel de espacio de nombres:
...
Interface IForm
Sub ChangeTextBoxText(ByVal text As String)
End Interface
...
Textualmente copie una similar a la de tus ejemplos.
La implemento desde la clase pedidos_de_facturacion :
...
Public Class PEDIDOS_DE_FACTURACION
'***
Implements IForm
' ASIGNO LA CONEXION POR AHORA FIJA EN EL \DOCUMENTS\SCC.MDF
Private Const cs As String = "Data Source=.\SQLEXPRESS;" & _
"AttachDbFilename=C:\Users\hugodoy\Documents\SCC.mdf;" & _
"Integrated Security=True;" & _
"Connect Timeout=30;" & _
"User Instance=True"
Private cont As Integer = 0
...
Por ultimo en el metodo button1_click llamo a la clase buscar_cliente:
...
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim form As New Buscar_cliente()
form.MdiParent = Me.MdiParent
form.Show()
End Sub
...
Sigo sin darme cuenta por donde esta el inconveniente.
Saludos, Hugo.
Perdon, lo deje asi porque me daba el error !!!
ResponderEliminarEl metodo button1_click llamo a la clase buscar_cliente:
...
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim form As New Buscar_cliente()
form.MdiParent = Me.MdiParent
form.Show()
' ' ' 'Dim form As New Buscar_cliente
' ' ' 'form.MdiParent = Me.MdiParent
' ' ' 'form.Opener = CType(Me, IForm)
' ' ' 'form.Show(Me)
End Sub
...
Saludos, Hugo.
hola hugodoy
ResponderEliminarla explicacion muy bien, pero te falto mencionar cual es el problema, si hay un error cual es ? que mensaje estas recibiendo
veo ademas que en ningun momento defines la propiedad "Opener", veras en el ejmeplo que debes definir una propiedad en el form hijo.
ademas PEDIDOS_DE_FACTURACION que seria ? es un formulario ? porque en este implementa la interfaz pero despues usa
Dim form As New Buscar_cliente()
como se puede interpretar eso, define la IForm en una clase pero despues instancias otra distinta
creo que el problema es que estas mezclando las cosas
saludos
Hola Leandro
ResponderEliminarEl mensaje de error es:
'Opener' no puede exponer el tipo 'IForm' fuera del proyecto a través de class 'Buscar_cliente'
Me lo da el compilador, no en tiempo de ejecucion.
Pedidos_de_facturacion es un Formulario
Button1_clic (como me daba error, "probando" le mande cualquiera) en realidad lo que estaba con comentarios era lo corresponde. Cuando me da el error, el codigo me queda asi:
...
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' ' ' 'Dim form As New Buscar_cliente()
' ' ' ' form.MdiParent = Me.MdiParent
' ' ' ' form.Show()
Dim form As New Buscar_cliente
form.MdiParent = Me.MdiParent
form.Opener = CType(Me, IForm)
form.Show(Me)
End Sub
...
Asi estaria bien ?
Una vez mas muchas gracias por tus aportes.
Saludos, Hugo.
hola hugodoy
ResponderEliminarpero porque hace esto:
form.Opener = CType(Me, IForm)
el opener deberia ser el propio formulario
form.Opener = Me
solo asignas el Me que es el form que realiza la apertura
luego dentro del form hijo es que casteas al IForm
saludos
Leandro excelente ejemplo como siempre, tengo un consulta. Tengo un Form1 en el cual tengo un textbox codproducto este realiza la busqueda del producto si el mismo cuenta con mas de una unidad de medida debe abrirse el form2 con las unidades de medida existentes para ese codigo de producto. La consulta esencial es en el primer form1 hago el query con el codproducto para luego enviar esos datos al form2?. Agradeceria tu orientacion.
ResponderEliminarHola Leandro, Feliz Año.
ResponderEliminarSolo para comentarte que el error:
'Opener' no puede exponer el tipo 'IForm' fuera del proyecto a través de class 'Buscar_cliente' ya no me dio mas.
Fijandome en la ayuda en linea: http://msdn.microsoft.com/es-es/library/90htzsex(VS.90).aspx
vi que podia ser un problema de tipo de variable, entonces hice este pequeño cambio que me dio resultados:
...
Public Class Buscar_cliente
'***
Private formOpener As IForm
'*** CAMBIE EL TIPO PUBLIC A FRIEND !
Friend Property Opener() As IForm
Get
Return formOpener
End Get
Set(ByVal value As IForm)
formOpener = value
End Set
End Property
...
y ya no me dio mas el error.
Saludos, Hugo.
hola hugodoy
ResponderEliminarque bien que pudiste resolver el problema
pero me suena raro el error que comentas, con Public deberia haber funcionado, salvo que la Interface este definida de forma que esta se exponga distinta
has validado que la definicion de IForm se Public
Public Interface IForm
End Interface
saludos
hola Richard
ResponderEliminarestas equivocando el camino, no deberias ejecutar una query en el form1 para luego pasar el resultado al form2
deberias tomar la minima informacion que necesitas para poder realizar la query, quizas pasar alguna informacion puntual y esto pasar al otro formulario
para ahi si realizar la query en el form2
saludos
Hola Leandro
ResponderEliminarSi, perfecto, tenes razon.
Defini la Interface como Publica
...
Public Interface IForm
Sub ChangeTextBoxText(ByVal text As String)
End Interface
...
y volvi atras el Friend a Public
...
Public Class Buscar_cliente
'***
Private formOpener As IForm
'*** VOLVI A CAMBIAR EL TIPO FRIEND POR PUBLIC!
Public Property Opener() As IForm
Get
Return formOpener
End Get
Set(ByVal value As IForm)
formOpener = value
End Set
End Property
...
Gracias !
Saludos, Hugo.
hola leandro gracias por tus aportes
ResponderEliminarveras intento combinar tus ejemplo de "C# comunicar formularios MDI" y "[DataGridView] Parte 3 – Pasaje de información entre grillas en distintos formulario" el problema que tengo es que intento combinar tus codigos para poder comunicar formularios para poder pasar informacion entre DataGridViews pero al abrir el formulario donde paso me sale este mensaje de error "Un formulario que no es de nivel superior no se puede mostrar como un cuadro de diálogo modal. Quite el formulario de los formularios primarios que lo contengan antes de llamar a Show"
es que intento hacer esto para crear un sistema para pasar datos entre DataGridView pero con un formulario como el de tu ejemplo
me podrias ayudar gracias y si lo necesitas te paso los codigos del sistema
hola Ascencio
ResponderEliminarno entendi muy bien el problema, pareciera como que quiere usar un formulario MDI pero como modal, o sea abriendolo por medio del ShowDialog(), pero en un entorno mdi no puede hacer esto
no se si es este el problema, porque no me quedo claro
saludos
Saludos, necesito una ayudita urgente.
ResponderEliminarTe explico; tengo el formulario MDI el principal con una barra de tareas con una opcion de imprimir, esta opcion imprimir debe ejecutar el metodo de imprimir que le creo en los formularios hijos del formulario que esta activo en ese momento.
Como haria esto. De antemano muchas gracias.
hola pedferdev
ResponderEliminarpodrias aplciar la tecncia que comento aqui
[WinForms] Realizar Acciones en formularios hijo
saludos
hola,, excelente ejemplo,, una pregunta, como hago para utilizar un metodo que esta en un formulario que no es el padre
ResponderEliminarhola Jdl
ResponderEliminarpero existe algun medio de conexion entre estos formularios? o alguna forma en la cual puedas tomar la instancia del form que quieres invocar
con la instancia puedes invocar directo, sin la instancia podrias usar
[Winforms] Singleton - Pasar datos entre formulario
la parte de evento para realizar una accion en el otro form
saludos
las dos forms se inicializan desde una ventana principal con la propiedad mdiContanier lo que quiero es que al ingresar un registro desde el form1 este actualice la lista de registros que hay en el form2,, pero el form2 no es hijo del form 1 ,, muchas gracias por tu atencion,
ResponderEliminarhola Jdl
ResponderEliminarpero entocnes quiere decir que desde el form principal puede teenr las instancias de estos dos form que se abren independientemente
porque si es asi podrias pasar la instancia de un form al otro para que luego se accedan entre ellos
o sea si usaste
Form2 frm2 = nre Form2();
frm2.Show();
luego en otra accion podrias usar
Form3 frm3 = nre Form3();
frm3.FormRelacionado = frm2;
frm3.Show();
por puesto crea una propiedad en el Form3 que sea del tipo form para poder asignar la instancia del mismo
saludos
Leandro buen dia... Te felicito por tu Blog y ejemplos, me han sido de mucha utilidad...
ResponderEliminarActualmente estoy queriendo implementar tu codigo pero me marca un error...
Inconsistent accessibility: property type 'appVarias...' is less accessible than property 'appVarias.../Form.Opener'
En el siguiente Link te envio el codigo que modifique para agregar tu ejemplo.
https://dl.dropbox.com/u/28531869/%5Bcsharp%5DWinDataGridViewPasajeInformacion.rar
Como puedes observar, estoy fusionando dos ejemplos que tu haz desarrollado, para darme a entender lo que deseo hacer te lo explico a continuación:
Tengo un Form Principal, Form1 y Form2.
El principal es MDI, y se llama al Form1, en form 1 tengo un grid tal como en tu ejemplo, desde este form se llama al form2 que tengo un datagridview que lo cargo desde la BD con datos varios, lo que quiero es seleccionar varias filas del grid de Form2, y al presionar un boton del form2, este envie los datos de las filas seleccionadas al form1, de ahi yo trabajaria con los datos del form1.
Espero me puedas apoyar con la solucion a mi problema, te lo agradezco de antemano.
Saludos, desde Honduras.
Moisés.
hola Moisés
ResponderEliminarhas validado la declaracion de la propiedad, verificaste que sea definida como "public" ?
saludos
Excelente Leandro!!!
ResponderEliminarEso era, es que no lo habia declarado como publico, ya me funciona como queria.
Saludos desde Honduras.
Moisés Banegas
Hola!! Estoy siguiendo tu ejemplo de Comunicar formularios de forma desacoplada estoy trabajando en visual basic 2010, pero al momento de hacer
ResponderEliminarDim _formInterface As IForm = CType(Me.Owner, IForm)
_formInterface.ChangeTextBoxText(TextBox1.Text)
me manda el siguiente error : Referencia a objeto no establecida como instancia de un objeto
Segui tu ejemplo paso a paso,odrias decirme que estoy haciendo mal .
Saludos
hola Marisel
ResponderEliminarpero validad que el form padre este implementando la interfaz de forma correcta ?
ademas valida que al abrir el form hijo uses
Dim frm As New FormHijo
frm.Show(Me)
ese Me que le pasas en el Show define el Owner
saludos
Hola leandro, cuando creo este evento dentro del datagridview
ResponderEliminarif (e.RowIndex < 0 || e.ColumnIndex != dgvEstudios.Columns["btnEliminar"].Index) { return; }
else
{
Int32 taskID = (Int32)dgvEstudios[0, e.RowIndex].Value;
MessageBox.Show(" " + taskID.ToString());
}
me arroja un error por que el valor de la celda no lo está capturando me devuelve un NULL, pero este mismo evento yo lo probé en un datagridview que está en un formulario que no posee mdiParent y me funcionó bien.
si sabes a que se debe este problema agradecería tu ayuda
de antemano muchas gracias
el evento es el CellClick
ResponderEliminarprivate void dgvEstudios_CellClick(object sender, DataGridViewCellEventArgs e)
{}
hola blogluis
ResponderEliminarcomo es eso de MdiParent porque no quedo claro
sera que estas accediendo a un grid que esta en otro formulario ?
que pasa si usas
dgvEstudios.Rows[e.RowIndex].Cells[0].Value
saludos
Excelente tutorial!!! Use el de VB.net Gracias
ResponderEliminarSaludos, como le puedo hacer para que en un solo "form padre" se puedan habrir dos formularios distintos pero que sean hijos del mismo padre?
ResponderEliminarForm1 (padre)
Form2 (hijo), Form3 (hijo)
aclarando que el diseño de ambos hijos es diferente y para diferente funcion.
hola ROBERTO
ResponderEliminardesde el Form2 cuando abres el form3 usarias
Form3 frm3 = new Form3();
frm3.MdiParent = this.MdiParent;
frm3.Show();
o sea asignas al form3 el mismo mdiparent que tiene el form2
saludos
ResponderEliminarHola Leonardo, es posible que el form2 sea un modal??
Saludos
hola MatiCris
ResponderEliminarsi estas en un ambiente MDI y ese forma parte del mdi container no puede mostrarlo como modal
para poder hacerlo no deberias definir el MdiParent
saludos
Gracias por tu respuesta Leandro funciono a la perfección.
ResponderEliminarSaludos
Gracias por el aporte, me funcionó bien.
ResponderEliminarGracias por tu aporte Leandro, tengo una grid con los nombre de columnas definidas ejemplo: articulo,nombre,precio.... al utilizar tu explicacion me agrega las columnas, como hago para que se inserten en las mismas columnas... Gracias
ResponderEliminarhola
EliminarNo se si entendi del todo el problema, pero si el grid esta enlazado a datos quizas debas agregar a este los nuevos datos que provienen del otro forma y al final volver asignar el DataSource para que actualice. Quzias debas usar linq para ubicar que row actualizar en luegar de udar un Add() directo
saludos
Gracias Leandro, ya solucione el problem utlize lo siguiente dataGridView7.AutoGenerateColumns = false;
ResponderEliminardataGridView7.DataSource = dataTableParam;
dataGridView7.Columns[0].DataPropertyName = "Aticulo";
Ahora tengo otro problema, tengo un botton que me envia a limpiar el datagridviewer con esta sentencia .. dataGridView7.Rows.Clear(); pero me sale el siguiente error "No se controlo Argument Exception ...No se puede borrar esta lista." que tengo que hacer ....Gracias por la ayuda
Leandro me hago entender mejor, en el datagridviewer del formulario PADRE donde muestro los datos que he traido del datagridviewer HIJO no me los permite borrar con rows.clear()...
ResponderEliminarme sale el siguiente error "No se controlo Argument Exception ...No se puede borrar esta lista." ...Gracias.
Gracias muy util, me a servido de mucho
ResponderEliminar