Wie richtig initialisiert eine Windows Forms-Anwendung
Ich geschrieben habe, dieses Muster-Programm - vereinfacht eine sehr komplexe Anwendung. Die gleiche Binär - [.exe] wird eine null-Zeiger-Ausnahme auf einigen Maschinen auf Start. Also, ich möchte wissen, wie man richtig bauen ein Windows Forms-Formular.
In unserer Anwendung die Form1_SizeChanged
- Methode ist ein Ergebnis der this.ResumeLayout(false)
Methode, die ist die Letzte Anweisung in InitializeComponents()
. Ich weiß nicht, zu simulieren, so dass ich nur die Größe geändert, mich für diesen test Programm.
public partial class Form1 : Form
{
public class Logger {
public Logger() { }
public void log(string str) {
Console.WriteLine("logging - " + str);
}
}
Logger logger = null;
public Form1()
{
InitializeComponent();
this.Size = new Size(200, 300);
MyInitialize();
}
private void MyInitialize() {
//Just that it takes some time.
Console.WriteLine("MyInitialize -- Enter");
for (int count = 0; count <5 ; count++){
Console.WriteLine("Sleeping -- " + count);
Thread.Sleep(1000);
}
logger = new Logger();
}
private void sleepingPill(int millisec) {
Thread.Sleep(millisec);
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
logger.log("Form1_SizeChanged -- Enter");
}
}
Nach meinem Verständnis Form1_SizeChanged
sollte nicht aufgerufen werden, es sei denn Form1
richtig aufgebaut ist. Kann jemand werfen etwas Licht auf, wie der Windows Forms-event-Architektur in diesem Szenario funktionieren?
Original Stack Trace: from our complex application
System.NullReferenceException was unhandled
Message=Object reference not set to an instance of an object.
Source=ABCD
StackTrace:
at ABCD.Form1.AppendToLog(String s)
at ABCD.Form1.Form1_SizeChanged(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnSizeChanged(EventArgs e)
at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height, Int32 clientWidth, Int32 clientHeight)
at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height)
at System.Windows.Forms.Control.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
at System.Windows.Forms.Form.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
at System.Windows.Forms.Control.ScaleControl(SizeF factor, BoundsSpecified specified)
at System.Windows.Forms.ScrollableControl.ScaleControl(SizeF factor, BoundsSpecified specified)
at System.Windows.Forms.Form.ScaleControl(SizeF factor, BoundsSpecified specified)
at System.Windows.Forms.Control.ScaleControl(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
at System.Windows.Forms.ContainerControl.Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
at System.Windows.Forms.ContainerControl.PerformAutoScale(Boolean includedBounds, Boolean excludedBounds)
at System.Windows.Forms.ContainerControl.PerformNeededAutoScaleOnLayout()
at System.Windows.Forms.ContainerControl.OnLayoutResuming(Boolean performLayout)
at System.Windows.Forms.Control.ResumeLayout(Boolean performLayout)
at ABCD.Form1.InitializeComponent()
at ABCD.Form1..ctor()
at ABCD.Program.Main()
InnerException:
Hinweis aus dem stack trace: Form1_sizeChanged aufgerufen, die InitializeComponents () .., die ich denke, sollte das nicht passieren. Form1_sizeChanged ist eine Instanz-Methode der Form1-Klasse und sollte nicht aufgerufen werden, bevor die Form1 erstellt. Wenn die .NET-Umgebung Bearbeiten möchten dieses Ereignis, sollte es warten, bis Form1 richtig aufgebaut ist, sollte es nicht?
Stack trace Hinzugefügt zu der Frage.
InformationsquelleAutor Karephul | 2011-02-10
Du musst angemeldet sein, um einen Kommentar abzugeben.
In Zeile zwei der Konstruktor legen Sie die Größe. Das wird inturn nennen Form1_SizeChanged (), bevor der logger erstellt. Bewegen Sie die Einstellung der Größe nach dem Aufruf MyInitialize.
Nein, die Ereignisse aus Benutzereingaben (Tastatur, Maus, etc.) sind in der Warteschlange von windows. Jedoch, durch die Einstellung der Size-Eigenschaft im code verursacht das SizeChanged-Ereignis sofort Feuer.
InformationsquelleAutor Richard Schneider
Persumably, Form1_SizeChanged wird aufgerufen, bevor MyInitialize und daher vor der logger initialisiert ist.
Ändern Sie die Methode zu
InformationsquelleAutor sgmoore
Ich würde vermuten, dass Sie immer Ihre null-Fehler, die auf das SizeChanged-Ereignis/
Ihre InitializeComponent () - routine legt die Form1_SizeChanged Ereignis zugeordnet werden, die Form ist SizeChanged-Ereignis. Ihre diese.Größe code wird ausgelöst, sagte Ereignis. Da logger.Protokoll nicht initialisiert, bis Sie Ihre MyInitialize routine, sehen Sie möglicherweise sporadischen Ergebnissen.
Grundsätzlich ist Windows in eine Warteschlange gestellt bis der Form1_SizeChanged Veranstaltung und Ihre app darauf reagieren wird asynchron, also manchmal ist es vielleicht darauf zu reagieren, nachdem der Logger() wird initialisiert und manchmal wird es reagieren, bevor der Logger wird initialisiert.
@Karephul: Der Grund, warum Sie sehen, dieses Verhalten ist, dass die Nachricht Schleife ist auf der Anwendungsebene, nicht die individuelle Klasse. Ein GUI-Fenster ist eine Klasse wie alles andere und Ereignisse empfangen, die von der message-loop, sobald die Nachricht Schleife schicken kann, um die Klasse und nicht unbedingt warten, bis der Konstruktor fertig zu sein.
Ich habe keinen speziellen link, der beschreibt genau das, was ich sage, aber Sie können mehr über die Windows-Messaging auf den folgenden link. msdn.microsoft.com/en-us/library/ms644927(VS.85).aspx Sie könnte wahrscheinlich verwenden Sie Spy++ sehe die Ereignisse, die auf Ihre form, aber wenn ich denke, ich würde vermuten, dass ein WM_SIZING Veranstaltung gebucht wurde für Ihre form, wenn Sie die Größe ändern. Wenn Sie ein PostMessage, es ist asynchornous, im Vergleich mit SendMessage, die synchron ist. Siehe msdn.microsoft.com/en-us/magazine/cc301431.aspx für mehr info auf PostMessage versus SendMessage.
InformationsquelleAutor John Koerner
Du die Frage beantwortet sich Ihre Frage. Es hat alles zu tun mit der ResumeLayout () - Aufruf.
Den automatisch generierten "InitializeComponent()" code schaltet die layout -, Zusammenbau der Komponenten und dreht sich dann das layout wieder auf. Dies, denke ich, ist eine Optimierung, weil die Neuberechnung layout auf jede sub-Komponente zusätzlich bekommen konnte, langsam. Beim layout wird dann wieder eingeschaltet, wird der resize-Ereignisse für viele Komponenten wird wahrscheinlich genannt, einschließlich Ihrer eigenen.
Beachten Sie, dass die forms-code spielt schnell und Locker mit was kann aufgerufen werden aus dem Konstruktor - Aufruf an alle Arten von virtuellen Methoden, die absolut sollte nicht aufgerufen werden, erst nachdem der Konstruktor fertig ist.
Dem Update ist die Initialisierung etwas, dass muss einem vollständig konstruierten Objekt wird in der Load-Ereignis. Das Load-Ereignis wird nur einmal aufgerufen, und wird aufgerufen, nachdem die Instanz ist vollständig aufgebaut. Für Sie bedeutet das entfernen der Form1_SizeChanged Ereignis aus, was die Entwicklungs-Umgebung verwaltet und fügen Sie einen Form1_Load-Ereignis enthält
Ja, es ist ein Schmerz. Ja, du hast Recht, und Formen, ist falsch. Zumindest die Arbeit nicht zu belastend wird.
Oh, das hab ich nicht gemacht. Ich habe gehört, dass die Move-Ereignisse können auftreten, bevor das Load-Ereignis, aber nachdem der Konstruktor fertig ist, also, wenn Sie müssen Verschieben alle Veranstaltungen aus irgendeinem Grund, müssen Sie möglicherweise nur tun defensiven Codierung in der Move-event-handler wie sgmoore zeigte.
InformationsquelleAutor mheyman