Es ist nicht nur ein guter Programmierstil, veränderte Zustände
nach getaner Arbeit wieder zurückzusetzen, sondern vielmehr eine
wichtige Maßnahme zur Verhinderung von Fehlern. Wenn Sie
beispielsweise bei einem Objekt (Form, PictureBox, Printer,
UserControl usw.) vorübergehen während einer Operation einen
anderen ScaleMode
einstellen, sollten Sie nach Abschluß dieser Operation die
ursprünglich angetroffene Einstellung wiederherstellen, damit
andere Operationen nicht unvermuteterweise auf eine andere
Einstellung als erwartet treffen und fehlerhaft funktionieren.
Auf den ersten Blick scheint das eine banale Angelegenheit zu
sein. Im Prinzip müsste es ja genügen, den ScaleMode des
betreffenden Objekts zu Beginn der Operation in einer lokalen
Variablen zwischenzuspeichern und in nach Abschluß der Operation
wieder der ScaleMode-Eigenschaft des Objekts zuzuweisen:
Dim nOldScaleMode As ScaleModeConstants
' ...
nOldScaleMode = Object.ScaleMode
' ... Operation(en)
Object.ScaleMode = nScaleMode
In der Regel reicht dies auch, wenn Sie lediglich zwischen den
"festen" ScaleMode-Einstellungen hin und her schalten.
Falls es jedoch vorkommen kann, dass benutzerdefinierte
ScaleMode-Eigenschaften zu sichern und wiederherzustellen sind,
reicht dieses Verfahren nicht aus. Benutzerdefinierte
ScaleMode-Eigenschaften bedeuten, dass mindestens eine der weiteren
Scale...-Eigenschaften ( ScaleLeft
und ScaleTop oder ScaleWidth
und ScaleHeight) verändert worden sind. Visual Basic setzt dann
nämlich den ScaleMode auf 0 (vbUser). Wenn Sie danach den ScaleMode
ändern, etwa in vbTwips, und in wieder auf vbUser zurücksetzen
würden, gehen die ursprünglichen Scale...-Werte verloren. Statt
dessen bleiben die letzten Scale..-Werte (hier vbTwips entsprechend)
zurück.
Treffen Sie also vor Beginn Ihrer Operation(en) auf den ScaleMode
vbUser, müssen Sie auch alle übrigen Scale...-Eigenschaften (da
Sie ja nicht wissen können, welche von diesen verändert wurden)
sichern und anschließend wiederherstellen:
Dim nOldScaleMode As ScaleModeConstants
Dim nOldScaleLeft as Single
Dim nOldScaleTop as Single
Dim nOldScaleWidth as Single
Dim nOldScaleHeight as Single
'...
With Object
nOldScaleMode = .ScaleMode
If nOldScaleMode = vbUser Then
nOldScaleLeft = .ScaleLeft
nOldScaleTop = .ScaleTop
nOldScaleWidth = .ScaleWidth
nOldScaleHeight = .ScaleHeight
End If
End With
'... Ihre Operation(en)
With Object
.OldScaleMode = nScaleMode
If nOldScaleMode = vbUser Then
.OldScaleLeft = nScaleLeft
.OldScaleTop = nScaleTop
.OldScaleWidth = nScaleWidth
.nOldScaleHeight = nScaleHeight
End If
End With
Das sieht nach mühseliger Tipperei aus, nicht wahr? Und
Performance kostet das auch, vor allem, wenn die benutzerdefinierten
Scale...-Einstellungen nur einmal gesetzt wurden (etwa zur
Entwicklungszeit) und später dann eigentlich nur jeweils
wiederhergestellt zu werden bräuchten.
Zur Tipperei sparenden und gegebenenfalls die Performance
steigernden Lösung dieser Aufgabe bietet sich die Verwendung einer
Klasse an, die den notwendigen Code enthält - die Klasse
clsStoreScaleMode.
Der Methode Store der Klasse übergeben Sie einfach das Objekt,
dessen Scale-Eigenschaften gesichert werden sollen und zum
Wiederherstellen der Einstellungen rufen Sie die Restore-Methode der
Klasse auf. Da eine Instanz dieser Klasse zur Sicherung meistens nur
lokal in einer Prozedur verwendet wird (andere
Verwendungsmöglichkeiten werde ich Ihnen noch später in diesem
Artikel zeigen), können Sie die Instanzvariable sogar ausnahmsweise
als "As New" deklarieren. Dann können Sie die
Objektinstanz ohne ausdrückliche Instanzierung verwenden. Seine
Lebensdauer ist automatisch auf die jeweilige Prozedur beschränkt.
Konkret sieht die Verwendung zum Beispiel so aus:
Sub Irgendwas()
Dim nStoreScaleMode As New clsStoreScaleMode
' z.B. Form1.ScaleWidth ist 100
nStoreScaleMode.Store Form1
' z.B. für API-Operationen:
' Form1.ScaleMode = vbPixels
' ... Ihre Operation(en) ...
nStoreScaleMode.Restore
' Form1.ScaleWidth ist wieder 100
End Sub
Das sieht doch schon viel handlicher aus, oder? Nun noch ein
Beispiel zur ständigen Wiederherstellung von voreingestellten Scale...-Werten.
Angenommen, Sie hätten die ScaleHeight- und ScaleWidth-Eigenschaft
eines Forms zur Entwicklungszeit individuell eingestellt,
beispielsweise auf ein bestimmtes Papierformat für eine
Druckvorschau. Dann speichern Sie den Scale-Zustand ein einziges Mal
in einer Objektinstanz der Klasse clsStoreScaleMode und restaurieren
ihn nur noch jedes Mal nach jeder Veränderung - wenn Sie etwa die
reale Größe des Forms auf dem Bildschirm geändert haben.
Zugegeben, das ist kein besonders glückliches Beispiel, da dann die
Größenänderung des Forms proportional erfolgen sollte, damit die
Darstellung nicht in der Breite oder in der Höhe verzerrt wird.
Aber ich denke, Sie werden das Prinzip verstehen und das Problem der
proportionalen Größenänderung des Forms selbst zu lösen wissen.
Das betreffende Form-Modul würde nun folgenden Code enthalten:
Private mStoreScaleMode As clsStoreScaleMode
Private Sub Form_Load()
Set mStoreScaleMode = New clsStoreScaleMode
mStoreScaleMode.Store Me
End Sub
Private Sub Form_Resize()
If Me.WindowState = vbNormal Then
mStoreScaleMode.Restore
End If
End Sub
Schauen wir uns nun einmal die beiden Methoden der Klasse näher
an. Die modulweiten Deklarationen und ihr Code lautet:
Public Enum ssmErrorConstants
ssmErrNoScaleModeProperty = vbObjectError + 10001
ssmErrNoScaleProperties = vbObjectError + 10002
ssmErrNoObject = vbObjectError + 10003
ssmErrUnknown = vbObjectError + 10010
End Enum
Private pObject As Object
Private pScaleHeight As Single
Private pScaleLeft As Single
Private pScaleMode As ScaleModeConstants
Private pScaleTop As Single
Private pScaleWidth As Single
Public Sub Store(Optional Object As Object, _
Optional ByVal StoreObject As Boolean = True)
Dim nObject As Object
If StoreObject Then
Set pObject = Object
Set nObject = pObject
Else
If Object Is Nothing Then
Set nObject = pObject
Else
Set nObject = Object
End If
End If
If nObject Is Nothing Then
Err.Raise ssmErrNoObject, "clsStoreScaleMode.Store", _
"Kein Objekt."
Else
With nObject
On Error Resume Next
pScaleMode = .ScaleMode
Select Case Err.Number
Case 0
pScaleLeft = .ScaleLeft
pScaleTop = .ScaleTop
pScaleHeight = .ScaleHeight
pScaleWidth = .ScaleWidth
Select Case Err.Number
Case 438
Err.Raise ssmErrNoScaleProperties, _
"clsStoreScaleMode.Store", _
"Objekt verfügt nicht über Scale-Eigenschaften."
Case Else
Err.Raise ssmErrUnknown, _
"clsStoresScaleMode.Store", _
"Objekt-Fehler Nr. " & Err.Number
End Select
Case 438
Err.Raise ssmErrNoScaleModeProperty, _
"clsStoreScaleMode.Store", _
"Objekt verfügt nicht über ScaleMode-Eigenschaft."
Case Else
Err.Raise ssmErrUnknown, _
"clsStoresScaleMode.Store", _
"Objekt-Fehler Nr. " & Err.Number
End Select
End With
End With
End Sub
Public Sub Restore(Optional Object As Object)
Dim nObject As Object
If Object Is Nothing Then
Set nObject = pObject
Else
Set nObject = Object
End If
If nObject Is Nothing Then
Err.Raise ssmErrNoObject, "clsStoreScaleMode.Restore", _
"Kein Objekt."
Else
With Object
On Error Resume Next
.ScaleMode = pScaleMode
Select Case Err.Number
Case 0
If pScaleMode = vbUser Then
.ScaleLeft = pScaleLeft
.ScaleTop = pScaleTop
.ScaleHeight = pScaleHeight
.ScaleWidth = pScaleWidth
Select Case Err.Number
Case 438
Err.Raise ssmErrNoScaleProperties, _
"clsStoreScaleMode.Restore", _
"Objekt verfügt nicht über Scale-Eigenschaften."
Case Else
Err.Raise ssmErrUnknown, _
"clsStoresScaleMode.Restore", _
"Objekt-Fehler Nr. " & Err.Number
End Select
End If
Case 438
Err.Raise ssmErrNoScaleModeProperty, _
"clsStoreScaleMode.Restore", _
"Objekt verfügt nicht über ScaleMode-Eigenschaft."
Case Else
Err.Raise ssmErrUnknown, _
"clsStoresScaleMode.Restore", _
"Objekt-Fehler Nr. " & Err.Number
End Select
End With
End With
End Sub
Sie sehen, dass da einiges gegenüber dem oben gezeigten, jedes
Mal erneut einzugebenden lokalen Code hinzugekommen ist. Diese
Erweiterungen haben jedoch ihren Sinn (als ob ich Ihnen sinnlosen
Code verkaufen täte...).
Da ist etwa der optionale Parameter StoreObject der
Store-Methode. Er ist auf True voreingestellt. Wie das zuvor
gezeigte zweite Verwendungsbeispiel zeigt, ist es dafür notwendig,
das Objekt zu speichern. Allerdings kann es eventuell notwendig
sein, auf die Zwischenspeicherung zu verzichten, damit nicht
irgendwelche Objektreferenzen "irgendwo in der Landschaft
herumstehen", die ein sauberes Terminieren des betreffenden
Objekts verhindern könnten. Das ist auch der erste Grund für den
optionalen Parameter Object der Restore-Methode. Sie können also
das Objekt, falls es nicht gespeichert werden sollte, ausdrücklich
übergeben.
Die ersten Code-Zeilen der beiden Methoden dienen zur
Feststellung, ob ein Objekt übergeben worden ist und auf welches
Objekt der eigentliche Code der Methode angewendet werden soll. Denn
auch bei der Store-Methode ist der Object-Parameter optional, so
dass Sie sich nach einer erstmaligen Übergabe des betreffenden
Objekts im weiteren die Übergabe sogar sparen können. Wurde kein
Objekt übergeben, wird die intern in der Variablen pObject
abgelegte Objektreferenz verwendet. Stellt sich nun heraus, dass gar
kein Objekt vorhanden ist, wird die Bearbeitung der Methoden mit der
Fehlermeldung ssmErrNoObject (aus der Enumeration ssmErrorConstants)
beendet.
Da wir den Objekt-Parameter unverbindlich als "As
Object" deklariert haben (späte Bindung - Sie können
natürlich die Klasse auch für einen bestimmten Objekt-Typ, etwa
für Forms spezialiseren, indem Sie den Object-Parameter als
"As Form" deklarieren), könnte es ja vorkommen, dass
versehentlich ein Objekt übergeben worden ist, dass nicht über
eine ScaleMode-Eigenschaft verfügt, oder nicht über die übrigen
Scale...-Eigenschaften. Wir schalten daher eine einfache
Fehlerbehandlung mit On Error Resume Next ein, die wir entsprechend
auswerten und im Fehlerfalle einen Laufzeitfehler ( Err.Raise
...) mit der passenden Fehlernummer (wieder aus ssmErrorConstants)
auslösen.
Im übrigen entspricht der Code dem Eingangs angeführten
Code-Beispiel.
Der Ordnung halber geben wir in jedem Fall das in pObject
gegebenenfalls gespeicherte Objekt im Terminate-Ereignis der Klasse
wieder frei:
Private Sub Class_Terminate()
Set pObject = Nothing
End Sub
Kommen wir nun zu den weiteren Verwendungmöglichkeiten der
Klasse clsStoreScaleMode.
Der tieferen Sinn des Spielchens mit den optionalen
Object-Parametern und der Speicherung der Objektreferenz wird Ihnen
aufgehen, wenn Sie erst einmal auf die Idee kommen, dass Sie
durchaus verschiedene Objekte ins Spiel bringen können.
So können Sie die Scale...-Einstellungen eines Forms ohne
Probleme an ein anderes Form weitergeben, indem Sie der
Store-Methode das erste Form übergeben, der Restore-Methode jedoch
ein anderes Form. Aufgrund der Deklarationen "As Object"
können Sie sogar so weit gehen, mit verschiedenen Objekttypen zu
hantieren. Die Übergabe der Scale...-Einstellungen einer PictureBox
(oder eines hochspezialisierten UserControls oder OCXes) an das
Printer-Objekt ist nur eine von vielen Möglichkeiten.
Sie können auch für ein und dasselbe Objekt mehrere
benutzerdefinierte Scale...-Einstellungen speichern, indem Sie das
Objekt im jeweiligen Zustand einer gesonderten Instanz der
clsStoreScaleMode-Klasse übergeben. Quasi wie bei der Verwendung
von Formatvorlagen in einer Textverarbeitung können Sie so ohne
Umstände jederzeit verschiedene Scale...-Einstellungen abrufen -
stecken Sie diese "Vorlagen"-Instanzen unter einem
Vorlagennamen in eine Collection
und... alles klar? Genauso können Sie auch verschiedene Zustände
in einer Collection stapeln, um eine Undo-Funktion zu ermöglichen.
Weiteren Komfort gerade im Hinblick auf die zuletzt angeführten
Verwendungsmöglichkeiten bietet die naheliegende Offenlegung der
internen Variablen zur Speicherung der Scale...-Eigenschaften über
Eigenschaften-Prozeduren. Wie auch VB es handhabt, wird hier der
interne Wert pScaleMode auf vbUser gesetzt, sobald eine der
Eigenschaften ScaleLeft, ScaleTop, ScaleWidth oder ScaleHeight
geändert wird.
Public Property Get Object() As Object
Set Object = pObject
End Property
Public Property Set Object(New_Object As Object)
Set pObject = New_Object
End Property
Public Property Get ScaleHeight() As Single
ScaleHeight = pScaleHeight
End Property
Public Property Let ScaleHeight(ByVal New_ScaleHeight As Single)
pScaleHeight = New_ScaleHeight
pScaleMode = vbUser
End Property
Public Property Get ScaleLeft() As Single
ScaleLeft = pScaleLeft
End Property
Public Property Let ScaleLeft(ByVal New_ScaleLeft As Single)
pScaleLeft = New_ScaleLeft
pScaleMode = vbUser
End Property
Public Property Get ScaleMode() As ScaleModeConstants
ScaleMode = pScaleMode
End Property
Public Property Let ScaleMode(ByVal New_ScaleMode _
As ScaleModeConstants)
pScaleMode = New_ScaleMode
End Property
Public Property Get ScaleTop() As Single
ScaleTop = pScaleTop
End Property
Public Property Let ScaleTop(ByVal New_ScaleTop As Single)
pScaleTop = New_ScaleTop
pScaleMode = vbUser
End Property
Public Property Get ScaleWidth() As Single
ScaleWidth = pScaleWidth
End Property
Public Property Let ScaleWidth(ByVal New_ScaleWidth As Single)
pScaleWidth = New_ScaleWidth
pScaleMode = vbUser
End Property

|