Costruttore ‘UserControl’ con parametri in C #

Chiamami pazzo, ma io sono il tipo di ragazzo a cui piacciono i costruttori con parametri (se necessario), al contrario di un costruttore senza parametri seguito dall’impostazione delle proprietà. Il mio processo di pensiero: se le proprietà sono necessarie per build effettivamente l’object, dovrebbero andare nel costruttore. Ottengo due vantaggi:

  1. So che quando un object è costruito (senza errori / eccezioni), il mio object è buono.
  2. Aiuta a evitare di dimenticare di impostare una certa proprietà.

Questa mentalità sta iniziando a farmi del male per quanto riguarda la forma / lo sviluppo di usercontrol. Immagina questo UserControl :

 public partial class MyUserControl : UserControl { public MyUserControl(int parm1, string parm2) { // We'll do something with the parms, I promise InitializeComponent(); } } 

In fase di progettazione, se cancello questo UserControl su un modulo, ottengo Exception :

Imansible creare il componente “MyUserControl” …
System.MissingMethodException – Nessun costruttore parametrico definito per questo object.

A me sembra che l’unico modo per aggiungere il costruttore predefinito (a meno che qualcun altro non conosca un modo).

 public partial class MyUserControl : UserControl { public MyUserControl() { InitializeComponent(); } public MyUserControl(int parm1, string parm2) { // We'll do something with the parms, I promise InitializeComponent(); } } 

L’intero punto di non includere il costruttore senza parametri era di evitare di usarlo. E non posso nemmeno usare la proprietà DesignMode per fare qualcosa del tipo:

 public partial class MyUserControl : UserControl { public MyUserControl() { if (this.DesignMode) { InitializeComponent(); return; } throw new Exception("Use constructor with parameters"); } } 

Questo non funziona neanche:

 if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) 

Bene, muovendomi …

Ho il mio costruttore senza parametri, posso rilasciarlo sul modulo, e il modulo InitializeComponent sarà simile al seguente:

 private void InitializeComponent() { this.myControl1 = new MyControl(); // blah, blah } 

E fidati di me, perché l’ho fatto (sì, ignorando i commenti generati da Visual Studio), ho provato a fare scherzi e ho passato i parametri a InitializeComponent modo che potessi passarli al costruttore di MyControl .

Il che mi porta a questo:

 public MyForm() { InitializeComponent(); // Constructed once with no parameters // Constructed a second time, what I really want this.myControl1 = new MyControl(anInt, aString); } 

Per me utilizzare un UserControl con parametri per il costruttore, devo aggiungere un secondo costruttore che non mi serve? E istanziare il controllo due volte?

Sento che devo fare qualcosa di sbagliato. Pensieri? Opinioni? Assicurazione (si spera)?

Le decisioni di progettazione prese in merito al modo in cui Windows Forms funziona più o meno precludono i .ctor parametrizzati per i componenti di Windows Form. Puoi usarli, ma quando lo fai passi fuori dai meccanismi generalmente approvati. Piuttosto, Windows Form preferisce l’inizializzazione dei valori tramite le proprietà. Questa è una tecnica di progettazione valida, se non ampiamente utilizzata.

Questo ha alcuni vantaggi, però.

  1. Facilità d’uso per i clienti. Il codice cliente non ha bisogno di rintracciare un gruppo di dati, può creare immediatamente qualcosa e vederlo semplicemente con risultati sensibili (se non interessanti).
  2. Facilità d’uso per il progettista. Il codice del designer è più chiaro e più facile da analizzare in generale.
  3. Scoraggia le insolite dipendenze dei dati all’interno di un singolo componente. (Anche se microsoft ha soffiato questo con SplitContainer )

C’è un sacco di supporto nei moduli per lavorare correttamente con il progettista anche in questa tecnica. Cose come DefaultValueAttribute , DesignerSerializationVisibilityAttribute e BrowsableAttribute ti danno la possibilità di fornire un’esperienza ricca di client con il minimo sforzo.

(Questo non è l’unico compromesso che è stato fatto per l’esperienza del cliente nei moduli di Windows. Anche componenti di class base astratti possono diventare pelosi.)

Suggerirei di attenersi a un costruttore senza parametri e di lavorare all’interno dei principi di progettazione delle forms di Windows. Se esistono precondizioni reali che il tuo UserControl deve applicare, incapsularle in un’altra class e quindi assegnare un’istanza di quella class al tuo controllo tramite una proprietà. Ciò consentirà anche una migliore separazione delle preoccupazioni.

Esistono due paradigmi in competizione per la progettazione di classi:

  1. Utilizzare i costruttori senza parametri e impostare successivamente un gruppo di proprietà
  2. Utilizzare i costruttori parametrizzati per impostare le proprietà nel costruttore

Visual Studio Windows Forms Designer ti obbliga a fornire un controllo parametrico sui controlli per funzionare correttamente. In realtà, richiede solo un costruttore senza parametri per istanziare i controlli, ma non per progettarli (il progettista analizzerà il metodo InitializeComponent durante la progettazione di un controllo). Ciò significa che è ansible utilizzare il progettista per progettare un modulo o un controllo utente senza un costruttore senza parametri, ma non è ansible progettare un altro controllo per l’utilizzo di tale controllo poiché il progettista non riuscirà a creare un’istanza.

Se non hai intenzione di creare un’istanza dei tuoi controlli in modo programmatico (ad esempio, costruisci l’interfaccia utente “a mano”), non preoccuparti della creazione di costruttori parametrizzati, poiché non verranno utilizzati. Anche se si sta programmando l’istanza dei controlli, si consiglia di fornire un costruttore senza parametri in modo che possano essere ancora utilizzati nel progettista, se necessario.

Indipendentemente dal paradigma che si usa, è anche generalmente una buona idea inserire un codice di inizializzazione lungo nel metodo OnLoad() , soprattutto perché la proprietà DesignMode funzionerà al momento del caricamento, ma non funzionerà nel costruttore.

io raccomanderei

 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; } } 

In questo modo, il costruttore di base viene sempre chiamato per primo e qualsiasi riferimento ai componenti è valido.

Potresti quindi sovraccaricare il responsabile pubblico, se necessario, assicurando che il controllo sia sempre istanziato con i valori corretti.

Ad ogni modo, ti assicuri che il CID senza parametri non venga mai chiamato.

Non ho provato questo, quindi se cade mi chiedo scusa!

Questo è sfortunatamente un problema di progettazione che si verificherà spesso, non solo nello spazio di controllo.

Ci sono spesso situazioni in cui è necessario avere un costruttore senza parametri, anche se un costruttore senza parametri non è l’ideale. Ad esempio, molti tipi di valore, IMO, starebbero meglio senza costruttori senza parametri, ma è imansible crearne uno che funzioni in questo modo.

In queste situazioni, devi semplicemente progettare il controllo / componente nel miglior modo ansible. L’utilizzo di parametri predefiniti ragionevoli (e preferibilmente il più comune) può aiutare in modo significativo, dal momento che è ansible (si spera) inizializzare il componente con un buon valore.

Inoltre, provare a progettare il componente in modo da poter modificare queste proprietà dopo la generazione del componente. Con i componenti di Windows Form, questo in genere va bene, dal momento che puoi praticamente fare qualsiasi cosa fino al momento del caricamento in sicurezza.

Ancora una volta, sono d’accordo – questo non è l’ideale, ma è solo qualcosa con cui dobbiamo convivere e lavorare.

Bene, in breve, il progettista è il tipo di ragazzo a cui piacciono i costruttori senza parametri. Quindi, per quanto sappia, se vuoi davvero usare costruttori basati su parametri, probabilmente sei bloccato a lavorarci intorno in un modo o nell’altro.

Fornisci un costruttore parametrico per il designer e rendilo privato – se davvero devi farlo in questo modo … 🙂

EDIT: Beh, naturalmente, questo non funzionerà con UserControls. Ovviamente non pensavo chiaramente. Il progettista deve eseguire il codice in InitializeComponent () e non può funzionare se il costruttore è privato. Mi dispiace per quello Funziona per le forms, tuttavia.

Basta fare questo:

 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(); } } 

Quindi il costruttore ‘reale’ può agire di conseguenza.

È passato un po ‘di tempo da quando è stata posta la domanda, ma forse il mio approccio è utile a qualcuno.

Personalmente preferisco anche usare Costruttori parametrizzati per evitare di dimenticare di impostare una certa proprietà.

Quindi, invece di usare il Costruttore reale, definisco semplicemente un PostConstructor vuoto pubblico in cui tutte le cose sono messe normalmente nel Costruttore. Quindi il costruttore effettivo di UserControl contiene sempre solo InitializeComponent () . In questo modo non è necessario adattare il tuo paradigma di programmazione preferito a VisualStudios per eseguire correttamente il Designer. Perché questo schema di programmazione funzioni, deve essere seguito dal basso.

In pratica questo PostConstructionalizm sarebbe simile a questo: iniziamo con un controllo nella parte inferiore della gerarchia di chiamate UserControl.

 public partial class ChildControl : UserControl { public ChildControl() { InitializeComponent(); } public void PostConstructor(YourParameters[]) { //setting parameters/fillingdata into form } } 

Quindi un UserControl contenente ChildControl sarebbe simile a questo:

 public partial class FatherControl : UserControl { public FatherControl() { InitializeComponent(); } public void PostConstructor(YourParameters[]) { ChildControl.PostConstructor(YourParameters[]) //setting parameters/fillingdata into form } } 

E infine un modulo che chiama uno dei controlli utente mette semplicemente PostConstructor dopo InitializeComponent .

 public partial class UI : Form { public UI(yourParameters[]) { InitializeComponent(); FatherControl.PostConstructor(yourParameters[]); } } 

Ho un modo per aggirarlo.

  1. Creare un controllo A sul modulo con il costruttore senza parametri.
  2. Creare un controllo B con costruttore parametrizzato nel modulo contstructor.
  3. Copia la posizione e la dimensione da A a B.
  4. Rendi invisibile.
  5. Aggiungi B al genitore di A.

Spero che questo ti sia d’aiuto. Ho appena incontrato la stessa domanda e ho provato e testato questo metodo.

Codice per dimostrare:

 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); } 

il titolare è Control A, fixedKAlgorithmComboBox è Control B.

Una soluzione ancora migliore e completa sarebbe quella di utilizzare reflect per copiare le proprietà una ad una da A a B. Per ora, sono occupato e non lo sto facendo. Forse in futuro tornerò con il codice. Ma non è così difficile e credo che tu possa farlo da solo.

Ho avuto un problema simile nel tentativo di passare un object creato nel Windows Form principale a un modulo UserControl personalizzato. Ciò che ha funzionato per me è stato l’aggiunta di una proprietà con un valore predefinito a UserControl.Designer.cs e l’aggiornamento dopo la chiamata InitializeComponent () nel modulo principale. Avere un valore predefinito impedisce al progettista WinForms di lanciare un errore “Riferimento object non impostato su un’istanza di un object”.

Esempio:

 // MainForm.cs public partial class MainForm : Form public MainForm() { /* code for parsing configuration parameters which producs in  myConfig */ InitializeComponent(); myUserControl1.config = myConfig; // set the config property to myConfig object } //myUserControl.Designer.cs partial class myUserControl { ///  /// Required designer variable. ///  private System.ComponentModel.IContainer components = null; ///  /// Clean up any resources being used. ///  /// true if managed resources should be disposed; otherwise, false. 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 ... } 

Spero che questo ti aiuti!