|
Aufgabenstellungen wie zum Beispiel die folgende kennen sie sicher: "Nur wenn eine bestimmte CheckBox gesetzt ist, eine andere aber nicht, und in einer bestimmten TextBox ein bestimmter Text steht und ein bestimmter Button nicht gesperrt ist, dann soll dieses geschehen..." - "Und wenn die erste CheckBox nicht gesetzt ist, und die TextBox keinen Text enthält oder jene ScrollBar sichtbar ist, dann soll jenes geschehen..." - und dergleichen mehr an Kreuz- und Querkombinationen von Bedingungen.
Wie leicht verhaspelt man sich dann in If...Then...Else(If)-Gestrüppen wie etwa diesem:
If (Check1.Value = vbChecked) And (Check2.Value = vbUnchecked) _
And (Text1.Text = "abc") And Not (Command1.Enabled = False) Then
' ...
ElseIf ((Check1.Value = vbUnchecked) And (Len(Text1.Text = 0) _
Or (VScroll1.Visible = True)) Then
' ...
ElseIf ...
' ...
End If
Wenn dann solche Bedingungs-Verkettungen auch noch zur Laufzeit dynamisch erstellt werden sollen, ist meistens der Ofen endgültig aus.
Wie wäre es mit einer Klasse, in der Sie mehrere einzelne Bedingungen und Zustände ablegen können, und die auf Anfrage anhand einer vorgegebenen Verknüpfung ("And" oder "Or") ein Gesamtergebnis liefert?
Eigentlich ist das Prinzip recht einfach. Diese Klasse enthält eine Collection, in die Arrays aus jeweils dem betreffenden Steuerelement (oder einem beliebigen Objekt, das über eine Standard-Eigenschaft verfügt) und dem gewünschten Wert der Standard-Eigenschaft aufgenommen werden. Bei der Abfrage wird in einer Schleife für jedes dieser Arrays geprüft, ob der Wert der Standard-Eigenschaft des darin enthaltenen Steuerelements mit dem Vorgabewert übereinstimmt. Ist "And" als Verknüpfung für diesen Bedingungsstapel vorgesehen, wird die Schleife verlassen, sobald die erste Prüfung fehlschlägt - nur wenn alle Prüfungen erfolgreich verlaufen, wird als Gesamtergebnis True zurückgegeben. Ist "Or" als Verknüpfung gesetzt, wird die Schleife bei der ersten erfolgreichen Prüfung verlassen und als Gesamtergebnis True zurückgegeben.
Wenn Sie als drittes Element in diese Bedingungs-Arrays noch eine Kennung für den bei der jeweiligen Prüfung anzuwendenden Vergleichsoperator einfügen, können Sie nicht nur auf Gleichheit, sondern auch genau so gut auf Ungleichheit prüfen oder einen Größenvergleich vornehmen.
Da das Prüfergebnis dieser Klasse selbst auch ihr Standardwert ist, können Sie zudem beliebige Verschachtelungen vornehmen - entsprechend der Klammerung in komplexen Ausdrücken. Für jede Klammerung verwenden Sie eine Instanz dieser Klasse, die Sie wiederum als Bedingung in die übergeordnete Instanz aufnehmen.
Die Dynamik zur Laufzeit ergibt sich, weil Sie ja jederzeit eine Instanz der Klasse anlegen und mit Bedingungen füllen können - wobei Sie ebenso dynamisch für jede Bedingung das zu prüfende Objekt, den Prüfwert und den Vergleichsoperator festlegen können.
Im folgenden sehen Sie den Aufbau der ersten fünf Bedingungen, die in dem Beispiel-Progrämmchen zur oben stehenden Abbildung wirken. Den Code der Klasse clsConditions finden Sie weiter unten.
Dim nCondition As clsConditions
Set mConditions1 = New clsConditions
With mConditions1
.Operator = opAND
.Add chkA, vbChecked
.Add chkB, vbChecked
.Add chkE, vbUnchecked
End With
Set mConditions2 = New clsConditions
With mConditions2
.Operator = opOR
.Add chkB, vbChecked
.Add chkD, vbChecked
.Add chkE, vbChecked
End With
Set mConditions3 = New clsConditions
With mConditions3
.Operator = opAND
.Add chkC, vbChecked
.Add chkD, vbChecked
Set nCondition = New clsConditions
With nCondition
.Operator = opOR
.Add chkE, vbChecked
.Add chkF, vbChecked
End With
.Add nCondition, True
End With
Set mConditions4 = New clsConditions
With mConditions4
.Operator = opAND
.Add chkA, vbChecked, compNot
.Add chkB, vbChecked, compNot
.Add chkC, vbChecked, compNot
.Add chkD, vbChecked
.Add chkE, vbChecked, compNot
.Add chkF, vbChecked
End With
Set mConditions5 = New clsConditions
With mConditions5
.Operator = opAND
.Add chkE, vbChecked
.Add chkF, vbChecked
.Add txt, "abc"
End With
Die sechste, noch fehlende Bedingungssammlung enthält die Prüfung, ob das Form maximiert ist. Da die Eigenschaft WindowState jedoch nicht die Standard-Eigenschaft des Forms ist, würden die Prüfschleifen versagen, wenn wir das Form einfach so als Bedingungs-Objekt aufnehmen würden. Ab Visual Basic 6 böte sich immerhin die Möglichkeit an, über die CallByName-Funktion auch noch die zu prüfende Eigenschaft zu dynamisieren. Doch auf einem anderen Weg kommen wir nicht nur zu einer VB 5-kompatiblen Lösung, sondern eröffnen auf diesem Weg noch weitere interessante Möglichkeiten. Wir verpacken das Form in eine Hilfsklasse (etwa clsMaximized):
Private mForm As Form
Public Property Get Value() As Boolean
Value = CBool(mForm.WindowState = vbMaximized)
End Property
Public Sub Init(Form As Form)
Set mForm = Form
End Sub
Und dazu nun die Erstellung des sechsten Bedingungs-Stapels:
Dim nMaximized As clsMaximized
Set mConditions6 = New clsConditions
With mConditions6
.Operator = opAND
.Add chkA, vbChecked
.Add chkC, vbChecked
Set nMaximized = New clsMaximized
nMaximized.Init Me
.Add nMaximized, True
End With
Statt des Forms wird nun eine Instanz dieser Klasse in den Bedingungs-Stapel aufgenommen, nachdem sie über die Init-Methode mit dem Form "gefüttert" worden ist.
Diese Hilfsklasse ist allerdings festgelegt - sie liefert True als Wert ihrer Standard-Eigenschaft Value, wenn das Form maximiert ist. Die Prüfung erfolgt dann gegen True oder gegen False, wenn Sie wollen). Sie könnten die Klasse auch neutral auslegen, indem Sie in Value den Wert der WindowState-Eigenschaft zurückgeben und nicht gegen True, sondern gegen den gewünschten Wert des Form-Zustands (hier also etwa vbmaximized) geprüft wird. Sie können die Klasse aber auch weiter dynamisieren, indem Sie den internen Vergleichswert dynamisch als Eigenschaft auslegen:
Private mForm As Form
Private mWindowState As Integer
Public Property Let WindowState(New_WindowState As Integer)
mWindowState = New_WindowState
End Property
Public Property Get Value() As Boolean
Value = CBool(mForm.WindowState = mWindowState)
End Property
Public Sub Init(Form As Form)
Set mForm = Form
End Sub
Auf ähnliche Weise könnten Sie auch Hilfsklassen erstellen, die Vergleiche ermöglichen, die spezielle Operatoren erfordern, etwa TypeOf, IsObject, IsArray, VarType und viele mehr. Oder eine solche Hilfsklasse prüft nicht einen mehr oder weniger statisch vorgegebenen Wert, sondern sie führt im Moment der Prüfung irgendwelche komplexen Operationen durch, deren Ergebnis sie als einfachen Wahrheitswert in ihrer Value-Eigenschaft zurückgibt.
Die nun endlich folgende Bedingungs-Stapel-Klasse clsConditions ist somit nur ein Modell, das Sie weiter ausbauen, variieren und auch spezialisieren können. Es fehlt hier beispielsweise die Möglichkeit, einzelne Bedingungen dynamisch entfernen oder die Prüfwerte ändern zu können.
Public Enum OperatorConstants
opOR
opAND
End Enum
Public Enum ComparisonConstants
compEq
compNot
End Enum
Private mConditions As Collection
Private pOperator As OperatorConstants
Public Property Get Operator() As OperatorConstants
Operator = pOperator
End Property
Public Property Let Operator(New_Operator As OperatorConstants)
pOperator = New_Operator
End Property
Public Sub Add(Element As Variant, Value As Variant, _
Optional Comparison As Variant)
Dim nCondition As Variant
If IsMissing(Comparison) Then
nCondition = Array(Element, Value, compEq)
Else
nCondition = Array(Element, Value, Comparison)
End If
mConditions.Add nCondition
End Sub
Public Sub Clear()
Set mConditions = New Collection
End Sub
Public Function Value() As Boolean
Dim nCondition As Variant
Select Case pOperator
Case opOR
For Each nCondition In mConditions
Select Case nCondition(2)
Case compEq
If nCondition(0) = nCondition(1) Then
Value = True
Exit Function
End If
Case compNot
If nCondition(0) <> nCondition(1) Then
Value = True
Exit Function
End If
End Select
Next
Case opAND
For Each nCondition In mConditions
Select Case nCondition(2)
Case compEq
If nCondition(0) <> nCondition(1) Then
Exit Function
End If
Case compNot
If nCondition(0) = nCondition(1) Then
Exit Function
End If
End Select
Next
Value = True
End Select
End Function
Public Sub Init(ByVal Operator As OperatorConstants, _
ParamArray Conditions() As Variant)
Dim l As Long
pOperator = Operator
If UBound(Conditions) = 0 Then
If IsObject(Conditions(0)) Then
If TypeOf Conditions(0) Is Collection Then
Set mConditions = Conditions(0)
Exit Sub
End If
Else
Err.Raise 380
End If
End If
Set mConditions = New Collection
With mConditions
For l = 0 To UBound(Conditions)
If IsArray(Conditions(l)) Then
If (LBound(Conditions(l)) = 0) _
And ((UBound(Conditions(l)) = 1) _
Or (UBound(Conditions(l)) = 2)) Then
.Add Conditions(l)
Else
Err.Raise 380
End If
Else
Err.Raise 380
End If
Next 'l
End With
End Sub
Private Sub Class_Initialize()
Set mConditions = New Collection
End Sub
Und letztendlich können Sie das Gesamtergebnis eines solchen Bedingungs-Stapels dazu verwenden, genau so dynamisch erstellte Steuerelement-Gruppen auf einen Schlag zu manipulieren - siehe "Sieben auf einen Streich" khwstateenabled.htm.
|