¿La mejor forma de acceder a un control en otro formulario en Windows Forms?

En primer lugar, se trata de una aplicación de escritorio que usa Windows Forms, no una pregunta de ASP.NET .

Necesito interactuar con controles en otras formas. Estoy intentando acceder a los controles utilizando, por ejemplo, lo siguiente …

otherForm.Controls["nameOfControl"].Visible = false; 

No funciona de la manera que esperaría. Termino con una excepción lanzada desde Main . Sin embargo, si hago public los controles en lugar de private , puedo acceder a ellos directamente, así que …

 otherForm.nameOfControl.Visible = false; 

¿Pero es esa la mejor manera de hacerlo? ¿Hacer public los controles en la otra forma se considera “la mejor práctica”? ¿Hay una forma “mejor” de acceder a los controles en otra forma?

Explicación adicional:

Esto es en realidad una especie de seguimiento de otra pregunta que hice, ¿El mejor método para crear un tipo de interfaz de diálogo de preferencias de vista de árbol en C #? . La respuesta que obtuve fue excelente y resolví muchos, muchos problemas organizacionales que tenía en términos de mantener la UI directa y fácil de trabajar tanto en tiempo de ejecución como en tiempo de diseño. Sin embargo, trajo a colación este inconveniente problema de controlar fácilmente otros aspectos de la interfaz.

Básicamente, tengo una forma de raíz que crea una instancia de muchas otras formas que se sientan en un panel en el formulario raíz. Entonces, por ejemplo, un botón de radio en una de esas sub-formas podría necesitar alterar el estado de un ícono de la barra de estado en la forma raíz principal. En ese caso, necesito el formulario secundario para hablar con el control en la franja de estado del formulario padre (raíz). (Espero que tenga sentido, no en un tipo de “quién es el primero”).

En lugar de hacer público el control, puede crear una propiedad que controle su visibilidad:

 public bool ControlIsVisible { get { return control.Visible; } set { control.Visible = value; } } 

Esto crea un acceso adecuado a ese control que no expondrá todo el conjunto de propiedades del control.

Personalmente, recomendaría NO hacerlo … Si está respondiendo a algún tipo de acción y necesita cambiar su apariencia, preferiría plantear un evento y dejar que se solucione solo …

Este tipo de acoplamiento entre formas siempre me pone nervioso. Siempre trato de mantener la UI lo más liviana e independiente posible.

Espero que esto ayude. Tal vez podría ampliar el escenario si no?

El primero no funciona, por supuesto. Los controles en un formulario son privados, visibles solo para esa forma por diseño.

Hacer que todo sea público tampoco es la mejor manera.

Si me gustaría exponer algo al mundo exterior (que también puede significar otra forma), hago una propiedad pública para él.

 public Boolean nameOfControlVisible { get { return this.nameOfControl.Visible; } set { this.nameOfControl.Visible = value; } } 

Puede usar esta propiedad pública para ocultar o mostrar el control o para solicitar a la propiedad de visibilidad actual de control:

 otherForm.nameOfControlVisible = true; 

También puede exponer controles completos, pero creo que es demasiado, debe hacer visibles solo las propiedades que realmente desea usar desde fuera del formulario actual.

 public ControlType nameOfControlP { get { return this.nameOfControl; } set { this.nameOfControl = value; } } 

Después de leer los detalles adicionales, estoy de acuerdo con robcthegeek : plantear un evento. Cree un EventArgs personalizado y pase los parámetros necesarios a través de él.

Supongamos que tiene dos formularios y desea ocultar la propiedad de un formulario a través de otro:

 form1 ob = new form1(); ob.Show(this); this.Enabled= false; 

y cuando quieras volver a enfocarte de form1 a través del botón form2, entonces:

 Form1 ob = new Form1(); ob.Visible = true; this.Close(); 

Me encargaría de esto en el formulario principal. Puede notificar al otro formulario que necesita modificarse a sí mismo a través de un evento.

  1. Use un controlador de eventos para notificar a otros el formulario para manejarlo.
  2. Cree una propiedad pública en el formulario secundario y acceda desde el formulario principal (con un molde válido).
  3. Cree otro constructor en el formulario secundario para configurar los parámetros de inicialización del formulario
  4. Cree eventos personalizados y / o use clases (estáticas).

