Angenommen, Sie möchten für eine Datenbankanwendung eine Bildschirmmaske für die Dateneingabe programmieren. Der einfachste Ansatz dafür wäre
' Dateneingabe für eine Adresse LINE INPUT "Name", n$ LINE INPUT "Strasse", a$ LINE INPUT "PLZ/Wohnort"; w$
Doch LINE INPUT hat einige Nachteile:
LINE
INPUT den Programmablauf solange blockiertAus all diesen Gründen möchte ich Ihnen hier einige Ideen für eine professionellere Eingabe aufzeigen.
Dazu eignet sich vorzüglich ein SUB-Unterprogramm mit
grob folgendem Aufbau:
SUB EditiereZeile (t$, p%, Ins%, Verl%)
' t$ = zu bearbeitender Text (Stringlänge = Feldgrösse)
' p% = Aktuelle Cursorposition (0=Anfang)
' Ins% = Flag für Einfüge- (-1)/Überschreibmodus (0)
' Verl% = Rückgabewert:
' 1 = Return/Enter
' 2 = Strg + Return
' 3 = Tab
' 4 = Umschalt + Tab
' 5 = Pfeil hoch
' 6 = Pfeil unten
' 7 = Strg + Pos1
' 8 = Strg + Ende
' 9 = Esc
' 10..19 = <F1>..<F10>
' aktuelle Cursorposition sichern
x% = POS(0)
y% = CSRLIN
IF p% >= LEN(t$) THEN
p% = LEN(t$) - 1
END IF
Verl% = 0
WHILE Verl% = 0
' Feld ausgeben
LOCATE y%, x%
PRINT t$;
' Cursor darstellen
LOCATE y%, x% + p%, 1, 6 + 4 * Ins%, 7
DO
k$ = INKEY$
LOOP WHILE k$ = ""
LOCATE , , 0 ' Cursorblinken aus
SELECT CASE k$
CASE " " TO "~", CHR$(128) TO CHR$(255)
' normales Zeichen
IF Ins% THEN
t$ = LEFT$(t$, p%) + k$ + MID$(t$, p% + 1, LEN(t$) - p% - 1)
ELSE
MID$(t$, p% + 1) = k$
END IF
IF p% = LEN(t$) - 1 THEN
BEEP ' Warnung, Feldende erreicht
ELSE
p% = p% + 1 ' Cursor eins vorwärts
END IF
CASE CHR$(0) + "K" ' Pfeil links
IF p% = 0 THEN ' Cursor am Feldanfang?
BEEP
ELSE
p% = p% - 1
END IF
CASE CHR$(0) + "M" ' Pfeil rechts
IF p% = LEN(t$) - 1 THEN ' bereits ganz rechts?
BEEP
ELSE
p% = p% + 1
END IF
CASE CHR$(0) + "G" ' Pos1
p% = 0
CASE CHR$(0) + "O" ' Ende
p% = LEN(RTRIM$(t$))
CASE CHR$(0) + "R" ' Einfügen
Ins% = NOT Ins% ' Wechseln zwischen Einfügen und Überschreiben
CASE CHR$(0) + "S" ' <Lösch>-Taste (Del)
t$ = LEFT$(t$, p%) + MID$(t$, p% + 2) + " "
CASE CHR$(8) ' <Rück>-Taste (Backspace)
IF p% = 0 THEN ' Bereits am Anfang?
BEEP
ELSE
t$ = LEFT$(t$, p% - 1) + MID$(t$, p% + 1) + " "
p% = p% - 1
END IF
CASE CHR$(13)
Verl% = 1
CASE CHR$(10)
Verl% = 2
CASE CHR$(9) ' Tab
Verl% = 3
CASE CHR$(0) + CHR$(15) ' Umschalt + Tab
Verl% = 4
CASE CHR$(0) + "H" ' Pfeil hoch
Verl% = 5
CASE CHR$(0) + "P" ' Pfeil unten
Verl% = 6
CASE CHR$(0) + "w" ' Strg + Pos1
Verl% = 7
CASE CHR$(0) + "u" ' Strg+Ende
Verl% = 8
CASE CHR$(27)
Verl% = 9
CASE CHR$(0) + ";" TO CHR$(0) + "D" ' Funktionstaste <F1> - <F10>
Verl% = ASC(RIGHT$(k$, 1)) - 49
CASE ELSE ' übrige Tasten
BEEP
END SELECT
WEND
END SUB
Im Hauptprogramm sollten Sie mit Vorteil Feldvariablen zur Maskenbeschreibung verwenden. Dadurch wird es extrem einfach, die einzelnen Felder für die Navigation zu verknüpfen, ohne dass Sie Code für jedes Feld wiederholen müssen, denn wie unter häufige Fehler von Anfänger beschrieben sollten Sie einen Copy&Paste-Programmierstil vermeiden. Als Beispiel verwende ich eine Adressmaske:
' Adressmaske
DECLARE SUB EditiereZeile (t$, p%, Ins%, Verl%)
CONST nFelder% = 6
DIM cx%(1 TO nFelder%), cy%(1 TO nFelder%), fld$(1 TO nFelder%)
FOR i% = 1 TO nFelder%
READ cx%(i%), cy%(i%), l%
fld$(i%) = SPACE$(l%)
NEXT i%
DATA 6, 1, 20, 35, 1, 20
DATA 9, 2, 30
DATA 5, 3, 4, 14, 3, 14
DATA 6, 5, 20
COLOR 7, 0
CLS
PRINT "Name Vorname"
PRINT "Adresse"
PRINT "PLZ Ort"
PRINT
PRINT "Tel."
PRINT
COLOR 15, 1
PRINT " F10 ";
COLOR 7, 0
PRINT " = Speichern & Ende ";
COLOR 15, 1
PRINT " Esc ";
COLOR 7, 0
PRINT " = Abbruch"
COLOR 1, 7
FOR i% = 1 TO nFelder%
LOCATE cy%(i%), cx%(i%)
PRINT fld$(i%);
NEXT i%
Ins% = 0 ' Zu Beginn Überschreibmodus
p% = 0 ' Cursor am Anfang
AktF% = 1 ' aktuelles Feld
DO
LOCATE cy%(AktF%), cx%(AktF%)
EditiereZeile fld$(AktF%), p%, Ins%, rc%
SELECT CASE rc%
CASE 1
AktF% = AktF% MOD nFelder% + 1
p% = 0
CASE 2
AktF% = (AktF% + nFelder% - 2) MOD nFelder% + 1
p% = 0
CASE 3
AktF% = AktF% MOD nFelder% + 1
p% = LEN(RTRIM$(fld$(AktF%)))
CASE 4
AktF% = (AktF% + nFelder% - 2) MOD nFelder% + 1
p% = LEN(RTRIM$(fld$(AktF%)))
CASE 5
AktF% = (AktF% + nFelder% - 2) MOD nFelder% + 1
CASE 6
AktF% = AktF% MOD nFelder% + 1
CASE 7
AktF% = 1
p% = 0
CASE 8
AktF% = nFelder%
p% = LEN(RTRIM$(fld$(AktF%)))
CASE 9, 19
' Nicht tun
CASE ELSE
BEEP
END SELECT
LOOP UNTIL rc% = 9 OR rc% = 19
COLOR 7, 0
LOCATE 9, 1
IF rc% = 9 THEN
PRINT "Abgebrochen"
ELSE
PRINT "Satz gespeichert"
END IF
END
Durch den Einsatz der Feldvariable fld$() zusammen
mit cx%() und cy%() für die
Cursorplazierung konnte das mehrfache Kopieren des Aufrufcodes vermieden
werden, so dass dafür die Auswertung des Tasten-Rückgabecodes rc% ohne weiteres mit allen Spezialfällen
ausprogrammiert werden konnte. Sie sehen nun selber, wie man eine mit F1 aufrufbare Online-Hilfefunktion implementieren
kann: Weiterer CASE-Block genügt.
Selbstverständlich können Sie auch das Unterprogramm
EditiereZeile nach Belieben ausbauen. Als Beispiel möchten
Sie dem Anwender die Möglichkeit geben, mit Alt+hervorgehobener Buchstabe
Felder direkt anspringen zu können. Dazu erzeugen Sie zu Beginn eine
Tastencode-Liste in Form eines Strings:
DIM SHARED ac$ ac$="" FOR i% = 1 TO 36 READ j% ac$ = ac$ + CHR$(j%) NEXT i% ' Buchstaben Alt+A - Alt+Z (nur zweites Byte) DATA 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50, 49, 24, 25, 16, 19 DATA 31, 20, 22, 47, 17, 45, 21, 44, ' Ziffern Alt+0 - Alt+9 (nur zweites Byte) DATA 129, 120, 121, 122, 123, 124, 125, 126, 127, 128
Einen solchen Tastendruck können Sie dann innerhalb von
EditiereZeile durch Ergänzen des letzten
CASE-Blockes wie folgt auswerten:
' 9 = Esc
' 10..19 = <F1>..<F10>
' 20..45 = <Alt>+<A>..<Alt>+<Z>
' 46..55 = <Alt>+<0>..<Alt>+<9>
' aktuelle Cursorposition sichern
' ...
' ... (unverändert)
CASE CHR$(0) + ";" TO CHR$(0) + "D" ' Funktionstaste <F1> - <F10>
Verl% = ASC(RIGHT$(k$, 1)) - 49
CASE ELSE ' übrige Tasten
IF LEN(k$) = 2 AND LEFT$(k$, 1) = CHR$(0) THEN
h% = INSTR(ac$, RIGHT$(k$, 1))
IF h% > 0% THEN
Verl% = 19 + h%
ELSE
BEEP
END IF
ELSE
BEEP
END IF
END SELECT
WEND
END SUB
Die Auswertung erfolgt am einfachsten im CASE ELSE-Block durch
eine Suche im vorhin definierten Tabellenstring.
Vielleicht möchten Sie ausser dem Texteingabefeld noch Radioknöpfe für Auswahllisten und Ankreuzboxen für ein- und ausschaltbare Elemente wie auf diesem Beispiel:

