Session D-WIZ

Wie mache ich meine
eigenen Builder

Rudolf Vogel
Vogel Software GmbH


Was sind Builders?

Builder sind Hilfsprogramme, die bei der Entwicklungsarbeit assistieren. VFP enthält bereits sehr viele nützliche Builder, die die Entwurfsarbeit sehr vereinfachen. Allerdings muß man immer noch sehr viele Schritte manuell erledigen. Hier bietet sich der Einsatz von eigenen Buildern an. Die offene Architektur von VFP macht es dem Programmierer sehr einfach, eigene Builder in die Entwicklungsumgebung zu integrieren.

Jeder Programmierer hat, was das Formulardesign angeht, einen individuellen Stil. Um diesen konsequent „durchzuhalten“, ist in der Regel sehr viel Arbeit und Konzentration notwendig. Was liegt näher, als diese Arbeit zu automatisieren?

Es folgt eine Auflistung von Tätigkeiten, die mir als sehr zeitraubend und lästig erscheinen:

VFP bietet eine nützliche Funktion, mit der die Programmierung von Buildern sehr leicht zu realisieren ist:

=ASELOBJ(Datenfeldname, [1 | 2])

Die Funktion ASELOBJ erstellt ein Datenfeld, in dem Objektverweise auf alle markierten Objekte oder das bearbeitete Formular enthalten sind. Die Eigenschaften dieser Objekte können anschließend mit einem Builder nach Belieben manipuliert werden.

Beispielprogramm 1: Ausrichten einer Reihe von Objekten

Alle markierten Objekte sollen linksbündig zum obersten Objekt sein. Jedes Objekt soll 24 Pixel „tiefer“ plaziert werden als das vorhergehende Objekt. Wir gehen in diesem Beispiel davon aus, daß die Objekte eine Höhe von 21 Pixeln haben. Ausnahme ist das Bearbeitungsfeld, das bei mir eine Höhe von 46 Pixeln hat. Es müssen mindestens 2 Objekte markiert sein.

* Objektverweis aller markierten Objekte in Datenfeld aLabels schreiben
=aselobj(aLabels)
*Objektverweis des bearbeiteten Formulares in Datenfeld aContainer schreiben
=aselobj(aContainer,1)
* Prüfen, ob Objekte markiert sind
if type("aLabels") = "U"
wait „Bitte markieren Sie zuvor mehrere Objekte!" wind
return
endif

local lnLaenge
lnLaenge = alen(aLabels)
if lnLaenge < 2
wait"Sie müssen mindestens 2 Objekte markieren!"
return
endif

* Abspeichern der Koordinaten des ersten Objektes
local lnTop, lnLeft
lnTop = aLabels[1].top
lnLeft = aLabels[1].left

* Bearbeitung der anderen Objekte
for i = 2 to lnLaenge

if aLabels[i-1].height < 24
lnTop = lnTop + 24

else
lnTop = lnTop + 48
endif

* Setzen der neuen Eigenschaften
aLabels[i].top = lnTop
aLabels[i].left= lnLeft
endfor

acontainer[1].Refresh

Wie kann ein Builder aktiviert werden?

Es gibt mehrere Möglichkeiten, Builder zu aktivieren. Da ein Builder ein „normales“ VFP-Programm ist, kann man ihn z. B. aus einem Menü oder Toolbar heraus starten. Zum Testen ist es komfortabel, ihn per „ON KEY LABEL“ zu aktivieren. Es gibt aber auch die Möglichkeit, ihn per Klick auf die rechte Maustaste zu starten. Dazu muß die Datei \vfp\wizards\builder editiert werden. Darauf kommen wir allerdings später zu sprechen.

Beispielprogramm 2: Durchsetzen von Namenskonventionen

Das Einhalten von Namenskonventionen ist ein zwar lästiges, aber dennoch notwendiges Übel. Der folgende Builder übernimmt diese Aufgabe. Sie müssen sich lediglich darum kümmern, den Objekten in Ihrem Formular eine Caption bzw. eine Controlsource zu geben. Die Namensvergabe übernimmt der Builder.

* Objektverweis aller markierten Objekte in Datenfeld aLabels schreiben
=aselobj(aLabels)
*Objektverweis des bearbeiteten Formulares in Datenfeld aContainer schreiben
=aselobj(aContainer,1)

* Prüfen, ob Objekte markiert sind
if type("aLabels") = "U"
wait „Bitte markieren Sie zuvor mehrere Objekte!" wind
return
endif

local lnLaenge
lnLaenge = alen(aLabels)

for i = 1 to lnLaenge