La mejor práctica sería # 4 si está utilizando formularios no modales.

Usted puede

  1. Cree un método público con el parámetro necesario en el formulario secundario y llámelo desde el formulario principal (con un molde válido)
  2. Cree una propiedad pública en formulario secundario y acceda a ella desde el formulario principal (con un molde válido)
  3. Crear otro constructor en forma secundaria para establecer los parámetros de inicialización del formulario
  4. Crear eventos personalizados y / o clases de uso (estáticas)

La mejor práctica sería # 4 si está utilizando formularios no modales.

Con la propiedad (resaltada) puedo obtener la instancia de la clase MainForm. Pero esta es una buena práctica? ¿Que recomiendas?

Para esto utilizo la propiedad MainFormInstance que se ejecuta en el método OnLoad.

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using LightInfocon.Data.LightBaseProvider; using System.Configuration; namespace SINJRectifier { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } protected override void OnLoad(EventArgs e) { UserInterface userInterfaceObj = new UserInterface(); this.chklbBasesList.Items.AddRange(userInterfaceObj.ExtentsList(this.chklbBasesList)); MainFormInstance.MainFormInstanceSet = this; //Here I get the instance } private void btnBegin_Click(object sender, EventArgs e) { Maestro.ConductSymphony(); ErrorHandling.SetExcecutionIsAllow(); } } static class MainFormInstance //Here I get the instance { private static MainForm mainFormInstance; public static MainForm MainFormInstanceSet { set { mainFormInstance = value; } } public static MainForm MainFormInstanceGet { get { return mainFormInstance; } } } } 

Estoy de acuerdo con el uso de eventos para esto. Como sospecho que está creando una aplicación MDI (ya que crea muchos formularios secundarios) y crea ventanas dinámicamente y puede que no sepa cuándo anular la suscripción a eventos, le recomendaría que eche un vistazo a los patrones de eventos débiles . Por desgracia, esto solo está disponible para Framework 3.0 y 3.5, pero algo similar se puede implementar de manera bastante sencilla con referencias débiles.

Sin embargo, si desea encontrar un control en un formulario basado en la referencia del formulario, no es suficiente simplemente mirar la colección de control del formulario. Como cada control tiene su propia colección de control, tendrá que recurrir a través de todos para encontrar un control específico. Puedes hacer esto con estos dos métodos (que se pueden mejorar).

 public static Control FindControl(Form form, string name) { foreach (Control control in form.Controls) { Control result = FindControl(form, control, name); if (result != null) return result; } return null; } private static Control FindControl(Form form, Control control, string name) { if (control.Name == name) { return control; } foreach (Control subControl in control.Controls) { Control result = FindControl(form, subControl, name); if (result != null) return result; } return null; } 

@Lars, un buen llamado para eludir las referencias de Formularios, también lo he visto a mí mismo. Asqueroso. ¡Nunca los he visto pasarlos a la capa BLL! ¡Eso ni siquiera tiene sentido! Eso podría haber afectado seriamente el rendimiento, ¿verdad? Si en algún lugar del BLL se guardaba la referencia, el formulario permanecería en la memoria, ¿verdad?

¡Tienes mi simpatía! 😉


@Ed, RE su comentario sobre hacer los Formularios UserControls. Dylan ya ha señalado que la forma de raíz crea una instancia de muchos formularios secundarios, dando la impresión de una aplicación MDI (donde supongo que los usuarios pueden querer cerrar varios formularios). Si estoy en lo correcto en esta suposición, creo que sería mejor guardarlos como formularios. Ciertamente abierto a la corrección sin embargo 🙂

¿Sus formularios hijos realmente necesitan ser Formularios? ¿Podrían ser controles de usuario en su lugar? De esta forma, podrían fácilmente generar eventos para el formulario principal y podría encapsular mejor su lógica en una sola clase (al menos, lógicamente, ya están todas las clases).

@Lars: estás aquí mismo. Esto fue algo que hice en mis comienzos y no he tenido que volver a hacerlo, por eso primero sugerí plantear un evento, pero mi otro método realmente rompería cualquier apariencia de encapsulación.

@Rob: Sí, suena bien :). 0/2 en este …

