11.3 Das Codegerüst einer Windows-Anwendung 

Eine Windows-Anwendung wird aus Quelltextdateien und automatisch generiertem Code gebildet. In unserem Beispiel liegen vor:
- Startcode
- TextboxKopieren.vb
- TextboxKopieren.Designer.vb
- Generierter Code
11.3.1 Einstellungen 

Über die Projekteinstellungen lassen sich einige wesentliche Aspekte der Windows-Anwendung steuern. Wenn das Anwendungsframework aktiviert ist (siehe Abbildung 11.5), wird eine Menge Code automatisch generiert.
Abbildung 11.5 Anwendungseinstellungen
- Visuelle XP-Stile aktivieren: Wenn diese Checkbox angewählt ist, werden u. a. gerundete Ecken und dynamische Farben angezeigt.
- Einzelinstanzanwendung erstellen: Ein Mehrfachstart wird unterbunden.
- My.Settings beim Herunterfahren speichern: Beim Beenden der Anwendung wird automatisch My.Settings.Save() aufgerufen.
- Authentifizierungsmodus: Die Standardeinstellung Windows bewirkt, dass das Programm die Anmeldedaten des aktuellen Benutzers übernimmt. Bei der Option Anwendungsdefiniert müssen Sie den entsprechenden Code selbst schreiben.
- Modus für das Herunterfahren: Die Anwendung wird beim Schließen des Startformulars oder des letzten Formulars beendet.
Die durch den Dialog spezifizierten Ereignisse sind in der Datei Application.Designer.vb im Verzeichnis MyProject als reguläre Kommandos gespeichert. Der Button neben dem letzten Punkt öffnet ein Codefenster zur Festlegung anwendungsweiter Ereignisse (siehe Tabelle 11.2).
Ereignis | Auslösung |
Startup |
vor dem Erstellen des Startformulars |
Shutdown |
wenn die Anwendung nicht normal beendet wird |
UnhandledException |
bei unbehandelter Ausnahme |
StartupNextInstance |
Einzelinstanzanwendung wird ein zweites Mal gestartet |
NetworkAvailabilityChanged |
Herstellen oder Trennen der Netzwerkverbindung |
Vorlagen zur Implementierung erhalten Sie über die Listboxen am oberen Rand:
Abbildung 11.6 Anwendungsereignisse
11.3.2 Automatisch generierter Startcode 

Bei der Durchsicht der Dateien im Projektverzeichnis fällt auf, dass an keiner Stelle eine Methode Main() auftaucht. Das folgende Listing zeigt die vom Compiler automatisch in die ausführbare Datei eingebundene Methode.
<EditorBrowsable(EditorBrowsableState.Never), _
GeneratedCode("MyTemplate", "8.0.0.0")> _
Friend Class MyApplication : Inherits WindowsFormsApplicationBase
<DebuggerStepThrough> Public Sub New() ...
<STAThread, EditorBrowsable(EditorBrowsableState.Advanced), _
DebuggerHidden> _
Friend Shared Sub Main(ByVal Args As String())
Try
Application.SetCompatibleTextRenderingDefault( _
WindowsFormsApplicationBase.UseCompatibleTextRendering)
End Try
MyProject.Application.Run(Args)
End Sub
<DebuggerStepThrough> Protected Overrides Sub OnCreateMainForm() ...
Private Shared __ENCList As List(Of WeakReference) = New List(Of WeakReference)
End Class
Ihre gesamte Programmlogik wird im Rahmen der Methode Run() ausgeführt. Das Feld Application ist vom Typ WindowsFormsApplicationBase und ruft implizit den Konstruktor der von Ihnen definierten Form auf.
Public Sub Run(ByVal commandLine As String())
MyBase.InternalCommandLine = New ReadOnlyCollection(Of String)(commandLine)
If Not IsSingleInstance Then
DoApplicationModel()
Else
...
End If
End Sub
Von hier geht es weiter zur Programmausführung in OnRun().
Private Sub DoApplicationModel()
Dim eventArgs As New StartupEventArgs(MyBase.CommandLineArgs)
If Not Debugger.IsAttached Then
Try
If OnInitialize(MyBase.CommandLineArgs) AndAlso OnStartup(eventArgs) Then
OnRun()
OnShutdown()
End If
Catch exception As Exception
If m_ProcessingUnhandledExceptionEvent Then Throw
If Not OnUnhandledException(New _
UnhandledExceptionEventArgs(True, exception)) Then Throw
End Try
ElseIf OnInitialize(MyBase.CommandLineArgs) AndAlso OnStartup(eventArgs) Then
OnRun()
OnShutdown()
End If
End Sub
Nach diesen Umwegen kommt es nun endlich zum eigentlichen Start der Anwendung in der Methode Run(). Sie startet die im folgenden Abschnitt beschriebene Nachrichtenschleife.
<EditorBrowsable(EditorBrowsableState.Advanced)> _
Protected Overridable Sub OnRun()
If MainForm Is Nothing Then
OnCreateMainForm()
If MainForm Is Nothing Then Throw New NoStartupFormException()
AddHandler MainForm.Load, New EventHandler(AddressOf MainFormLoadingDone)
End If
Try
Application.Run(m_AppContext)
Finally
If Not m_NetworkObject Is Nothing Then
m_NetworkObject.DisconnectListener()
End If
If Not m_FirstInstanceSemaphore Is Nothing Then
m_FirstInstanceSemaphore.Close()
m_FirstInstanceSemaphore = Nothing
End If
AsyncOperationManager.SynchronizationContext = m_AppSyncronizationContext
m_AppSyncronizationContext = Nothing
End Try
End Sub
Hinweis |
Nach Deselektion des Anwendungsframeworks (siehe Abbildung 11.5) können Sie eine eigene Main-Methode als »Startformular« verwenden. |
<STAThread()> Public Shared Sub Main()
Application.Run(New TextboxKopieren())
End Sub
Hinweis |
Die automatisch hinzugefügten Debugger- und Editor-Attribute erschweren die Analyse des automatischen Startverhaltens im Debugger. |
11.3.3 Nachrichtenschleife 

