Constructor ''UserControl'' con parámetros en C#
winforms parameters (10)
Bueno, en resumen, el diseñador es el tipo de persona al que le gustan los constructores sin parámetros. Por lo tanto, que yo sepa, si realmente desea usar constructores basados en parámetros, probablemente esté trabado con el trabajo de una forma u otra.
Llámame loco, pero soy el tipo de persona que le gustan los constructores con parámetros (si es necesario), a diferencia de un constructor sin parámetros seguido de las propiedades de configuración. Mi proceso de pensamiento: si las propiedades son necesarias para construir realmente el objeto, deberían ir al constructor. Tengo dos ventajas:
- Sé que cuando se construye un objeto (sin error / excepción), mi objeto es bueno.
- Ayuda a evitar olvidarse de establecer una propiedad determinada.
Esta forma de pensar me está empezando a hacer daño en lo que respecta al desarrollo de la forma / control de usuarios. Imagina este UserControl
:
public partial class MyUserControl : UserControl
{
public MyUserControl(int parm1, string parm2)
{
// We''ll do something with the parms, I promise
InitializeComponent();
}
}
En designtime, si UserControl
este UserControl
en un formulario, recibo una Exception
:
Error al crear el componente ''MyUserControl'' ...
System.MissingMethodException: ningún constructor sin parámetros definido para este objeto.
Parece que, para mí, la única forma de evitarlo era agregar el constructor predeterminado (a menos que alguien más sepa una forma).
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
public MyUserControl(int parm1, string parm2)
{
// We''ll do something with the parms, I promise
InitializeComponent();
}
}
El objetivo de no incluir el constructor sin parámetros era evitar usarlo. Y ni siquiera puedo usar la propiedad DesignMode
para hacer algo como:
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
if (this.DesignMode)
{
InitializeComponent();
return;
}
throw new Exception("Use constructor with parameters");
}
}
Esto tampoco funciona:
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
Bien, avanzando ...
Tengo mi constructor sin parámetros, puedo soltarlo en el formulario y el InitializeComponent
del formulario se verá así:
private void InitializeComponent()
{
this.myControl1 = new MyControl();
// blah, blah
}
Y créanme, porque lo hice (sí, ignorando los comentarios que generó Visual Studio), intenté dar vueltas y pasé parámetros a InitializeComponent
para poder pasarlos al constructor de MyControl
.
Lo que me lleva a esto:
public MyForm()
{
InitializeComponent(); // Constructed once with no parameters
// Constructed a second time, what I really want
this.myControl1 = new MyControl(anInt, aString);
}
Para que use UserControl
con parámetros para el constructor, ¿tengo que agregar un segundo constructor que no necesito? ¿Y crear una instancia del control dos veces?
Siento que debo estar haciendo algo mal. ¿Pensamientos? Opiniones? Aseguramiento (con suerte)?
Desafortunadamente, este es un problema de diseño que ocurrirá con frecuencia, no solo en el espacio de control.
A menudo hay situaciones en las que necesita tener un constructor sin parámetros, aunque un constructor sin parámetros no sea ideal. Por ejemplo, muchos tipos de valores, IMO, estarían mejor sin constructores sin parámetros, pero es imposible crear uno que funcione de esa manera.
En estas situaciones, solo tiene que diseñar el control / componente de la mejor manera posible. Usar parámetros predeterminados razonables (y preferiblemente los más comunes) puede ayudar dramáticamente, ya que al menos (con suerte) puede inicializar el componente con un buen valor.
Además, intente diseñar el componente de manera que pueda cambiar estas propiedades después de que se genere el componente. Con los componentes de Windows Forms, esto generalmente está bien, ya que prácticamente se puede hacer cualquier cosa hasta el momento de carga de forma segura.
De nuevo, estoy de acuerdo, esto no es ideal, pero es algo con lo que tenemos que vivir y trabajar.
Ha pasado bastante tiempo desde que se hizo la pregunta, pero tal vez mi enfoque sea útil para alguien.
Personalmente, también prefiero usar constructores parametrizados para evitar olvidar establecer cierta propiedad.
Entonces, en lugar de usar el Constructor real, simplemente defino un PostConstructor vacío público donde se ponen todas las cosas que normalmente pondrías en el Constructor. Entonces, el Constructor Actual del UserControl siempre contiene solo InitializeComponent () . De esta forma, no tiene que ajustar su paradigma de programación favorito a VisualStudios para ejecutar el Diseñador correctamente. Para que este esquema de programación funcione, debe seguirse desde abajo.
En la práctica, este PostConstructionalizm se vería algo así: Comencemos con un Control en la parte inferior de su jerarquía de llamadas UserControl.
public partial class ChildControl : UserControl
{
public ChildControl()
{
InitializeComponent();
}
public void PostConstructor(YourParameters[])
{
//setting parameters/fillingdata into form
}
}
Entonces, un UserControl que contenga ChildControl se vería así:
public partial class FatherControl : UserControl
{
public FatherControl()
{
InitializeComponent();
}
public void PostConstructor(YourParameters[])
{
ChildControl.PostConstructor(YourParameters[])
//setting parameters/fillingdata into form
}
}
Y finalmente un Formulario que llama al Control de Usuario simplemente pone el PostConstructor después de InitializeComponent .
public partial class UI : Form
{
public UI(yourParameters[])
{
InitializeComponent();
FatherControl.PostConstructor(yourParameters[]);
}
}
Hay dos paradigmas que compiten para diseñar clases:
- Use constructores sin parámetros y establezca un conjunto de propiedades después
- Usar constructores parametrizados para establecer propiedades en el constructor
El Diseñador de Windows Forms de Visual Studio lo obliga a proporcionar un constuctor sin parámetros en los controles para que funcione correctamente. En realidad, solo requiere un constructor sin parámetros para crear instancias de controles, pero no para diseñarlos (el diseñador realmente analizará el método InitializeComponent al diseñar un control). Esto significa que puede usar el diseñador para diseñar un formulario o control de usuario sin un constructor sin parámetros, pero no puede diseñar otro control para usar ese control porque el diseñador no podrá instanciarlo.
Si no tiene la intención de crear instancias de sus controles mediante programación (es decir, crear su UI "a mano"), no se preocupe por crear constructores parametrizados, ya que no se usarán. Incluso si va a instanciar mediante programación sus controles, puede proporcionar un constructor sin parámetros para que puedan ser utilizados en el diseñador si fuera necesario.
Independientemente del paradigma que use, generalmente también es una buena idea colocar un código de inicialización largo en el método OnLoad()
, especialmente dado que la propiedad DesignMode
funcionará en tiempo de carga, pero no funcionará en el constructor.
Las decisiones de diseño tomadas con respecto a la forma en que Windows Forms funciona más o menos excluyen los componentes parametrizados de Windows Forms. Puedes usarlos, pero cuando lo haces estás saliendo de los mecanismos generalmente aprobados. Por el contrario, Windows Forms prefiere la inicialización de valores a través de propiedades. Esta es una técnica de diseño válida, si no se usa ampliamente.
Esto tiene algunos beneficios, sin embargo.
- Facilidad de uso para los clientes. El código del cliente no necesita rastrear un montón de datos, puede crear algo inmediatamente y simplemente verlo con resultados sensibles (aunque poco interesantes).
- Facilidad de uso para el diseñador. El código de diseñador es más claro y más fácil de analizar en general.
- Desalienta las dependencias de datos inusuales dentro de un solo componente. (Aunque incluso Microsoft sopló este con el
SplitContainer
)
También hay mucho apoyo en las formas para trabajar correctamente con el diseñador en esta técnica. Cosas como DefaultValueAttribute
, DesignerSerializationVisibilityAttribute
y BrowsableAttribute
dan la oportunidad de proporcionar una experiencia de cliente enriquecido con un mínimo esfuerzo.
(Este no es el único compromiso que se hizo para la experiencia del cliente en los formularios de Windows. Los componentes abstractos de la clase base también pueden ponerse peludos).
Sugiero seguir con un constructor sin parámetros y trabajar dentro de los principios de diseño de formularios de Windows. Si existen condiciones previas reales que su UserControl
debe imponer, encapsúlelas en otra clase y luego asigne una instancia de esa clase a su control a través de una propiedad. Esto dará una mejor separación de preocupación también.
Proporcione un constructor sin parámetros para el diseñador y haga que sea privado, si realmente debe hacerlo de esta manera ... :-)
EDITAR: Bueno, por supuesto, esto no funcionará para UserControls. Obviamente no estaba pensando claramente. El diseñador necesita ejecutar el código en InitializeComponent () y no puede funcionar si el constructor es privado. Lo siento por eso. Sí funciona para formularios, sin embargo.
Solo haz esto:
public partial class MyUserControl : UserControl
{
public MyUserControl() : this(-1, string.Empty)
{
}
public MyUserControl(int parm1, string parm2)
{
// We''ll do something with the parms, I promise
if (parm1 == -1) { ... }
InitializeComponent();
}
}
Entonces el constructor ''real'' puede actuar en consecuencia.
Tengo una forma de evitarlo.
- Cree un control A en el formulario con el constructor sin parámetros.
- Crea un control B con constructor parametrizado en el formulario contstructor.
- Copie la posición y el tamaño de A a B.
- Haz A invisible.
- Agregue B al padre de A.
Espero que esto ayude Acabo de encontrarme con la misma pregunta y probé y probé este método.
Código para demostrar:
public Form1()
{
InitializeComponent();
var holder = PositionHolderAlgorithmComboBox;
holder.Visible = false;
fixedKAlgorithmComboBox = new MiCluster.UI.Controls.AlgorithmComboBox(c => c.CanFixK);
fixedKAlgorithmComboBox.Name = "fixedKAlgorithmComboBox";
fixedKAlgorithmComboBox.Location = holder.Location;
fixedKAlgorithmComboBox.Size = new System.Drawing.Size(holder.Width, holder.Height);
holder.Parent.Controls.Add(fixedKAlgorithmComboBox);
}
el titular es Control A, fixedKAlgorithmComboBox es Control B.
Una solución aún mejor y más completa sería usar reflect para copiar las propiedades una por una de A a B. Por el momento, estoy ocupado y no estoy haciendo esto. Quizás en el futuro regrese con el código. Pero no es tan difícil y creo que puedes hacerlo tú mismo.
Tuve un problema similar al tratar de pasar un objeto creado en el formulario principal de Windows a un formulario UserControl personalizado. Lo que funcionó para mí fue agregar una propiedad con un valor predeterminado a UserControl.Designer.cs y actualizarla después de la llamada InitializeComponent () en el formulario principal. Tener un valor predeterminado evita que el diseñador de WinForms arroje un error de "Referencia de objeto no configurado a una instancia de un objeto".
Ejemplo:
// MainForm.cs
public partial class MainForm : Form
public MainForm()
{
/* code for parsing configuration parameters which producs in <myObj> myConfig */
InitializeComponent();
myUserControl1.config = myConfig; // set the config property to myConfig object
}
//myUserControl.Designer.cs
partial class myUserControl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
// define the public property to hold the config and give it a default value
private myObj _config = new myObj(param1, param2, ...);
public myObj config
{
get
{
return _config ;
}
set
{
_config = value;
}
}
#region Component Designer generated code
...
}
¡Espero que esto ayude!
yo recomendaria
public partial class MyUserControl : UserControl
{
private int _parm1;
private string _parm2;
private MyUserControl()
{
InitializeComponent();
}
public MyUserControl(int parm1, string parm2) : this()
{
_parm1 = parm1;
_parm2 = parm2;
}
}
De esta forma, el constructor base siempre se llama primero y cualquier referencia a los componentes es válida.
A continuación, puede sobrecargar el controlador público si es necesario, asegurándose de que el control siempre se crea una instancia con los valores correctos.
De cualquier manera, se asegura de que nunca se llame al ctor sin parámetros.
No lo he probado, así que si se cae, ¡me disculpo!