Code zu Steuerelementen in Classic Visual Basic
- Manipulieren des Eingabefeldes von ComboBox-Steuerelementen
- Ändern von Fensterstilen
- Verwenden eines ScrollBar-Steuerelements als Größenänderungsdreieck
- Ausrichten der Beschriftung von Steuerelementen
- Arbeiten mit der
Controls
-Auflistung - Definieren aktiver Regionen innerhalb eines Bildes
- Beschränken der Eingabe eines Textfeldes auf Zahlen
- Markieren des Inhalts eines Textfeldes bei Fokuserhalt
- Anwenden von Standardoperationen auf ein TextBox-Steuerelement
- Unterbinden des Pieptons bei Drücken der Eingabetaste in einem TextBox-Steuerelement
- Schnelles Füllen von ListBox- und ComboBox-Steuerelementen
- Aktivieren von Steuerelementen ohne Laufzeitfehler
- Entfernen des Rahmens eines WebBrowser-Steuerelements
- Entfernen der Bildlaufleisten eines WebBrowser-Steuerelements
- Entfernen des Kontextmenüs eines WebBrowser-Steuerelements
- Die Verlaufsliste des WebBrowser-Steuerelements
- Bestimmen der ausgewählten Option in einem Array von Optionsfeldern
- Anzeigen von Laufwerken, Ordnern und Dateien in einem ListBox-Steuerelement
- Suchen eines Eintrags in einem ListBox-Steuerelement
- Mehrzeilige Textfelder und Standardschaltflächen
- Drehen einer Linie um einen Punkt
- Kopieren des Textes des aktiven Steuerelements in die Zwischenablage
- Erstellen von 3D-Trennlinien
- Anpassen der Höhe eines Label-Steuerelements an den darzustellenden Text
- Unterscheiden zwischen „normaler“ Eingabetaste und jener am Ziffernblock
- Entfernen von Einträgen aus einem DriveListBox-Steuerelement
- Setzen von Tabulatoren in einem TextBox-Steuerelement
Manipulieren des Eingabefeldes von ComboBox-Steuerelementen
Das ComboBox-Steuerelement unter Visual Basic bietet keine Möglichkeit, die Länge des eingegebenen Textes zu begrenzen, das Textfeld für Eingaben zu sperren oder den eingegebenen Text mit einem Platzhalterzeichen unlesbar zu machen. Über die API-Funktion SendMessage
kann diese Funktionalität nachgerüstet werden:
Private Declare Function GetWindow _
Lib "user32.dll" _
( _
ByVal hWnd As Long, _
ByVal wCmd As Long _
) As Long
Private Declare Function SendMessage _
Lib "user32.dll" _
Alias "SendMessageA" _
( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long _
) As Long
Private Const EM_LIMITTEXT As Long = &HC5&
Private Const EM_SETPASSWORDCHAR As Long = &HCC&
Private Const EM_SETREADONLY As Long = &HCF&
Private Const CB_LIMITTEXT As Long = &H141&
Private Const GW_CHILD As Long = 5&
Private Sub ComboBoxSetReadOnly( _
ByVal ComboBox As ComboBox, _
ByVal ReadOnly As Boolean _
)
Call SendMessage( _
GetWindow(ComboBox.hWnd, GW_CHILD), _
EM_SETREADONLY, _
IIf(ReadOnly, 1&, 0&), _
0& _
)
Call ComboBox.Refresh
End Sub
Private Sub ComboBoxSetPasswordChar( _
ByVal ComboBox As ComboBox, _
ByVal PasswordChar As String _
)
If Len(PasswordChar) = 0 Then
PasswordChar = vbNullChar
ElseIf Len(PasswordChar) > 1 Then
PasswordChar = Left(PasswordChar, 1)
End With
Call SendMessage( _
GetWindow(ComboBox.hWnd, GW_CHILD), _
EM_SETPASSWORDCHAR, _
Asc(PasswordChar), _
0& _
)
Call ComboBox.Refresh
End Sub
Private Sub ComboBoxSetMaxLength( _
ByVal ComboBox As ComboBox, _
ByVal Length As Long _
)
' Folgende Aufrufe bewirken dasselbe, beim auskommentierten Aufruf wird
' die Zugriffsnummer des Textfeldes des ComboBox-Steuerelements
' ermittelt und die Textlänge des Textfeldes festgelegt.
Call SendMessage(ComboBox.hWnd, CB_LIMITTEXT, Length, 0&)
'Call SendMessage( _
' GetWindow(ComboBox.hWnd, GW_CHILD), _
' EM_LIMITTEXT, _
' Length, _
' 0& _
')
Call ComboBox.Refresh
End Sub
Ändern von Fensterstilen
Visual Basic bietet keine Möglichkeit der direkten Manipulation der Fensterstile von Formularen und Steuerelementen. Die nachfolgend angegebene Prozedur SetStyleBits
vereinfacht das Setzen von normalen und erweiterten Fensterstilen:
Private Declare Function GetWindowLong _
Lib "user32.dll" _
Alias "GetWindowLongA" _
( _
ByVal hWnd As Long, _
ByVal nIndex As Long _
) As Long
Private Declare Function SetWindowLong _
Lib "user32.dll" _
Alias "SetWindowLongA" _
( _
ByVal hWnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long _
) As Long
Private Const GWL_EXSTYLE As Long = -20&
Private Const GWL_STYLE As Long = -16&
' Sets normal and extended style-bits for a control/form.
'
' Parameters:
'
' 'hWnd' - The control's handle.
' 'Apply' - 'True', if the style should be applied.
' 'False', when the style should be removed.
' 'IsExStyle' - Determines if the style is an extended style.
'
' Note that it's sometimes necessary to update the window by sending an
' update message or using 'UpdateWindow' to show the new style-bits.
Private Sub SetStyleBits( _
ByVal hWnd As Long, _
ByVal Style As Long, _
Optional ByVal Apply As Boolean = True, _
Optional ByVal IsExStyle As Boolean = False _
)
Dim OldStyle As Long, StyleID As Long
StyleID = IIf(IsExStyle, GWL_EXSTYLE, GWL_STYLE)
OldStyle = GetWindowLong(hWnd, StyleID)
If Apply Then
Call SetWindowLong(hWnd, StyleID, OldStyle Or Style)
Else
Call SetWindowLong(hWnd, StyleID, OldStyle And Not Style)
End If
End Sub
Verwenden eines ScrollBar-Steuerelements als Größenänderungsdreieck
Fenster, die in der Größe geändert werden können, besitzen vielfach ein Größenänderungsdreieck, das sich in der rechten unteren Ecke eines Fensters befindet. Es gibt verschiedene Möglichkeiten, ein solches Dreieck in mit Visual Basic erstellten Anwendungsfenstern zu erzeugen. Ein ausgefallener Weg bedient sich dazu einer Bildlaufleiste, die durch Manipulation ihrer Fensterstile und ihrer Eigenschaften in ein Größenänderungsdreieck verwandelt wird. Die Vorgehensweise funktioniert sowohl mit horizontalen als auch vertikalen Bildlaufleisten. Das folgende Beispiel setzt voraus, daß sich auf dem Formular eine Bildlaufleiste mit Namen HScroll1
befindet:
Private Declare Function SetWindowLong _
Lib "user32.dll" _
Alias "SetWindowLongA" _
( _
ByVal hWnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long _
) As Long
Private Declare Function GetWindowLong _
Lib "user32.dll" _
Alias "GetWindowLongA" _
( _
ByVal hWnd As Long, _
ByVal nIndex As Long _
) As Long
Private Const GWL_STYLE As Long = -16&
Private Sub Form_Load()
With HScroll1
Dim PrevScaleMode As ScaleModeConstants
PrevScaleMode = Me.ScaleMode
Me.ScaleMode = vbPixels
Const GrabberWidth As Integer = 16
Const GrabberHeight As Integer = GrabberWidth
Call .Move( _
Me.ScaleWidth - GrabberWidth, _
Me.ScaleHeight - GrabberHeight, _
GrabberWidth, _
GrabberHeight _
)
Me.ScaleMode = PrevScaleMode
.TabStop = False
.Enabled = False
.MousePointer = vbSizeNWSE
' Funktioniert nicht unter Windows NT 3.51.
Call SetWindowLong( _
.hWnd, _
GWL_STYLE, _
GetWindowLong(.hWnd, GWL_STYLE) Or &H10& _
)
' Steuerelement aktualisieren.
Call .Refresh
End With
End Sub
Private Sub Form_Resize()
With HScroll1
Call .Move(Me.ScaleWidth - .Width, Me.ScaleHeight - .Height)
End With
End Sub
Wurde das Aussehen erst einmal angepaßt, muß noch dafür gesorgt werden, daß das Dreieck immer in der rechten unteren Ecke des Fensters gehalten wird, wenn sich dieses in seiner Größe ändert. Dazu wird in der Behandlungsroutine des Resize
-Ereignisses des Formulars die Bildlaufleiste neu positioniert.
Ausrichten der Beschriftung von Steuerelementen
Einige Steuerelemente unterstützen direkt das Ändern der Ausrichtung ihrer Beschriftung mittels Eigenschaften. Manche Steuerelemente besitzen jedoch keine derartigen Eigenschaften. Für Steuerelemente, die sich von der Fensterklasse BUTTON
ableiten, wie etwa Schaltflächen, Rahmen, Auswahlfelder und Optionsfelder, kann über die im Folgenden vorgestellte Technik die Ausrichtung der Beschriftung festgelegt werden:
Private Declare Function SetWindowLong _
Lib "user32.dll" _
Alias "SetWindowLongA" _
( _
ByVal hWnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long _
) As Long
Private Declare Function GetWindowLong _
Lib "user32.dll" _
Alias "GetWindowLongA" _
( _
ByVal hWnd As Long, _
ByVal nIndex As Long _
) As Long
Private Const GWL_STYLE As Long = -16&
Private Const BS_TOP As Long = &H400&
Private Const BS_VCENTER As Long = &HC00&
Private Const BS_BOTTOM As Long = &H800&
Private Const BS_LEFT As Long = &H100&
Private Const BS_CENTER As Long = &H300&
Private Const BS_RIGTH As Long = &H200&
Private Enum AlignmentConstants
Top = BS_TOP
VerticalCenter = BS_VCENTER
Bottom = BS_BOTTOM
Left = BS_LEFT
Center = BS_CENTER
Right = BS_RIGTH
End Enum
Private Sub Form_Load()
' Beschriftung eines CommandButton-Steuerelements rechts ausrichten.
Call SetAlignment(Me.Command1, Right)
' Beschriftung eines Frame-Steuerelements zentrieren.
Call SetAlignment(Me.Frame1, Center)
' Beschriftung eines CheckBox-Steuerelements unten rechts ausrichten.
Call SetAlignment(Me.Check1, Bottom Or Right)
' Beschriftung eines OptionButton-Steuerelements links oben ausrichten.
Call SetAlignment(Me.Option1, Top)
End Sub
Private Sub SetAlignment( _
ByVal Control As Control, _
ByVal Alignment As AlignmentConstants _
)
Call SetWindowLong( _
Control.hWnd, _
GWL_STYLE, _
GetWindowLong(Control.hWnd, GWL_STYLE) Or Alignment _
)
Call Control.Refresh
End Sub
Arbeiten mit der Controls
-Auflistung
Die Steuerelemente eines Formulars können über dessen Controls
-Auflistung durchlaufen werden. Dabei ist es auch möglich, auf Eigenschaften eines Steuerelements, von dem nur der Name als Zeichenfolge bekannt ist, zuzugreifen bzw. diese zu manipulieren. Das folgende Beispiel ändert die Enabled
-Eigenschaften der Steuerelemente mit den Namen Command1
bis Command6
:
Dim i As Long
For i = 1 To 6
Me.Controls("Command" & CStr(i)).Enabled = False
Next i
Einzelne Elemente aus Steuerelementenfeldern können angesprochen werden, wie das folgende Beispiel zeigt. Hier wird immer das Element mit Index 2 deaktiviert:
Dim i As Long
For i = 1 To 6
Me.Controls("Command" & CStr(i))(2).Enabled = False
Next i
Das folgende Beispiel setzt die Text
-Eigenschaften aller Textfelder des Formulars auf die leere Zeichenfolge:
Dim ctr As Control
For Each ctr In FMain.Controls
If TypeOf ctl Is TextBox Then
ctr.Text = ""
End If
Next ctr
Es ist auch möglich, Steuerelemente zur Laufzeit in die Controls
-Auflistung aufzunehmen. Im folgenden Beispiel wird eine ereignissensitive Schaltfläche erzeugt:
Private WithEvents cmdNewButton As CommandButton
Private Sub cmdMakeControls_Click()
Set cmdNewButton = Me.Controls.Add("VB.CommandButton", "cmdNewButton", Me)
With cmdNewButton
.Width = 1800
.Height = 340
.Caption = "&Show Message"
.Top = 300
.Left = 300
.Visible = True
End With
End Sub
Private Sub cmdNewButton_Click()
Call MsgBox("Hello World!")
End Sub
Definieren aktiver Regionen innerhalb eines Bildes
In verspielten Oberflächen kommen oft Bilder zum Einsatz, bei denen der Benutzer auf bestimmte Stellen klicken kann, um eine Aktion auszulösen. Das ist auch einfach in Visual-Basic-Anwendungen realisierbar, indem auf einem PictureBox-Steuerelement, das die Grafik enthält, Label- oder Image-Steuerelemente in der Größe der aktiven Region plaziert werden. Die Eigenschaft BackStyle
muß auf transparent und die Eigenschaft BorderStyle
auf vbBSNone
festgelegt werden. Weiters sollten vorhandene Steuerelementbeschriftungen gelöscht werden. Nun können wie gewohnt die Ereignisse MouseMove
und Click
des Label-Steuerelements ausgewertet werden.
Beschränken der Eingabe eines Textfeldes auf Zahlen
Wenn in ein Textfeld nur Zahlen eingegeben werden sollen, dann muß dafür nicht unbedingt ein zusätzliches Steuerelement, wie etwa MaskedEdit, herangezogen werden. Diese Funktionalität läßt sich auch mit wenigen zusätzlichen Zeilen Quellcode realisieren. Die beiden vorgestellten Lösungen sind nicht immun gegen das Einfügen von beliebigem Text über die Zwischenablage.
Wichtig ist dabei das Ereignis KeyPress
des TextBox-Steuerelements. Es wird für ein Steuerelement ausgelöst, wenn eine Taste gedrückt wurde und es gerade den Fokus besitzt. Im Parameter KeyAscii
wird der Code des Zeichens übergeben, welcher der gedrückten Taste entspricht. Wird dieser Parameter in der Prozedur KeyPress
auf 0 gesetzt, so wird der Tastendruck ignoriert und das Zeichen erscheint nicht im Textfeld. Mit der Funktion InStr
kann die Eingabe auf beliebige Zeichen eingeschränkt werden:
Private Sub Text1_KeyPress(KeyAscii As Integer)
If InStr("1234567890-," & Chr$(vbKeyBack), Chr$(KeyAscii)) = 0 Then
KeyAscii = 0
End If
End Sub
In diesem Beispiel wurde auch noch Chr$(vbKeyBack)
an die Zeichenfolge der erlaubten Zeichen angehängt, damit mit der Löschtaste eingegebene Zeichen auch wieder gelöscht werden kann. Wenn die Umwandlung des Zeichens mit Chr$(KeyAscii)
zu umständlich oder zu langsam ist, kann stattdessen direkt den Zeichencode geprüft werden. Dies führt jedoch zu schwerer lesbarem Code (die Zahlen 0 bis 9 entsprechen den Zeichencodes 48 bis 57):
Private Sub Text1_KeyPress(KeyAscii As Integer)
' Allow the user to press Backspace. You can add other keys (for example
' plus minus and comma) here.
If KeyAscii = vbKeyBack Then Exit Sub
' ASCII value 48-57 = key 1-9.
If KeyAscii < 48 Or KeyAscii > 57 Then
KeyAscii = 0
End If
End Sub
Markieren des Inhalts eines Textfeldes bei Fokuserhalt
Um den gesamten Inhalt eines Textfeldes zu markieren, wenn dieses den Fokus erhält, können zwei Methoden verwendet werden. Dabei bezeichnet Text1
das TextBox-Steuerelement:
Private Sub Text1_GotFocus()
Me.Text1.SelStart = 0
Me.Text1.SelLength = Len(Me.Text1.Text)
End Sub
Private Sub Text1_GotFocus()
Call SendKeys("{home}+{end}")
End Sub
Anwenden von Standardoperationen auf ein TextBox-Steuerelement
Um Standardfunktionen wie Kopieren, Einfügen und Ausschneiden auf ein TextBox- oder RichTextBox-Steuerelement anzuwenden, bietet sich die generische Routine DoEdit
an:
Private Enum EditOperation
Copy
Cut
Paste
Undo
End Enum
'
' 'Control' must point to a textbox or richtextbox control.
'
Private Sub DoEdit( _
ByVal Control As Control, _
ByVal Operation As EditOperation _
)
Dim s As String
Select Case Operation
Case Copy: s = "^C"
Case Cut: s = "^X"
Case Paste: s = "^V"
Case Undo: s = "^Z"
End Select
Call Control.SetFocus
Call SendKeys(s)
End Sub
Unterbinden des Pieptons bei Drücken der Eingabetaste in einem TextBox-Steuerelement
Nach dem Drücken der Eingabetaste in einem Textfeld erfolgt kein Beep:
Private Sub Text1_KeyPress(KeyAscii As Integer)
' Wenn Eingabetaste gedrückt wurde.
If KeyAscii = vbKeyReturn Then
KeyAscii = 0 ' Piepton unterdrücken.
End If
End Sub
Schnelles Füllen von ListBox- und ComboBox-Steuerelementen
Die von Visual Basic bereitgestellten Methoden der ListBox-Klasse sind sehr langsam im Vergleich zu den entsprechenden Windows-API-Funktionen. Damit folgendes Beispiel funktioniert wird ein Formular mit einer Schaltfläche cmdFillList
und eine ListBox List1
benötigt. Damit das Beispiel auch mit Kombinationsfeldern funktioniert, muß anstelle von LB_ADDSTRING
die Konstante CB_ADDSTRING = &H143&
verwendet werden. In diesem Beispiel wird außerdem zuerst ein Informationstext der ListBox hinzugefügt, die Aktualisierung der ListBox unterbunden, die Elemente hinzugefügt, das Informationselement entfernt und die Fensteraktualisierung wieder aktiviert:
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" ( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As String _
) As Long
Private Declare Function LockWindowUpdate Lib "user32.dll" ( _
ByVal hWnd As Long _
) As Long
Private Const LB_ADDSTRING As Long = &H180&
Private Sub LockWindow(ByVal hWnd As Long)
Call LockWindowUpdate(hWnd)
End Sub
Private Sub UnlockWindow()
Call LockWindowUpdate(0&)
End Sub
Private Sub cmdFillList_Click()
Me.cmdFillList.Enabled = False
Screen.MousePointer = vbHourglass
Call Me.List1.Clear
Me.List1.Enabled = False
Call Me.List1.AddItem("Lade Liste…")
Call Me.List1.Refresh
Dim hWndListBox As Long
hWndListBox= Me.List1.hWnd
Call LockWindow(hWndListBox)
Dim i As Long
For i = 1 To 32000
Call SendMessage( _
hWndListBox, _
LB_ADDSTRING, _
0&, _
"Item " & CStr(i) & vbNullChar _
)
If i Mod 10 = 0 Then
DoEvents
End If
Next i
Call Me.List1.RemoveItem(0)
Me.List1.Enabled = True
Call UnlockWindow
Screen.MousePointer = vbNormal
Me.cmdFillList.Enabled = True
End Sub
Aktivieren von Steuerelementen ohne Laufzeitfehler
Die SetFocus
-Methode der Steuerelemente unter Visual Basic hat einen großen Nachteil: Ist beispielsweise das Steuerelement, dessen Methode SetFocus
aufgerufen wurde, nicht sichtbar oder deaktiviert, so löst der Aufruf über Control1.SetFocus
(Control1
bezeichnet hier ein Steuerelement) einen Laufzeitfehler aus. Dies kann umgangen werden, indem der Fokus über die API-Funktion SetFocus
gesetzt wird. Der Rückgabewert dieser Funktion gibt Auskunft über den Erfolg, der aber normalerweise uninteressant ist:
Private Declare Function SetFocusAPI Lib "user32.dll" Alias "SetFocus" ( _
ByVal hWnd As Long _
) As Long
Um den Fokus auf Control1
zu setzen, wird dann folgender Code verwendet:
Call SetFocusAPI(Me.Control1.hWnd)
Entfernen des Rahmens eines WebBrowser-Steuerelements
Das WebBrowser-Steuerelement hat standardmäßig eine Clientkante als Rahmen, aber es besitzt keine Eigenschaft BorderStyle
, um diesen Rahmen zu entfernen oder zu ändern. Dies ist besonders dann notwendig, wenn die Formularoberfläche eine HTML-Seite sein soll. Dabei sollte der Übergang zwischen HTML-Dokument und Formular möglichst unsichtbar sein.
Die intuitive Lösung zum Entfernen des Rahmens ist denkbar einfach, allerdings nicht sehr professionell: Man erstellt vier rahmenlose Frame-Steuerelemente und plaziert sie so über dem WebBrowser-Steuerelement, daß dessen Rahmen überdeckt wird. Dazu sollte der Rahmen der Frames entfernt werden und die Hintergrundfarbe auf vbButtonFace
eingestellt werden.
Jedoch gibt es auch eine professionelle Lösung, den Rahmen vom Steuerelement weg zu bekommen. Der 3D-Rahmen sowie die horizontale Bildlaufleiste, die selbst dann angezeigt wird, wenn sie nicht benötigt wird, sind nämlich nicht Teil des Steuerelements sondern des darin angezeigten Dokument. Für eine Änderung des Rahmens ist es also erforderlich, daß bereits ein Dokument in das Steuerelement geladen wurde und über die Document
-Eigenschaft bereitsteht. Dies ist mit Eintritt des Ereignisses DocumentComplete
des WebBrowser-Steuerelements gegeben. In diesem Ereignis kann daher die Eigenschaft border
des Document
-Objekts, die sich über Document.body.style.border
erreichen läßt, ändern:
Private Sub WebBrowser1_DocumentComplete( _
ByVal pDisp As Object, _
ByRef URL As Variant _
)
Me.WebBrowser1.Document.body.style.border = "none"
End Sub
Entfernen der Bildlaufleisten eines WebBrowser-Steuerelements
Die dauerhaft angezeigte Bildlaufleiste kann in gleicher Weise im DocumentComplete
-Ereignis beeinflußt werden: Soll sie nur dann angezeigt werden, wenn sie benötigt wird, um durch den Inhalt scrollen zu können, kann hierfür die Eigenschaft Document.body.scroll
auf "auto"
gesetzt werden; soll die Leiste hingegen nie angezeigt werden, dann kann dazu der Wert "no"
angegeben werden. Im folgenden Beispiel wird eingestellt, daß die Bildlaufleiste nur bei Bedarf sichtbar ist:
Private Sub WebBrowser1_DocumentComplete( _
ByVal pDisp As Object, _
ByRef URL As Variant _
)
Me.WebBrowser1.Document.body.scroll = "auto"
End Sub
Entfernen des Kontextmenüs eines WebBrowser-Steuerelements
Bei HTML-Oberflächen in Anwendungen ist es meist vorteilhaft, wenn der Benutzer keine Möglichkeit hat, Text und andere Dokumentelemente zu kopieren. Der Mauszeiger kann in der HTML-Datei auf default
gesetzt werden, weiters muß jedoch die Auswahl unterbunden werden. Dazu kann man den gesamten Inhalt der HTML-Datei in eine Tabelle schreiben, die folgendermaßen aussieht (wichtig dabei ist der nicht korrekt geschlossene Hyperlink!):
<a style="cursor: default" href="#">
<table>
<tr>
<td></a>Page body goes here!</td>
</tr>
</table>
Jetzt muß nur mehr das Kontextmenü des WebBrowser-Steuerelements entfernt werden. Dies ist über JavaScript möglich. Das öffnende body
-Element müßte folgendermaßen modifiziert werden:
<body onContextMenu="return false">
Befehle des WebBrowser-Steuerelements
Der Inhalt eines WebBrowser-Steuerelements kann gedruckt oder ausgewählt werden. Es gibt auch noch zahlreiche andere Funktionen, die über die Methode ExecWB
ausgeführt werden können. Der nachstehende Code zeigt, wie man den gesamten Inhalt eines WebBrowser-Steuerelements auswählt und wie man eine HTML-Datei damit ausdruckt:
' Alles auswählen.
Call Me.WebBrowser1.ExecWB(OLECMDID_SELECTALL, OLECMDEXECOPT_DODEFAULT)
' Ausdrucken.
Call Me.WebBrowser1.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DODEFAULT)
Die Verlaufsliste des WebBrowser-Steuerelements
Das WebBrowser-Steuerelement besitzt u. a. die Methode Navigate
, bei der im zweiten Parameter Flags angegeben werden können. Wird die Konstante navNoHistory
hier angegeben, fügt das WebBrowser-Steuerelement die Seite, zu der navigiert wird, nicht der Verlaufsliste hinzu. Bei einigen Programmierern führt dies zur großen Verwunderung, da die besuchte Seite trotzdem in der Verlaufsliste aufscheint.
Daher soll hier darauf hingewiesen werden, daß die Verlaufsliste nicht gleich der History ist: Die History ist eine interne Liste, die dem Webbrowser dazu dient, vorwärts und rückwärts navigieren zu können. Bei der Angabe der angeführten Konstanten wird die Seite lediglich nicht in den interne History aufgenommen, im Verlauf scheint sie aber trotzdem auf.
Bestimmen der ausgewählten Option in einem Array von Optionsfeldern
Folgende Funktion bestimmt die ausgewählte Option in einem Array von Optionsfeldern. Zu beachten ist dabei, daß das Array von Steuerelementen als Object
übergeben wird:
Private Function GetSelOption(ByVal OptionGroup As Object) As Integer
Dim Index As Integer
For Index = OptionGroup.LBound To OptionGroup.UBound
If OptionGroup(Index).Value Then
GetSelOption = Index
Exit Function
End If
Next Index
End Function
Ein Aufruf könnte folgendermaßen aussehen:
Debug.Print _
"Sie haben Option Nummer " & CStr(GetSelOption(Me.Option1)) & "gewählt."
Dabei ist Option1
ein Feld von Optionsfeldern.
Anzeigen von Laufwerken, Ordnern und Dateien in einem ListBox-Steuerelement
Das FileListBox-Steuerelement ist ziemlich langsam. Windows stellt für Listenfelder einen eigenen Darstellungsmodus zur Verfügung, bei dem Laufwerke, Ordner und Dateien wahlweise angezeigt werden können. In folgendem Beispiel bezeichnet List1
ein ListBox-Steuerelement:
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" ( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByRef lParam As String _
) As Long
Private Const LB_DIR As Long = &H18D& ' Dateianzeige.
' Dateien ohne spezielle Attribute anzeigen.
Private Const DDL_READWRITE As Long = &H0&
' Schreibgeschützte Dateien anzeigen.
Private Const DDL_READONLY As Long = &H1&
' Versteckte Dateien anzeigen.
Private Const DDL_HIDDEN As Long = &H2&
' Systemdateien anzeigen.
Private Const DDL_SYSTEM As Long = &H4&
' Ordner anzeigen.
Private Const DDL_DIRECTORY As Long = &H10&
' Archivierte Dateien anzeigen.
Private Const DDL_ARCHIVE As Long = &H20&
' Laufwerke anzeigen.
Private Const DDL_DRIVES As Long = &H4000&
' Nur Dateien mit dem gewünschten Attribut anzeigen, sonst werden Dateien ohne
' spezielle Attribute immer angezeigt.
Private Const DDL_EXCLUSIVE As Long = &H8000&
Der Einsatz wäre mit dem folgenden Code möglich:
'
' Die Konstanten werden nacheinander in separaten Aufrufen von 'SendMessage' gesendet,
' da sonst keine Sortierung nach Laufwerken, Ordnern und Dateien erfolgt.
'
' Laufwerke hinzufügen.
Call SendMessage( _
Me.List1.hWnd, _
LB_DIR, _
DDL_DRIVES Or DDL_EXCLUSIVE, _
ByVal "C:\*.*" _
)
' Ordner hinzufügen.
Call SendMessage( _
Me.List1.hWnd, _
LB_DIR, _
DDL_DIRECTORY Or DDL_EXCLUSIVE, _
ByVal "C:\*.*" _
)
' Dateien ohne spezielle Attribute hinzufügen.
Call SendMessage( _
Me.List1.hWnd, _
LB_DIR, _
0&, _
ByVal "C:\*.*" _
)
Suchen eines Eintrags in einem ListBox-Steuerelement
Um zu prüfen, ob sich ein Element bereits in einer ListBox befindet, muß man nicht alle Elemente über eine Schleife durchgehen und vergleichen, bis das Element gefunden oder das Ende der ListBox erreicht wurde. In folgendem Beispiel werden zwei Zeichenfolgen in der ListBox List1
gesucht:
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" ( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As String _
) As Long
Private Const LB_FINDSTRING As Long = &H18F&
Private Sub Form_Load()
With Me.List1
Call .AddItem("Karl")
Call .AddItem("Peter")
Call .AddItem("Frank")
Call .AddItem("Brian")
Call .AddItem("Adam")
End With
Debug.Print _
"Suche nach ""Brian"": " & _
CStr(SendMessage(Me.List1.hWnd, LB_FINDSTRING, -1&, "Brian"))
Debug.Print _
"Suche nach ""John"": " & _
CStr(SendMessage(Me.List1.hWnd, LB_FINDSTRING, -1&, "John"))
End Sub
Mehrzeilige Textfelder und Standardschaltflächen
Die Verwendung von Standardschaltflächen (Eigenschaft Default
) ist allgemein gebräuchlich. Ein Problem ergibt sich dabei, wenn auf einem Formular ein mehrzeiliges Textfeld (Eigenschaft MultiLine
) und eine Standardschaltfläche plaziert sind. In diesem Fall kann nämlich der Benutzer nicht durch Drücken der Eingabetaste einen Zeilenumbruch im Textfeld erzwingen, sondern es wird der Code hinter der Standardschaltfläche ausgeführt und kein Ereignis des Textfeldes.
Es gibt aber eine einfache Abhilfe, die sich des Windows-API bedient, mit dem dieses Problem gelöst werden kann. Im Folgenden bezeichne cmdOK
die Schaltfläche, deren Default
-Eigenschaft aktiviert ist, Text1
sei das mehrzeileige Textfeld:
Private Declare Function GetFocus Lib "user32.dll" ( _
) As Long
Private Sub cmdOK_Click()
If GetFocus = Me.Text1.hWnd Then
Me.Text1.SelText = vbNewLine
Else
Call MsgBox("Die 'Default'-Schaltfläche wurde gedrückt.")
End If
End Sub
Durch das Drücken der Eingabetaste wird nämlich nicht der Fokus auf ein anderes Steuerelement gesetzt, sondern bleibt dem Textfeld erhalten. Es muß also lediglich getestet werden, ob die TextBox immer noch den Fokus besitzt, ist dies der Fall, dann wurde die Eingabetaste in der TextBox gedrückt und es wird ein Zeilenumbruch an der aktuellen Position eingefügt.
Drehen einer Linie um einen Punkt
Nachstehender Code erfordert ein Formular, auf dem ein Timer-Steuerelement mit dem Namen Timer1
und ein Line-Steuerelement Line1
plaziert ist. Es wird eine Linie um einen Punkt gedreht, wie bei einem Zeiger einer analogen Uhr:
Private Const Radius As Long = 400&
Private Const StartX As Long = 1000&
Private Const StartY As Long = 1000&
Private Const PI As Double = 3.1415
Private Sub Form_Load()
With Me.Timer1
.Interval = 50
.Enabled = True
End With
End Sub
Private Sub Timer1_Timer()
Static m_Angle As Double
If m_Angle > 2 * PI Then
m_Angle = m_Angle Mod PI
End If
m_Angle = m_Angle + PI / 40
' Drehpunkt der Linie.
Me.Line1.X1 = StartX
Me.Line1.Y1 = StartY
' Bewegung des Punktes am Kreis.
If m_Angle < PI Then
Me.Line1.Y2 = StartY + Abs(Radius * Sin(m_Angle))
Me.Line1.X2 = StartX + Radius * Cos(m_Angle)
Else
Me.Line1.Y2 = StartY - Abs(Radius * Sin(m_Angle))
Me.Line1.X2 = StartX + Radius * Cos(m_Angle)
End If
End Sub
Kopieren des Textes des aktiven Steuerelements in die Zwischenablage
Ab und zu ist es sinnvoll, im Programm die Möglichkeit anzubieten, die Auswahl im aktuellen Steuerelement in die Zwischenablage zu kopieren. Zu diesem Zweck kann der folgende Code verwendet werden:
Private Sub mnuEditCopy_Click()
If TypeOf Screen.ActiveControl Is TextBox Then
Call Clipboard.Clear
Call Clipboard.SetText(Screen.ActiveControl.Text)
ElseIf … Then
⋮
End If
End Sub
Je nach Typ des aktiven Steuerelements kann ein anderer Inhalt in die TextBox abgelegt werden. Man kann auch überprüfen, ob das aktive Steuerelement gleich einem bestimmten Steuerelement ist. Dazu muß man einfach Screen.ActiveControl
über den Operator Is
mit dem gewünschten Steuerelement vergleichen.
Erstellen von 3D-Trennlinien
Unter Windows verwenden viele Anwendungen dreidimensionale Trennlinien, um die Formulare in zusammengehörende Bereiche zu unterteilen. Eine solche Linie kann man entweder durch Zusammensetzen zweier Line-Steuerelemente erzielen oder man schreibt dafür ein eigenes Benutzersteuerelement. Folgender Code muß in ein Benutzersteuerelement, dessen Windowless
-Eigenschaft aktiviert ist, eingefügt werden:
Option Explicit
Private Sub UserControl_Paint()
UserControl.Line _
(0, 0)-(UserControl.Width, UserControl.Height), _
vb3DHighlight, _
BF
UserControl.Line _
(0, 0)-(UserControl.Width - 1 * Screen.TwipsPerPixelX, 0), _
vb3DShadow
End Sub
Private Sub UserControl_Resize()
UserControl.Height = 2 * Screen.TwipsPerPixelY
Call UserControl.Refresh
End Sub
Im Resize
-Ereignis wird sichergestellt, daß das Steuerelement nicht mehr als zwei Pixel hoch gezogen wird. Bei einer Größenänderung wird die Oberfläche neu gezeichnet. Jetzt muß man nur noch die Linien auf den Formularen aufziehen. Wenn man auch vertikale Linien zeichnen will, dann muß man den Code entsprechend anpassen, indem man beispielsweise eine Eigenschaft Orientation
hinzufügt, bei der der Benutzer angeben kann, ob es sich um eine horizontale oder vertikale Trennlinie handeln soll. Zum Zeichnen von 3D-Rahmen kann man auch die API-Funktion DrawEdge
verwenden.
Anpassen der Höhe eines Label-Steuerelements an den darzustellenden Text
Ab und zu ist es erforderlich, daß ein Label in seiner Höhe angepaßt werden muß, damit es einen bestimmten Text aufnehmen kann, ohne dabei jedoch die Breite des Steuerelements zu verändern. Die API-Funktion DrawText
bietet eine Möglichkeit, das für die Darstellung eines Textes auf einem angegebenen Gerätekontext zu berechnen. Die im Folgenden angegebene Prozedur AdaptLabel
paßt das übergebene Label so an, daß es den im zweiten Parameter übergebenen Text aufnehmen kann:
Private Declare Function DrawText Lib "user32.dll" Alias "DrawTextA" ( _
ByVal hDC As Long, _
ByVal lpStr As String, _
ByVal nCount As Long, _
ByRef lpRect As RECT, _
ByVal wFormat As Long _
) As Long
Private Const DT_CALCRECT As Long = &H400&
Private Const DT_WORDBREAK As Long = &H10&
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
'
' Schreibt den in 'Text' angegebenen Text in das Label 'Label', wobei dieses
' in der Höhe angepaßt wird, damit es den gesamten Text aufnehmen kann.
' Die Breite des Labels bleibt unverändert.
'
' Beispielaufruf:
'
' \\\
' Call AdaptLabel(Me.Label1, "Hallo Welt!")
' ///
'
Public Sub AdaptLabel( _
ByVal Label As Label, _
ByVal Text As String, _
Optional ByVal hDC As Long = 0 _
)
If hDC = 0 Then
hDC = Label.Parent.hDC
End If
Dim rct As RECT
rct.Left = 0
rct.Right = Label.Width \ Screen.TwipsPerPixelX
Call DrawText( _
hDC, _
Text, _
-1&, _
rct, _
DT_CALCRECT Or DT_WORDBREAK _
)
Label.Height = rct.Bottom * Screen.TwipsPerPixelY
Label.Caption = Text
End Sub
Der Code kann leicht umgeschrieben werden, um die Maße eines Textes zu ermitteln, ohne dabei automatisch umzubrechen. Zu diesem Zweck darf im Parameter wFormat
von DrawTextEx
nicht das Flag DT_WORDBREAK
gesetzt werden.
Unterscheiden zwischen „normaler“ Eingabetaste und jener am Ziffernblock
Es ist nicht möglich, mit reinen Visual-Basic-Mitteln zwischen der „normalen“ Eingabetaste und jener am Ziffernblock zu unterscheiden. Zu diesem Zweck kann man im KeyDown
-Ereignis mit der API-Funktion PeekMessage
die letzte Fensternachricht ermitteln und überprüfen:
Private Declare Function PeekMessage Lib "user32.dll" Alias "PeekMessageA" ( _
ByRef lpMsg As msg, _
ByVal hWnd As Long, _
ByVal wMsgFilterMin As Long, _
ByVal wMsgFilterMax As Long, _
ByVal wRemoveMsg As Long _
) As Long
Private Type POINTAPI
x As Long
y As Long
End Type
Private Type MSG
hWnd As Long
message As Long
wParam As Long
lParam As Long
time As Long
pt As POINTAPI
End Type
Private Const PM_NOREMOVE As Long = &H0&
Private Const VK_RETURN As Long = &HD&
Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer)
Dim n As Long, msg_ As MSG
n = PeekMessage(msg_, Me.Text1.hWnd, 0&, 0&, PM_NOREMOVE)
If n <> 0 Then
If msg_.wParam = VK_RETURN Then
If CBool(msg_.lParam And &H1000000) Then
Me.Caption = "Eingabetaste des Ziffernblocks"
Else
Me.Caption = "Normale Eingabetaste"
End If
Else
Me.Caption = ""
End If
Else
Me.Caption = ""
End If
End Sub
Entfernen von Einträgen aus einem DriveListBox-Steuerelement
Die API-Funktion SendMessage
wird verwendet, um über die Fensterzugriffsnummer (übergeben im Parameter hWnd
) der DriveListBox den im Parameter wMsg
abgelegten Befehl CB_DELETESTRING
an das Steuerelement zu senden. Dem Parameter wParam
wird dazu der Index des zu löschenden Eintrags übergeben, lParam
ist hier nicht von Bedeutung und wird daher auf 0 gesetzt:
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" ( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long _
) As Long
Private Const CB_DELETESTRING As Long = &H144&
Private Sub Form_Load()
' Laufwerk mit dem Index 0 (normalerweise 1. Diskettenlaufwerk) löschen.
Call SendMessage(Me.Drive1.hWnd, CB_DELETESTRING, 0&, 0&)
End Sub
Setzen von Tabulatoren in einem TextBox-Steuerelement
Will man in einem Textfeld tabellarische Daten darstellen, dann kann man den Text durch Tabulatorzeichen trennen, um Spalten zu simulieren. Visual Basic bietet jedoch keine direkte Möglichkeit, die Breite der Spalten zu beeinflussen. Hierzu kann man die API-Funktion SendMessage
in Verbindung mit EM_SETTABSTOPS
verwenden. Will man die Breite der Tabulatoren individuell festlegen, kann man deren Positionen in einem Array ablegen und dieses an die Funktion übergeben. Die Positionen der Tabulatoren muß dabei allerdings in Dialogeinheiten (Dialog Untis) angegeben werden.
Im folgenden Beispiel soll ein zweispaltiges Textfeld realisiert werden, das einen Tabulator genau in der horizontalen Mitte hat. Das Umrechnen der Position aus der Einheit Pixel in Dialogeinheiten gestaltet sich schwierig, da das Windows-API hierzu keine vorgefertigten Funktionen anbietet. Die im nächsten Listing gezeigte Funktionsprozedur GetDialogUnitsPerPixel
implementiert ein heuristisches Verfahren zur Ermittlung dieses Wertes auf Basis der Fonteinstellungen des durch die im Parameter hWnd
übergebene Fensterzugriffsnummer identifizierten Steuerelements.
Folgendes Beispiel erfordert ein Formular mit einem Textfeld, dessen MultiLine
-Eigenschaft auf True
gesetzt ist, sowie ein Line-Steuerelement:
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" ( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByRef lParam As Any _
) As Long
Private Const WM_GETFONT As Long = &H31&
Private Const EM_SETTABSTOPS As Long = &HCB&
Private Declare Function GetClientRect Lib "user32.dll" ( _
ByVal hWnd As Long, _
ByRef lpRect As RECT _
) As Long
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function GetDialogBaseUnits Lib "user32.dll" ( _
) As Long
Private Declare Function GetTextExtentPoint32 _
Lib "gdi32.dll" _
Alias "GetTextExtentPoint32A" _
( _
ByVal hDC As Long, _
ByVal lpString As String, _
ByVal cbString As Long, _
ByRef lpSize As SIZE _
) As Long
Private Type SIZE
cx As Long
cy As Long
End Type
Private Declare Function GetDC Lib "user32.dll" ( _
ByVal hWnd As Long _
) As Long
Private Declare Function ReleaseDC Lib "user32.dll" ( _
ByVal hWnd As Long, _
ByVal hDC As Long _
) As Long
Private Declare Function SelectObject Lib "gdi32.dll" ( _
ByVal hDC As Long, _
ByVal hObject As Long _
) As Long
Private Sub Form_Load()
Me.Text1.Text = "Hallo" & vbTab & "Welt!"
End Sub
Private Sub Form_Resize()
Call Me.Text1.Move(300, 300, Me.ScaleWidth - 600, Me.ScaleHeight - 600)
Dim rct As RECT
Call GetClientRect(Text1.hWnd, rct)
Dim TabStops(0 To 0) As Long
TabStops(0) = ((rct.Right - rct.Left) * 0.5) / GetDialogUnitsPerPixel(Text1.hWnd)
Call SendMessage(Text1.hWnd, EM_SETTABSTOPS, 2, TabStops(0))
With Line1
.X1 = Me.ScaleWidth * 0.5
.X2 = .X1
.Y1 = 0
.Y2 = Me.ScaleHeight
End With
End Sub
'
' Berechnet die Anzahl an Dialogeinheiten pro Pixel für das angegebene Fenster.
'
Private Function GetDialogUnitsPerPixel(ByVal hWnd As Long) As Single
' Gerätekontext des Steuerelements ermitteln.
Dim hDC As Long
hDC = GetDC(hWnd)
If hDC <> 0 Then
Const Chars As String = _
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
' Auswählen des 'HFONT's des Fensters in seinen Gerätekontext (VB
' wählt den Font des Steuerelements nicht in dessen Gerätekontext).
Dim hFont As Long
hFont = SendMessage(hWnd, WM_GETFONT, 0&, ByVal 0&)
Dim hFontOld As Long
hFontOld = SelectObject(hDC, hFont)
Dim sz As SIZE
If GetTextExtentPoint32(hDC, Chars, Len(Chars), sz) <> 0 Then
' Durchschnittliche Zeichenbreite in Pixeln ermitteln (heuristisch).
Dim AverageCharacterWidth As Long
AverageCharacterWidth = sz.cx / Len(Chars)
' Horizontale Dialogeinheiten ermitteln. Diese sind im Low-Word von
' 'GetDialogBaseUnits' enthalten.
Dim DlgBaseX As Long
DlgBaseX = GetDialogBaseUnits And &HFFFF&
' Anzahl der Dialogeinheiten pro Pixel zurückgeben.
GetDialogUnitsPerPixel = _
(2 * AverageCharacterWidth) / DlgBaseX
End If
Call SelectObject(hDC, hFontOld)
Call ReleaseDC(hWnd, hDC)
End If
End Function