Ein grundlegender Unterschied zwischen einer Konsolenanwendung und einer Windows-Anwendung ist die sogenannte Nachrichtenschleife. Unter dem Begriff Nachricht ist ein Ereignis zu verstehen. Windows-Programme reagieren auf Ereignisse, und eine Nachrichtenschleife wartet ununterbrochen auf eingehende Ereignisse. Ob es sich um Ereignisse der Maus, der Tastatur oder um Ereignisse von Peripheriegeräten wie beispielsweise dem Drucker handelt, spielt keine Rolle.
Die statische Methode Run der Klasse Application startet die Nachrichtenschleife. Als Argument wird Run eine neue Instanz des Formulars oder ein Applikationskontext übergeben. Der automatisch generierte Code wählt die zweite Möglichkeit und erzeugt ein Formular vorher an einer anderen Stelle. Wenn Sie die Main-Methode selbst kodieren, werden Sie meist ein Formular übergeben. Beim Aufruf von Run passieren zwei Dinge:
- Es wird eine Nachrichtenschleife gestartet.
- Dasjenige Form-Objekt wird angezeigt, das als Hauptformular festgelegt wurde.
Jeder Start einer Anwendung hat zur Folge, dass ein neuer Prozess gestartet wird, der mindestens einen Thread hat, in dem sich die Vorgänge der Anwendung abspielen. Eine Nachrichtenschleife ist immer an einen bestimmten Thread gebunden. Durch den Aufruf von Run wird die Nachrichtenschleife an den Primärthread der Anwendung und darüber hinaus auch an die Form gebunden. Schließen Sie das Form-Objekt, wird auch die Anwendung beendet. Es handelt sich hierbei gewissermaßen um das Hauptfenster der Anwendung.
Werden aus dem Hauptfenster heraus weitere Fenster geöffnet und wird das Hauptfenster anschließend geschlossen, wird auch die Laufzeit der Anwendung beendet. Das hat zur Folge, dass automatisch alle anderen noch geöffneten Fenster gleichzeitig geschlossen werden.
Application.Run ist überladen. Sie können auch die parameterlose Variante einsetzen und haben damit erreicht, dass die Lebensdauer der Nachrichtenschleife nicht mehr an die Existenz der Startform gebunden ist.
Dim frm As New TextboxKopieren()
frm.Show()
Application.Run()
Jetzt verhält sich die Anwendung ganz anders, denn mit dem Schließen der Form wird die Anwendung nicht beendet. Sie sehen die Form zwar nicht mehr, aber die Nachrichtenschleife lebt weiter und wartet auf Ereignisse, die nicht mehr kommen können. Im ersten Moment scheint dieses Verhalten ein Handicap zu sein, dennoch ist auch die parameterlose Run-Methode von Bedeutung. Wir werden darauf noch zu sprechen kommen, wenn wir uns mit Windows-Anwendungen beschäftigen, die mehrere Fenster haben.
11.3.4 Dateien *.vb und *.Designer.vb 