Solo debe acceder al contenido de una vista desde otra si está creando controles / módulos / componentes más complejos. De lo contrario, debe hacer esto a través de la architecture estándar Model-View-Controller: debe conectar el estado habilitado de los controles que le interesan a algún predicado a nivel de modelo que suministre la información correcta.

Por ejemplo, si quisiera habilitar un botón Guardar solo cuando se ingresara toda la información requerida, tendría un método predicado que indique cuándo los objetos modelo que representan ese formulario se encuentran en un estado que se puede guardar. Luego, en el contexto en el que elijo habilitar el botón, simplemente usaría el resultado de ese método.

Esto se traduce en una separación mucho más clara entre la lógica empresarial y la lógica de presentación, lo que permite que ambos evolucionen de forma más independiente, lo que le permite crear una interfaz con múltiples back-ends o múltiples interfaces con un solo back-end con facilidad.

También será mucho más fácil escribir pruebas de unidades y de aceptación, ya que puede seguir un patrón de ” Confiar pero verificar ” al hacerlo:

  1. Puede escribir un conjunto de pruebas que configuren los objetos de su modelo de varias maneras y verificar que el predicado “es salvable” arroje un resultado apropiado.

  2. Puede escribir un conjunto separado de esa comprobación para ver si su botón Guardar está conectado de forma adecuada al predicado “es salvable” (lo que sea que sea para su marco, en Cocoa para Mac OS X esto a menudo sería a través de un enlace).

Mientras pasen los dos juegos de pruebas, puede estar seguro de que su interfaz de usuario funcionará como usted lo desea.

Parece un candidato principal para separar la presentación del modelo de datos. En este caso, sus preferencias deben almacenarse en una clase separada que active actualizaciones de eventos cada vez que una propiedad en particular cambie (busque en INotifyPropertyChanged si sus propiedades son un conjunto discreto o en un solo evento si son más teclas de texto de forma libre )

En su vista de árbol, realizará los cambios en su modelo de preferencias, y luego activará un evento. En sus otros formularios, se suscribirá a los cambios que le interesen. En el controlador de eventos que usa para suscribirse a los cambios de propiedad, use esto. InvoqueRequired para ver si está en el hilo correcto para hacer la interfaz de usuario. llame, si no, luego use this.BeginInvoke para llamar al método deseado para actualizar el formulario.

Paso 1:

 string regno, exm, brd, cleg, strm, mrks, inyear; protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e) { string url; regno = GridView1.Rows[e.NewEditIndex].Cells[1].Text; exm = GridView1.Rows[e.NewEditIndex].Cells[2].Text; brd = GridView1.Rows[e.NewEditIndex].Cells[3].Text; cleg = GridView1.Rows[e.NewEditIndex].Cells[4].Text; strm = GridView1.Rows[e.NewEditIndex].Cells[5].Text; mrks = GridView1.Rows[e.NewEditIndex].Cells[6].Text; inyear = GridView1.Rows[e.NewEditIndex].Cells[7].Text; url = "academicinfo.aspx?regno=" + regno + ", " + exm + ", " + brd + ", " + cleg + ", " + strm + ", " + mrks + ", " + inyear; Response.Redirect(url); } 

Paso 2:

 protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { string prm_string = Convert.ToString(Request.QueryString["regno"]); if (prm_string != null) { string[] words = prm_string.Split(','); txt_regno.Text = words[0]; txt_board.Text = words[2]; txt_college.Text = words[3]; } } } 

Cambiar el modificador de público a interno. .Net utiliza deliberadamente modificador privado en lugar del público, debido a la prevención de cualquier acceso ilegal a sus métodos / propiedades / controles fuera de su proyecto. De hecho, el modificador público puede accederse donde sea, por lo que son realmente peligrosos. Cualquier cuerpo de tu proyecto puede acceder a tus métodos / propiedades. Pero en el modificador interno, ningún cuerpo (otro de su proyecto actual) puede acceder a sus métodos / propiedades.

