Nennen Sie mich verrückt, aber ich bin der Typ, der Konstruktoren mit Parametern (wenn nötig) liebt, im Gegensatz zu einem Konstruktor ohne Parameter, auf den das Festlegen von Eigenschaften folgt. Mein Denkprozess: Wenn die Eigenschaften zum Erstellen des Objekts erforderlich sind, sollten sie in den Konstruktor aufgenommen werden. Ich habe zwei Vorteile:
Diese Denkweise schmerzt mich in Bezug auf die Entwicklung von Formularen/Benutzerkontrollen. Stellen Sie sich diese UserControl
vor:
public partial class MyUserControl : UserControl
{
public MyUserControl(int parm1, string parm2)
{
// We'll do something with the parms, I promise
InitializeComponent();
}
}
Wenn ich diese UserControl
auf ein Formular lösche, erhalte ich zur Entwurfszeit eine Exception
:
Fehler beim Erstellen der Komponente "MyUserControl" ...
System.MissingMethodException - Kein parameterloser Konstruktor für dieses Objekt definiert.
Es scheint mir, als wäre dies der einzige Weg, um den Standardkonstruktor hinzuzufügen (es sei denn, jemand anderes kennt einen Weg).
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
public MyUserControl(int parm1, string parm2)
{
// We'll do something with the parms, I promise
InitializeComponent();
}
}
Der parameterlose Konstruktor sollte nicht verwendet werden. Ich kann die DesignMode
-Eigenschaft auch nicht verwenden, um Folgendes zu tun:
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
if (this.DesignMode)
{
InitializeComponent();
return;
}
throw new Exception("Use constructor with parameters");
}
}
Das funktioniert auch nicht:
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
Gut, geht mit ...
Ich habe meinen parameterlosen Konstruktor, ich kann ihn im Formular ablegen, und die Variable InitializeComponent
des Formulars sieht folgendermaßen aus:
private void InitializeComponent()
{
this.myControl1 = new MyControl();
// blah, blah
}
Und vertrauen Sie mir, weil ich es getan habe (ja, die Kommentare, die Visual Studio ignoriert hat), ich versuchte herumzuspielen und Parameter an InitializeComponent
übergeben, damit ich sie an den Konstruktor von MyControl
übergeben konnte.
Was mich dazu führt:
public MyForm()
{
InitializeComponent(); // Constructed once with no parameters
// Constructed a second time, what I really want
this.myControl1 = new MyControl(anInt, aString);
}
Um eine UserControl
mit Parametern für den Konstruktor zu verwenden, muss ich einen zweiten Konstruktor hinzufügen, den ich nicht benötige? Und die Kontrolle zweimal instanziieren?
Ich habe das Gefühl, dass ich etwas falsch machen muss. Gedanken? Meinungen Versicherung (hoffentlich)?
Entwurfsentscheidungen bezüglich der Funktionsweise von Windows Forms schließen mehr oder weniger parametrisierte .ctors für Windows-Formularkomponenten aus. Sie können sie verwenden, aber wenn Sie dies tun, verlassen Sie die allgemein anerkannten Mechanismen. Vielmehr bevorzugt Windows Forms die Initialisierung von Werten über Eigenschaften. Dies ist eine gültige Designtechnik, wenn nicht weit verbreitet.
Dies hat jedoch einige Vorteile.
SplitContainer
durchgeblasen hat)Es gibt eine Menge Unterstützung in Formularen, um auch in dieser Technik richtig mit dem Designer zusammenzuarbeiten. Dinge wie DefaultValueAttribute
, DesignerSerializationVisibilityAttribute
und BrowsableAttribute
bieten Ihnen die Möglichkeit, mit minimalem Aufwand ein reichhaltiges Kundenerlebnis zu bieten.
(Dies ist nicht der einzige Kompromiss, der für die Kundenerfahrung in Windows-Formularen getroffen wurde. Abstrakte Basisklassenkomponenten können auch haarig werden.)
Ich würde vorschlagen, bei einem parameterlosen Konstruktor zu bleiben und innerhalb der Windows Forms-Design-Prinzipien zu arbeiten. Wenn es echte Vorbedingungen gibt, die Ihre UserControl
erzwingen muss, kapseln Sie sie in eine andere Klasse ein und weisen Sie Ihrem Steuerelement dann über eine Eigenschaft eine Instanz dieser Klasse zu. Dies führt zu einer etwas besseren Trennung der Bedenken.
Es gibt zwei konkurrierende Paradigmen zur Gestaltung von Klassen:
Der Visual Studio-Windows Forms-Designer zwingt Sie zur Bereitstellung eines parameterlosen Konstuktors für Steuerelemente, damit er ordnungsgemäß funktioniert. Tatsächlich ist nur ein parameterloser Konstruktor erforderlich, um Steuerelemente zu instanziieren, nicht aber um sie zu entwerfen (der Designer parst die InitializeComponent-Methode beim Entwerfen eines Steuerelements). Dies bedeutet, dass Sie den Designer zum Entwerfen eines Formulars oder Benutzersteuerelements ohne parameterlosen Konstruktor verwenden können. Sie können jedoch kein anderes Steuerelement für die Verwendung dieses Steuerelements entwerfen, da der Designer es nicht instanziieren kann.
Wenn Sie Ihre Steuerelemente nicht programmgesteuert instanziieren möchten (d. H. Ihre Benutzeroberfläche "von Hand" erstellen), müssen Sie sich keine Sorgen um die Erstellung parametrisierter Konstruktoren machen, da diese nicht verwendet werden. Selbst wenn Sie Ihre Steuerelemente programmgesteuert instanziieren möchten, möchten Sie möglicherweise einen parameterlosen Konstruktor bereitstellen, damit diese bei Bedarf im Designer verwendet werden können.
Unabhängig davon, welches Paradigma Sie verwenden, ist es im Allgemeinen auch eine gute Idee, langwierigen Initialisierungscode in die OnLoad()
-Methode einzufügen, insbesondere da die DesignMode
-Eigenschaft beim Laden funktioniert, nicht aber im Konstruktor.
Ich würde empfehlen
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;
}
}
Auf diese Weise wird der Basiskonstruktor immer zuerst aufgerufen, und alle Verweise auf Komponenten sind gültig.
Sie könnten dann ggf. den public ctor überlasten, um sicherzustellen, dass die Steuerung immer mit den korrekten Werten instanziiert wird.
So oder so stellen Sie sicher, dass der parameterlose Parameter niemals aufgerufen wird.
Ich habe das nicht getestet, also entschuldige ich mich, wenn es vorbei geht!
Stellen Sie dem Designer einen parameterlosen Konstruktor zur Verfügung und machen Sie ihn privat - wenn Sie es wirklich so machen müssen ... :-)
BEARBEITEN: Natürlich funktioniert dies nicht für UserControls. Ich habe offensichtlich nicht klar gedacht. Der Designer muss den Code in InitializeComponent () ausführen und er kann nicht funktionieren, wenn der Konstruktor privat ist. Das tut mir leid. Es funktioniert jedoch für Formulare.
Dies ist leider ein Designproblem, das häufig auftritt, nicht nur im Kontrollbereich.
Oft gibt es Situationen, in denen Sie einen parameterlosen Konstruktor benötigen, obwohl ein parameterloser Konstruktor nicht ideal ist. Beispielsweise wären viele Wertetypen, IMO, ohne parameterlose Konstruktoren besser geeignet, aber es ist unmöglich, einen solchen zu erstellen, der auf diese Weise funktioniert.
In diesen Situationen müssen Sie die Steuerung/Komponente nur so gut wie möglich entwerfen. Die Verwendung vernünftiger (und vorzugsweise die gebräuchlichsten) Standardparameter kann erheblich helfen, da Sie die Komponente (hoffentlich) mit einem guten Wert initialisieren können.
Versuchen Sie außerdem, die Komponente so zu entwerfen, dass Sie diese Eigenschaften ändern können, nachdem die Komponente generiert wurde. Bei Windows Forms-Komponenten ist dies normalerweise in Ordnung, da Sie alles tun können, bis die Ladezeit sicher ist.
Wieder stimme ich zu - das ist nicht ideal, aber es ist nur etwas, mit dem wir leben und umgehen müssen.
Kurz gesagt, der Designer ist ein Typ, der Parameter ohne Konstruktoren bevorzugt. Wenn Sie also wirklich auf Parameter basierende Konstruktoren verwenden möchten, arbeiten Sie wahrscheinlich mit der Arbeit auf die eine oder andere Weise.
Mach einfach das:
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();
}
}
Dann kann der "echte" Konstruktor entsprechend handeln.
Es ist schon eine Weile her, seit die Frage gestellt wurde, aber vielleicht ist mein Ansatz für jemanden hilfreich.
Ich persönlich bevorzuge auch parametrisierte Konstruktoren, um zu vermeiden, dass eine bestimmte Eigenschaft festgelegt wird.
Anstatt den eigentlichen Konstruktor zu verwenden, definiere ich einfach einen public void PostConstructor , in dem alle Dinge enthalten sind, die Sie normalerweise in den Konstruktor stellen würden. Der Actual Constructor des UserControl enthält also immer nur InitializeComponent () ..__ Auf diese Weise müssen Sie Ihr bevorzugtes Programmierparadigma nicht an VisualStudios anpassen, um den Designer ordnungsgemäß auszuführen. Damit dieses Programmierschema funktioniert, muss es von ganz unten befolgt werden.
In der Praxis würde dies PostConstructionalizm in etwa so aussehen: Beginnen wir mit einem Control am unteren Rand Ihrer UserControl-Aufrufhierarchie.
public partial class ChildControl : UserControl
{
public ChildControl()
{
InitializeComponent();
}
public void PostConstructor(YourParameters[])
{
//setting parameters/fillingdata into form
}
}
Ein UserControl mit ChildControl würde also so aussehen:
public partial class FatherControl : UserControl
{
public FatherControl()
{
InitializeComponent();
}
public void PostConstructor(YourParameters[])
{
ChildControl.PostConstructor(YourParameters[])
//setting parameters/fillingdata into form
}
}
Ein Formular, das eines der Benutzersteuerelemente aufruft, setzt den PostConstructor schließlich nach InitializeComponent .
public partial class UI : Form
{
public UI(yourParameters[])
{
InitializeComponent();
FatherControl.PostConstructor(yourParameters[]);
}
}
Ich habe eine Möglichkeit, es zu umgehen.
Hoffe das wird helfen. Ich bin gerade auf dieselbe Frage gestoßen und habe diese Methode getestet und getestet.
Code zur Demonstration:
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);
}
inhaber ist Control A, FixedKAlgorithmComboBox ist Control B.
Eine noch bessere und umfassendere Lösung wäre, die Eigenschaften mithilfe von reflect nacheinander von A nach B zu kopieren. Im Moment bin ich damit beschäftigt und mache das nicht. Vielleicht werde ich in Zukunft mit dem Code zurückkommen. Aber es ist nicht so schwer und ich glaube, dass Sie es selbst schaffen können.
Ich hatte ein ähnliches Problem beim Versuch, ein im Windows-Hauptformular erstelltes Objekt an ein benutzerdefiniertes UserControl-Formular zu übergeben. Was für mich funktionierte, war das Hinzufügen einer Eigenschaft mit einem Standardwert zu UserControl.Designer.cs und die Aktualisierung nach dem Aufruf InitializeComponent () im Hauptformular. Mit einem Standardwert wird verhindert, dass der WinForms-Designer einen Fehler "Objektverweis, der nicht auf eine Instanz eines Objekts festgelegt ist" auslöst.
Beispiel:
// 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
...
}
Hoffe das hilft!