Beispiel einer Etikettenparametermaske in einer Anwendung
In einem solchen Fall empfiehlt sich die Erstellung von weiteren
SUB-Unterprogrammen, also analog zu EditiereZeile
auch für die übrigen Elemente jeweils ein solches Unterprogramm:
DECLARE SUB EditiereZeile (t$, p%, Ins%, Verl%) DECLARE SUB RadioButtonFamilie (cx%(), cy%(), StartInd%, AnzKn%, AktW%, Verl%) DECLARE SUB CheckBox (cx%, cy%, istEin%, Verl%) ' Gesamte Verarbeitung DECLARE SUB BildschirmMaske (db%(), gk!(), s$(), StartInd%)
Und das allerwichtigste dabei ist das Unterprogramm
BildschirmMaske selber, bei welchem Sie eine Beschreibung von
Ihrer Maske (vergleichbar mit einer <FORM ..>-HTML-Datei)
übergeben können. Weil QuickBASIC ja keine objektorientiert Sprache
ist, bei welcher Sie eine Basisklasse Bedienelement erstellen
können und davon TextFeld, CheckBox,
Radioknopfgruppe usw. mit
Hilfe sog. Vererbung erstellen
können, müssen Sie mit Hilfe von Feldvariablen und eigenen
Typen arbeiten. Beim obigen, sogar noch in GWBASIC.EXE
geschriebenen Programm löste ich dies mit mehreren
Maskendatenbeschreibungsvariablen DB%(), GK!() und S$(). Diese Variablen
mussten für das Unterprogramm vorbereitet übergeben werden zusammen
mit einem Startindex. Die »Sprache« sah dann in etwas so aus:
Zuerst holte man sich das erste (Startindex!) Element aus DB%() und fand dann einen Bedienelementcode vor:
| Code | Bedeutung |
|---|---|
| 0 | Textfeld freier String |
| 1 | Textfeld für Ganzzahl |
| 2 | Textfeld für Gleitkommazahl |
| 16 | Radio-Auswahlmenü horizontal |
| 17 | Checkbox-Auswahlmenü horizontal |
| 32 | Radio-Auswahlmenü vertikal |
| 33 | Checkbox-Auswahlmenü vertikal |
| -1 | Ende des Maskensystems |
| -2 | neue Seite, auf die mit BildAuf/BildAb geblättert werden kann |
In der Implementation von BildschirmMaske gehört eine
entsprechende WHILE db%(AktCod%) <> -2-Schleife hinein
zusammen mit einem SELECT CASE-Block für die obige Tabelle.
In db%() folgt nach diesem Code jeweils eine
Beschreibung der zusätzlichen Daten. Als Beispiele nehme ich ein Textfeld
für Ganzzahlen:
| Variable | Bedeutung |
|---|---|
| db%(AktCod%) | Kennung 1 |
| db%(AktCod%+1) | Zeile für LOCATE |
| db%(AktCod%+2) | Spalte für LOCATE Beginn
Eingabetextfeld |
| db%(AktCod%+3) | Index auf s$() mit Prompttext |
| s$(db%(AktCod%+3)) | Prompttext, ASCII-Wert vom 1. Zeichen stellt die Position des hervorgehobenen Buchstabens dar: A=1, B=2 usw. |
| s$(db%(AktCod%+3)+1) | Puffer mit Eingabewert als t$ dem Unterprogramm EditiereZeile zu
übergeben |
| db%(AktCod%+4) | kleinster erlaubter Wert für die Eingabe |
| db%(AktCod%+5) | grösster erlaubter Wert für die Eingabe |
| db%(AktCod%+6) | vom Benutzer effektiv eingegebener Wert |
Beispiel für ein Radioknopf-Menü:
| Variable | Bedeutung |
|---|---|
| db%(AktCod%) | Kennung 32 |
| db%(AktCod%+1) | Zeile für LOCATE |
| db%(AktCod%+2) | Spalte für LOCATE, Lage
des Häkchen |
| db%(AktCod%+3) | Index auf s%() mit Prompttext |
| s$(db%(AktCod%+3)) | Prompttext, z.B. »Papierausrichtung« |
| s$(db%(AktCod%+3)+1) .. s%(db%(AktCod%+3)+n) | Liste der Texte, z.B. »Hochformat«, »Querformat«, ASCII-Wert vom 1. Zeichen stellt die Position des hervorgehobenen Buchstabens dar |
| db%(AktCod%+4 | Anzahl Optionen n |
| db%(AktCod%+5 | aktuelle Option (0..n-1) |
Auf den ersten Blick sieht es recht kompliziert aus, aber in Wirklichkeit
geht die Ausprogrammierung eines BildschirmMaske-Unterprogramms
recht einfach und effizient, so dass Sie am Schluss mit einem wirklich sehr
flexiblen und leistungsfähigen Software-Baustein für Ihre Projekte
belohnt werden :-). Wichtig ist dabei am
Schluss die Erhöhung von AktCod% innerhalb von
jedem CASE-Block, da dort in db%() die
Beschreibung des nächsten Bedienelementes folgt.
Aber auch das Hauptprogramm wird insgesamt betrachtet, recht einfach:
Nachdem Sie Ihr Menü mit Papier und Bleistift entworfen haben, können
Sie es in Form von DATA-Zeilen gemäss obigen Spezifikationen
beschreiben und eingeben. Somit brauchen Sie lediglich die Felder db%(), gk!() und s$() zu dimensionieren und mit einer
READ-Schleife zu Beginn laden.