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.