Supongamos que está creando un proyecto, que tiene algunos campos secretos. Entonces, si estos campos son accesibles desde su proyecto, puede ser peligroso y contrario a sus ideas iniciales. Como una buena recomendación, puedo decir siempre usar modificador interno en lugar de modificador público.

¡Pero algo extraño!

Debo decir también en VB.Net, mientras que nuestros métodos / propiedades siguen siendo privados, se puede acceder desde otros formularios / clase llamando al formulario como una variable sin ningún otro problema.

No sé por qué en este lenguaje de progtwigción el comportamiento es diferente de C #. Como sabemos, ambos están utilizando la misma plataforma y afirman que son casi la misma plataforma back-end, pero como puede ver, todavía se comportan de manera diferente.

Pero he resuelto este problema con dos enfoques. Ya sea; mediante el uso de la interfaz (que no es una recomendación, como ya sabes, las interfaces generalmente necesitan un modificador público, y no se recomienda el uso de un modificador público (como te dije antes)),

O

Declara tu Formulario completo en alguna clase estática y variable estática y todavía hay un modificador interno. Luego, cuando suponga que usa ese formulario para mostrárselo a los usuarios, pase una nueva construcción de Formulario () a esa clase / variable estática. Ahora puede ser accesible en cualquier lugar como lo desee. Pero aún necesitas algo más. También declaras el modificador interno de tu elemento en Designer File of Form. Mientras su formulario esté abierto, puede ser accesible en cualquier lugar. Puede funcionar para ti muy bien.

Considera este ejemplo.

Supongamos que quiere acceder al TextBox de un Formulario.

Entonces, el primer trabajo es la statement de una variable estática en una clase estática (la razón de la estática es la facilidad de acceso sin utilizar nuevas claves en el futuro).

En segundo lugar vaya a la clase de diseñador de ese Formulario que supone el acceso a otras Formas. Cambie su statement de modificador TextBox de privada a interna. No te preocupes .Net nunca lo cambia de nuevo a modificador privado después de su cambio.

En tercer lugar, cuando desee llamar a ese formulario para abrirlo, pase la nueva Construcción de formulario a esa variable estática – >> clase estática.

Cuarto; desde cualquier otra Forma (en cualquier parte de su proyecto) puede acceder a esa forma / control mientras De está abierto.

mira el código a continuación (Tenemos tres objetos. 1- una clase estática (en nuestro ejemplo, la llamamos A)

2 – Cualquier otro formulario que desee abrir el Formulario final (tiene TextBox) (en nuestro ejemplo FormB).

3 – La Forma real que necesitamos para ser abiertos, y Suponemos acceder a su TextBox1 interno (en nuestro ejemplo FormC).

Mira los códigos a continuación:

clase estática interna A {

 internal static FormC FrmC; 

}

FormB … {. . .

A.FrmC = new FormC ();

. . .

}

FormC (Archivo de Diseñador). . . {

  internal System.Windows.Forms.TextBox TextBox1; 

}

Puede acceder a esa Variable estática (Aquí FormC) y su control interno (Aquí Textbox1) donde y cuando quiera, mientras que FormC está abierto.


Cualquier comentario / idea házmelo saber. Me complace escuchar de usted o de cualquier otro cuerpo sobre este tema más. Honestamente he tenido algunos problemas con respecto a este problema mencionado en el pasado. La mejor manera fue la segunda solución que espero que funcione para usted. Déjame saber cualquier idea / sugerencia nueva.

Gracias y un saludo

Mansoor Bozorgmehr

mansour.bozorgmehr@gmail.com

 public void Enable_Usercontrol1() { UserControl1 usercontrol1 = new UserControl1(); usercontrol1.Enabled = true; } /* Put this Anywhere in your Form and Call it by Enable_Usercontrol1(); Also, Make sure the Usercontrol1 Modifiers is Set to Protected Internal */