Partielle Klassen erlauben es, den Quellcode auf zwei oder mehr Quellcodedateien aufzuteilen. Damit wird die gleichzeitige Bearbeitung derselben Klasse durch mehrere Entwickler möglich. Bei der Implementierung einer Form-Klasse wird eine partielle Klassedefinition bereitgestellt. In unserer ersten Windows-Anwendung ist der Code auf die Dateien TextboxKopieren.vb und TextboxKopieren.Designer.vb aufgeteilt.
Wenn Sie eine Windows-Anwendung entwickeln, wird vom Visual Studio umfangreicher Code automatisch generiert. In erster Linie wird das Layout der Form und der darin enthaltenen Steuerelemente festgelegt. Der Code steht in der Methode InitializeComponent der Datei TextboxKopieren.Designer.vb (hier leicht umformatiert, um das Listing kurz zu halten):
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class TextboxKopieren
Inherits System.Windows.Forms.Form
'... überschreibt den Löschvorgang, um die Komponentenliste zu bereinigen.
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
...
End Sub
'Wird vom Windows Form-Designer benötigt.
Private components As System.ComponentModel.IContainer
'... Die folgende Prozedur ist für den Windows Form-Designer erforderlich.
'Das Bearbeiten ist mit dem Windows Form-Designer möglich.
'Das Bearbeiten mit dem Code-Editor ist nicht möglich.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.btnKopieren = New System.Windows.Forms.Button
Me.btnBeenden = New System.Windows.Forms.Button
Me.txtOriginal = New System.Windows.Forms.TextBox
Me.txtKopie = New System.Windows.Forms.TextBox
Me.SuspendLayout()
'
'btnKopieren
'
Me.btnKopieren.Location = New System.Drawing.Point(168, 16)
Me.btnKopieren.Name = "btnKopieren"
Me.btnKopieren.Size = New System.Drawing.Size(128, 23)
Me.btnKopieren.TabIndex = 2
Me.btnKopieren.Text = "Kopieren"
Me.btnKopieren.UseVisualStyleBackColor = True
'
'btnBeenden
...
'txtOriginal
'
Me.txtOriginal.Location = New System.Drawing.Point(16, 16)
Me.txtOriginal.Name = "txtOriginal"
Me.txtOriginal.Size = New System.Drawing.Size(128, 20)
Me.txtOriginal.TabIndex = 0
'
'txtKopie
...
'TextboxKopieren
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(317, 93)
Me.Controls.Add(Me.txtKopie)
Me.Controls.Add(Me.txtOriginal)
Me.Controls.Add(Me.btnBeenden)
Me.Controls.Add(Me.btnKopieren)
Me.Name = "TextboxKopieren"
Me.Text = "Meine erste WinAnwendung"
Me.ResumeLayout(False)
Me.PerformLayout()
End Sub
Friend WithEvents btnKopieren As System.Windows.Forms.Button
Friend WithEvents btnBeenden As System.Windows.Forms.Button
Friend WithEvents txtOriginal As System.Windows.Forms.TextBox
Friend WithEvents txtKopie As System.Windows.Forms.TextBox
End Class
In erster Linie handelt es sich um die Eigenschaftseinstellungen der beteiligten Komponenten, die von der Standardvorgabe des jeweiligen Typs abweichen, also objektspezifisch sind. Es sind die Einstellungen, die Sie im Eigenschaftsfenster der Komponente vornehmen und die aus der Lage und Größe der Komponente im Designer resultieren. Die Entwicklungsumgebung setzt das selbstständig in den dazu passenden Code um.
Beachten Sie auch bitte unbedingt den XML-Kommentar zu InitializeComponent. Aus eigener Erfahrung kann ich Ihnen nahelegen, diesen zu beherzigen und wirklich keine Änderungen am Code in InitializeComponent vorzunehmen. Ich kann davon berichten, dass ich meine Ignoranz mit dem Verlust sämtlicher Steuerelemente in einer Form bezahlen musste.
Neben der Initialisierungsmethode werden auf Klassenebene auch alle Steuerelemente standardmäßig als Friend und WithEvents deklariert. Die Initialisierung der Objekte erfolgt innerhalb von InitializeComponent.
Kommen wir nun zum Code in der Datei TextboxKopieren.vb:
Public Class TextboxKopieren
Private Sub btnKopieren_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnKopieren.Click
txtKopie.Text = txtOriginal.Text
End Sub
Private Sub btnBeenden_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnBeenden.Click
Application.Exit()
End Sub
End Class
Hinweis |
Nicht alle Teile einer Klasse müssen mit Partial gekennzeichnet werden. Die Deklaration in der Designer-Datei ist hinreichend. |
In der Datei sind alle benutzerdefinierten Methoden implementiert, zu denen natürlich auch die Ereignishandler zu zählen sind. In unserem Beispiel handelt es sich um die Ereignishandler, die beim Klicken auf die beiden Schaltflächen ausgeführt werden sollen: btnKopieren_Click und btnBeenden_Click.
Der Vorteil partieller Klassen wird jetzt deutlich:
Der benutzerdefinierte Code ist von dem Code getrennt, den die Entwicklungsumgebung generiert. Dadurch wird der Entwickler nicht so schnell der Verlockung nachgeben, eventuell doch Änderungen in InitializeComponent durchzuführen.
Der wesentliche Teil des Programmcodes, nämlich der benutzerdefinierte, wird überschaubarer, was insbesondere dem Programmieranfänger den Einstieg erleichtert.
Die hier gezeigten Methoden sind alle Visual Basic-Dateien, die die Anwendung bilden. Damit stellt sich die Frage: Wer ruft die Methode InitializeComponent auf? Die Antwort gibt das Attribut DesignerGenerated der Klasse in der Datei TextboxKopieren.Designer.vb. Wie bereits in Abschnitt 4.8.2, »Codeerzeugung: DesignerGenerated«, dargestellt wurde, wird der Code zum Aufrufen automatisch vom Compiler erzeugt. Meiner Meinung nach ist der Vorteil knapperen Codes kleiner als der Nachteil, der sich dadurch ergibt, dass die Methodenaufrufe schwerer nachzuvollziehen sind.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.