do case
case alabels[i].class="Textbox" and not empty(alabels[i].controlsource)
alabels[i].name = "txt" + cutalias(proper(alabels[i].controlsource))
case alabels[i].baseclass="Commandbutton"
alabels[i].name = "cmd" + proper(alabels[i].caption)
case alabels[i].baseclass="Label"
alabels[i].name = "lbl" + proper(alabels[i].caption)
case alabels[i].baseclass="Checkbox"
alabels[i].name = "chk" + proper(alabels[i].caption)
case alabels[i].baseclass="Editbox" and not empty(alabels[i].controlsource)
alabels[i].name = "edt" + cutalias(proper(alabels[i].controlsource))
case alabels[i].baseclass="Combobox" and not empty(alabels[i].controlsource)
alabels[i].name = "cbo" + cutalias(proper(alabels[i].controlsource))
case alabels[i].baseclass="Container" and not empty(alabels[i].controlsource)
alabels[i].name = "cnt" + cutalias(proper(alabels[i].controlsource))
endcase
endfor

* „Abschneiden“ des Alias aus der Controlsource eines Objektes
proc cutalias
lparameter lcControlSource
if at(".", lcControlSource) = 0
return lcControlSource
else
return right(lcControlSource, ;
len(lcControlSource) - at(".", lcControlSource) )
endif

Beispielprogramm 3: „Namensvettern“ suchen und anordnen

Das folgende Programmbeispiel sucht zu jedem Editierobjekt im Formular das passende Label und ordnet es linksbündig davon an. Der Builder geht davon aus, daß bei allen markierten Editierobjekten der linke Rand identisch ist und die Namen von zusammengehörigen Editierobjekten und Labels, abgesehen von den ersten 3 Buchstaben, identisch sind.

=aselobj(alabels)
=aselobj(aContainer,1)

if type("alabels") = "U"
wait "Bitte markieren Sie zuvor mehrere Objekte!" wind
return
endif

local lnLaenge
lnLaenge = alen(alabels)
if lnLaenge < 2
wait "Sie müssen mindestens 2 Objekte markieren!" window
return
endif

local i, j, lcRestName, lnMaxWidth, lnGetLeft
lnGetLeft = 0
lnMaxWidth = 0
for i = 1 to lnLaenge
if upper(left(alabels[i].name,3)) = "LBL"
* Wir haben ein Label gefunden
* wir merken uns die größte Labelbreite für später
lnMaxWidth = max(lnMaxWidth, alabels[i].width)
wait "Label " + alabels[i].name + ;
" gefunden, suche Namensvetter..." wind time .5
lcRestName = upper(subs(alabels[i].name, 4, ;
len(alabels[i].name)-3))

**********************************************************************
* das passende Editierobjekt suchen und anschließend dem
* dazugehörigen Label die gleiche vertikale Position geben
**********************************************************************

for j = 1 to lnLaenge
if upper(subs(alabels[j].name, 4, len(alabels[j].name)-3)) ;
= lcRestName ;
and not upper(left(alabels[j].name,3)) = "LBL"
wait "Namensvetter gefunden: "+alabels[j].name wind time .5
alabels[i].top = alabels[j].top
exit
endif
endfor
else
lnGetLeft = alabels[i].left
endif
endfor
for i = 1 to lnLaenge
if upper(left(alabels[i].name,3)) = "LBL"
* Label gefunden, horizontale Position anpassen
alabels[i].left = lnGetLeft - 10 - lnMaxWidth
endif
endfor

Registrieren von Buildern

Als Beispiel hierfür benutzen wir den „Multi Object Property Changer“, den Sie auf der Konferenzdiskette unter dem Dateinamen MULTIPRP.ZIP finden. Der „Multi Object Property Changer“ ist ein Quick&Dirty Builder, der eine Eigenschaft aller selektierten Objekte ändert. Da dieser Builder sehr universell ist, macht es Sinn, ihn durch Klick auf die rechte Maustaste zu aktivieren. Bearbeiten Sie hierzu die Tabelle \VFP\wizards\builder.

Um den Builder zu deaktivieren, der von VFP standardmäßig gestartet wird, setzen Sie einfach ein „x“ vor den vorhandenen MULTISELECT-Eintrag.

Fügen Sie nun der Tabelle einen neuen Datensatz hinzu.

Wenn Sie nun bei der Bearbeitung eines Formulares mehrere Objekte markieren und nach Klicken auf die rechte Maustaste die Menüoption „Steuerelementassistenten“ selektieren, wird der neue Builder automatisch gestartet. Hätten Sie vorher den standardmäßig eingerichteten Builder nicht deaktiviert, würde Ihnen VFP beide Builder in einem Menü zur Auswahl anbieten.

Im Prinzip können Sie für jede Ihrer Klassen automatisch einen eigenen Builder starten, indem Sie dafür in der Tabelle BUILDER.DBF Einträge vornehmen.