Steuerelementfelder in VB2005
von Philipp Stephani
Einleitung
Der Designer aus Visual Basic 6 bietet die Möglichkeit des bequemen Zusammenfassens mehrerer gleichartiger Steuerelemente zu Steuerelementfeldern über die Festlegung der Index-Eigenschaft. In Visual Basic 2005 scheint diese Funktionalität auf den ersten Blick nicht mehr vorhanden zu sein. Dies ist im unterschiedlichen Modell für die Verwaltung von Steuerelementen begründet: Während unter Visual Basic 6 Steuerelemente besondere Sprachbestandteile sind, die unabhängig vom Programmcode in Formulardateien gespeichert werden, handelt es sich in der .NET-Technologie dabei um ganz normale Objekte, die mit Quellcodeanweisungen erstellt und verwaltet werden. Für mit dem Visual Studio-Designer aufs Formular gezeichnete Steuerelemente wird der zugehörige Code in einer auf .Designer.vb endenden Datei automatisch erstellt, es ist jedoch ohne Probleme möglich und üblich, selbst Steuerelementobjekte zu erstellen. In diesem Tutorial soll schrittweise erläutert werden, wie Steuerelemente am einfachsten erstellt und in Containern gespeichert werden können.
Erstellen des Projekts
In diesem Artikel wird ein einfaches Projekt mit einem einzelnen Formular verwendet, welches beispielsweise durch Auswählen der Vorlage Windows-Anwendung im Dialogfeld Neues Projekt erstellt werden kann. Sämtlicher dargestellter Quelltext gehört zum Code des Formulars. Es werden Klassen aus den Namensräumen System, System.Collections.Generic, System.Drawing und System.Windows.Forms verwendet, die hier als importiert angenommen werden.
Erstellen von einzelnen Steuerelementen
Im ersten Schritt wird zunächst eine einzelne Schaltfläche erzeugt und dem Formular hingefügt. Der Code kann an jeder Stelle eingefügt werden, beispielsweise im Ereignisbehandlungsroutine des Load-Ereignisses des Formulars.
Dim NewButton As New Button NewButton.Name = "NewButton" NewButton.Text = "&Klick mich!" NewButton.Location = New Point(50, 50) NewButton.Size = New Size(200, 40) Controls.Add(NewButton)
Listing 1
Was geschieht hier? In der ersten Zeile wird eine neue Schaltfläche, d.h. ein Objekt der Klasse System.Windows.Forms.Button, erzeugt und der lokalen Variablen NewButton zugewiesen. In den vier folgenden Zeilen werden vier essentielle Eigenschaften gesetzt. Dies ist zwar zum fehlerlosen Ausführen des Codes nicht nötig, aber sehr sinnvoll, da die erstellte Schaltfläche in den allermeisten Fällen einen Beschriftungstext, eine Position auf dem Formular und eine Größe haben soll. Ohne die Festlegung der Text-Eigenschaft ist keine Beschriftung vorhanden; wird auf die Festlegung der Location- und Size-Eigenschaften verzichtet, so zeigt Visual Basic die neu erstellte Schaltfläche in der linken oberen Ecke mit einer Standardgröße an. Statt der Festlegung von Location und Size können natürlich auch andere Möglichkeiten zur Festlegung der Steuerelementbegrenzungen, z.B. die SetBounds-Methode, verwendet werden. Die Name-Eigenschaft wird festgelegt, damit auf das Steuerelement mit der Controls- Auflistungsobjektes zugegriffen werden kann. Mit der letzten Zeile wird die Schaltfläche dem Formular hinzugefügt. Soll sie nicht zum Formular, sondern zu einem Containersteuerelement, z.B. einem Panel, gehören, so ist die Add- Methode des Controls-Objekts des entsprechenden Containers zu benutzen, beispielsweise Panel1.Controls.Add.Behandlung von Ereignissen
Bis hierhin ist die erzeugte Schaltfläche noch nicht besonders sinnvoll, da sie nicht auf Benutzereingaben reagieren kann. Es muss deshalb Code für die Behandlung von Ereignissen hinzugefügt werden. Dies ist nicht so bequem wie bei im Designer gezeichneten Steuerelementen, wo beispielsweise bei Schaltflächen eine Ereignisbehandlungsroutine für das Click-Ereignis durch einen schlichten Doppelklick auf das Steuerelement eingefügt wird. Stattdessen wird bei der manuellen Erstellung von Steuerelementen die Registrierung einer Ereignisbehandlungsroutine unter Zuhilfenahme einer AddHandler-Anweisung bewerkstelligt:
Dim NewButton As New Button NewButton.Name = "NewButton" NewButton.Text = "&Klick mich!" NewButton.Location = New Point(50, 50) NewButton.Size = New Size(200, 40) AddHandler NewButton.Click, AddressOf ButtonClick Controls.Add(NewButton)
Listing 2
Die hier benutze Funktion ButtonClick muss natürlich ebenfalls definiert werden, und zwar mit der durch das fragliche Ereignis vorgegebenen Signatur:Private Sub ButtonClick(sender As Object, e As EventArgs) MessageBox.Show("Hello from " & CType(sender, Button).Name) End Sub
Listing 3
Nach einem Klick auf die Schaltfläche springt ein Nachrichtendialogfeld auf, das eine Nachricht sowie den Namen der angeklickten Schaltfläche anzeigt, womit auch illustriert wird, wie auf den Auslöser des Ereignisses zugegriffen werden kann: Das Argument sender, welches nach der .NET-Ereignisbehandlungskonvention einen Verweis auf das das Ereignis auslösende Objekt darstellt, muss mit CType in den benötigten Zieltyp konvertiert werden. Das funktioniert natürlich nur, wenn der Typ des auslösenden Steuerelements in den Zieltyp konvertiert werden kann, andernfalls schlägt die Umwandlung mit einer Ausnahme fehl. Wird eine einzige Ereignisbehandlungsroutine für mehrere unterschiedliche Steuerelemente benutzt, sollte deshalb mit dem TypeOf-Operator oder einer ähnlichen Technik zunächst der dynamische Typ des sender-Parameters überprüft werden.Verwendung einer Klassenvariablen
Bis jetzt wurde eine lokale Variable zum Referenzieren des Steuerelements benutzt. Oft bietet es sich jedoch an, eine Klassenvariable dafür zu verwenden, um auch außerhalb der erstellenden Prozedur einfach (d.h. ohne Verwendung der Controls-Auflistung) auf das Steuerelement zugreifen zu können. Hierbei bieten sich mehrere Varianten an:
- Private NewButton As Button:
Hierbei wird nur die Variable deklariert. Zuweisung und Ereignisbehandlung müssen im Code geschehen. - Private NewButton As New Button:
Das Objekt wird bei Erstellung des Formulars instanziert, sodass dies später nicht mehr nötig ist. - Private WithEvents NewButton As Button:
Die Ereignisbehandlung muss nicht durch AddHandler, sondern kann auch durch die einfachere, aber auch unflexiblere Handles-Klausel geschehen: Private Sub ButtonClick(sender As Object, e As EventArgs) Handles NewButton.Click
Steuerelementfelder
Wie durch diese Ausführungen erkannt werden konnte, läuft das Erstellen von Steuerelementen in .NET wesentlich anders und vor allem deutlich flexibler als in früheren Versionen von Visual Basic ab. Wie in allen anderen Punkten, so müssen auch hier die bisherigen Programmierkonzepte gründlich überdacht werden, da die zwei Sprachen auf unterschiedlichen Prinzipien basieren. Eine Eins-zu-eins-Umsetzung von Steuerelementfeldern in Visual Basic 2005 ist deshalb nicht möglich. Der größte Unterschied für den Programmierer besteht darin, dass es keinerlei Unterstützung des Designers für Steuerelementfelder mehr gibt. Auch die Positionierung von Steuerelementen muss deshalb von Hand erfolgen. Die Frage, wie eine Anzahl gleichartiger Steuerelemente erstellt und verwaltet werden, muss außerdem für jeden Fall neu beantwortet werden. Im Folgenden sollen nun einige typische Fälle besprochen werden.
- Mehrere gleichartige Steuerelemente, die weder Benutzerinteraktion noch späteren Zugriff benötigen, beispielsweise Bezeichnungsfelder, sollen erstellt werden: Hier genügt die einfache Erstellung und Hinzufügung von Steuerelementobjekten wie im ersten Beispiel, meist sogar ohne Zuweisung der Name-Eigenschaft, da kein späterer Zugriff mehr nötig ist.
- Steuerelemente, auf die nach der Erstellung noch zugegriffen werden muss: Dafür sind die im Framework vorhandenen Containerklassen in den Namensräumen System.Collections.Generic und System.Collections gut geeignet. Sie erlauben sehr flexible Verwaltung, z.B. Hinzufügen und Entfernen von Steuerelementen zu beliebigen Zeitpunkten.
- Steuerelemente, die auf Benutzerinteraktionen reagieren müssen: Da die Handles-Klausel nur bei einzelnen Objekten, nicht aber bei Containerklassen funktioniert, muss hier auf die oben beschriebene AddHandler-Anweisung zurückgegriffen werden.
Imports System Imports System.Collections.Generic Imports System.Windows.Forms Public Class Beispiel Private ReadOnly mEingabefelder As New LinkedList(Of TextBox) Private Sub Hinzufügen(ByVal sender As Object, ByVal e As EventArgs) Handles btnHinzufügen.Click Static Position As Integer = 40 Position += 40 Dim Eingabefeld As New TextBox Eingabefeld.SetBounds(20, Position, 100, 30) Eingabefeld.Text = "0" Eingabefeld.TextAlign = HorizontalAlignment.Right AddHandler Eingabefeld.TextChanged, AddressOf Summieren mEingabefelder.AddLast(Eingabefeld) Controls.Add(Eingabefeld) Dim Schaltfläche As New Button Schaltfläche.SetBounds(140, Position, 100, 30) Schaltfläche.Text = "Entfernen" Schaltfläche.Tag = Eingabefeld AddHandler Schaltfläche.Click, AddressOf Entfernen Controls.Add(Schaltfläche) End Sub Private Sub Entfernen(ByVal sender As Object, ByVal e As EventArgs) Dim Schaltfläche As Button = DirectCast(sender, Button) Dim Eingabefeld As TextBox = DirectCast(Schaltfläche.Tag, TextBox) Controls.Remove(Eingabefeld) Controls.Remove(Schaltfläche) mEingabefelder.Remove(Eingabefeld) Summieren(sender, e) End Sub Private Sub Summieren(ByVal sender As Object, ByVal e As EventArgs) Dim Summe As Double = 0 For Each Eingabefeld As TextBox In mEingabefelder Dim Zahl As Double = 0 If Double.TryParse(Eingabefeld.Text, Zahl) Then Summe += Zahl Next txtSumme.Text = Summe.ToString() End Sub End Class
Listing 4: Komplettes Beispiel
In diesem sehr einfachen Beispiel werden neu erzeugte Steuerelementgruppen einfach immer weiter unten angehängt; beim Entfernen bleiben die sich darunter befindlichen Gruppen an ihrem Platz, wodurch Lücken entstehen. Bei ernsthaften Anwendungen würde dies selbstverständlich anders geregelt werden, was aber der Funktionalität des Beispiels keinen Abbruch tut. Statt der verketteten Liste können natürlich, je nach Anwendung, andere Containerklassen eingesetzt werden.Fazit
Es ist mir hoffentlich gelungen, in diesem kurzen Artikel die grundlegenden Ideen und Vorgehensweisen zum Erstellen und Verwalten dynamisch generierter Steuerelemente herauszuarbeiten. Die aus Visual Basic 6 bekannten Steuerelementfelder existieren nicht mehr und müssen durch neue, objektorientierte Denkweisen ersetzt werden, was einerseits die Abkehr von bekannten Vorgehensweisen erzwingt, andererseits neue Möglichkeiten eröffnet.
Ihre Meinung
Falls Sie Fragen zu diesem Tutorial haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.