|
Formularentwurf: Gutes Layout und nutzerfreundliche Funktion
|
| Autor |
Nachricht |
astern
Datenmodell-Missionar

Verfasst am: 17. Dez 2009, 20:59 Rufname: Andreas
Wohnort: Rastede
|
| |
| Version: (keine Angabe möglich) |
|
Hallo!
Ich wundere bzw. ärgere mich immer wieder, wenn ich ungenügend durchdachte und damit benutzerunfreundliche Formulare sehe. Da ich mir selber schon vor längerer Zeit eine Art Style-Guide für meine Formulare überlegt habe, möchte ich diesen hier einmal vorstellen. Ich gehe dabei von folgendem Modell aus (siehe Abbildung unten!):
EIN Kunde erteilt MEHRERE Kundenaufträge.
EIN Kundenauftrag ist von EINEM Kunden.
Also besteht eine 1:n-Beziehung zwischen tblKunde und tblKundenauftrag.
Zu EINEM Kundenauftrag gehören MEHRERE Materialarten.
EINE Materialart gehört zu MEHREREN Kundenaufträgen.
Also besteht eine m:n-Beziehung zwischen tblKundenauftrag und tblMaterialart.
Also brauchen wir eine Zwischentabelle tblKauf_Mat.
Es soll jetzt ein Formular zur Auftragsbearbeitung entwickelt werden. Den ganzen Entwicklungsprozess kann ich hier aus Platzgründen nicht beschreiben. Dazu gibt es in der Accessdatei, die ich unten zum Download anbiete, genügend Material. Ich habe dort nämlich jeden einzelnen Zwischenschritt vom allerersten Entwurf bis zum fertigen Formular dokumentiert. Außerdem finden sich dort auch zahlreiche Kommentare im VBA-Quelltext. Ich möchte hier jetzt vielmehr meine Design- und Funktionsprinzipien erläutern (Die Nummern beziehen sich auf das Bild unten!).
(1) Fenstertitel und Formulartitel bilden eine Einheit und sind zusammen zu lesen.
(2) Alle Formularelemente sind bündig und möglichst gleich breit ausgerichtet. Das braucht man nicht von Hand zurechtzufummeln - dafür gibt es in der Entwurfsansicht des Formulars entsprechende Befehle. Die Farben sind dezent gehalten - also nicht blau, rot, grün in einem Formular, sondern Abstufungen einer Farbe – z.B. dunkelblau / mittelblau / hellblau + schwarz und weiß.
(3) Auf dem Formular befinden sich kleine Hinweise zur Bedienung, damit die Software quasi selbsterklärend wird (Wer liest schon noch Handbücher?). Diese Hinweise erscheinen nach einem Click des Nutzers auf den Button mit dem Fragezeichen.
(4) Der Standardbutton zum Schließen des Fensters wird durch einen selbst programmierten Button ergänzt. Beide haben dieselbe Funktion.
(5) Wir Mitteleuropäer lesen von links nach rechts und von oben nach unten. In dieser Weise sollten daher auch die einzelnen Formularelemente angeordnet sein - in der Reihenfolge, in der sie der Benutzer höchstwahrscheinlich brauchen wird. Hier also: Links in der Liste einen Auftrag auswählen, dann rechts daneben die Auftragsdaten bearbeiten und schließlich unten das dazugehörige Material hinzufügen.
Die Liste ist nach Datumswerten absteigend sortiert, d.h. die neuesten Aufträge stehen oben. Das muss in den Listenfeld-Eigenschaften unter Daten / Datensatzherkunft eingestellt werden.
(6) Die Suche wird hier so realisiert: Es gibt Eingabefelder für die Suchkriterien („Datum“, „Nummer“ und „Beschreibung“). Der Nutzer clickt auf „Suchen“ und daraufhin werden in der Liste nur diejenigen Aufträge angezeigt, auf die die Suchkriterien passen. Auch Kombinationen von Suchkriterien sind möglich. Ein Click auf „Alle“ lässt in der Liste wieder alle Aufträge erscheinen.
(7) Fast jedes Formular braucht die drei Standardaktionen "Neu", "Speichern" und "Löschen". Die entsprechenden Buttons
- werden entweder beschriftet oder erhalten ein aussagekräftiges Bildchen
- befinden sich im farblich abgesetzten Fußbereich des Formulars
- werden von links nach rechts in der Reihenfolge angeordnet, in der sie gebraucht werden: Erst "Neu", dann "Speichern" und dann "Löschen".
- befinden sich auf jedem Formular immer in derselben Reihenfolge an der gleichen Stelle ("erwartungskonform"!)
- zeigen, wenn der Mauszeiger einige Sekunden darüber verharrt, einen sog. "SteuerelementTip-Text", damit der Benutzer genau weiß, was der Button bewirkt.
Neu:
Betätigt der Benutzer den "Neu"-Button, so werden die Liste und der "Löschen"-Button deaktiviert. Dadurch wird er gezwungen, auch wirklich das zu tun, was er vorhatte.
Speichern:
Nach dem Click auf den "Speichern"-Button werden die Liste und der "Löschen"-Button wieder aktiviert. Vor dem Speichern muss überprüft werden, ob der Benutzer die Pflichtfelder ausgefüllt hat - in diesem Fall Kunde, Beschreibung und Nummer. Wenn nicht, bekommt der Nutzer eine Warnmeldung.
Nach dem Speichern ist der gerade gespeicherte Datensatz in der Liste markiert. Das geschieht nicht automatisch, sondern muss programmiert werden.
Löschen:
Das Löschen erfolgt erst nach der Rückfrage "Wollen Sie wirklich ...?". Nach dem Löschen ist der erste Datensatz in der Liste markiert. Das geschieht auch nicht automatisch, sondern muss programmiert werden.
Achtung! Trick_17: Der „gelöschte“ Datensatz ist gar nicht gelöscht! Er wird nur in der Tabelle tblKundenauftrag durch den Eintrag des aktuellen Datums in der Spalte kauf_geloescht als „gelöscht markiert“:
| Code: | strSQLtext = "UPDATE tblKundenauftrag" _
& " SET kauf_geloescht=" & _
Format(CDate(Date), "\#mm\/dd\/yyyy\#") _
& " WHERE kauf_id=" & Me!kauf_id
CurrentDb.Execute strSQLtext | Im Formular frmAuftragsbearbeitung und in der darin enthaltenen Liste werden entsprechend nur solche Zeilen der Tabelle tblKundenauftrag dargestellt, die in der Spalte kauf_geloescht keinen Eintrag haben. Dadurch sind „gelöschte“ Daten jederzeit wieder herstellbar!
(8) Laut Datenmodell ist EIN Kundenauftrag von EINEM Kunden. Dieser wird also über ein Kombinationsfeld ausgewählt. Durch die Auswahl eines Kunden im Kombinationsfeld wird dessen Primärschlüssel als Fremdschlüssel in die Tabelle tblKundenauftrag eingetragen. Man beachte die Anzeige im Kombinationsfeld: Normalerweise kann man dort nur den Inhalte EINER Tabellenspalte anzeigen - also z.B. nur den Nachnamen, aber nicht Vorname UND Nachname. Mit einem kleinen Trick gelingt das aber trotzdem. Dazu habe ich die Datensatzherkunft des Kombinationsfeldes so definiert:
| Code: | SELECT kun_id, kun_name & ", " & kun_vorname AS kundenname
FROM tblKunde
ORDER BY kun_name | Noch ein wichtiger Trick: Wenn bei offenem Auftragsbearbeitungs-Formular ein neuer Kunde angelegt wird, so erscheint dieser NICHT automatisch in dem Kombinationsfeld! Dafür muss man durch entsprechende Programmierung selber sorgen. Der folgende Code bewirkt, dass das Kombinationsfeld vor jeder Benutzung zuerst aktualisiert wird:
| Code: | Private Sub cboKunde_GotFocus()
Me.cboKunde.Requery
End Sub | Das Kombinationsfeld wird hier für zweierlei Zwecke genutzt: Zum einen zur ANZEIGE des Kunden, von dem der ausgewählte Auftrag ist; zum anderen aber auch zur EINGABE des Kundennamens bei einem neuen Auftrag. Das hat den Nachteil, dass durch einen unvorsichtigen Click in das Kombinationsfeld ein Auftrag von Meier in einen Auftrag von Schulze verwandelt wird. Dem kann man mit folgendem Code vorbeugen:
| Code: | Private Sub cboKunde_Dirty(Cancel As Integer)
If IsNull(Me.cboKunde.OldValue) Then Exit Sub
If MsgBox("Wollen Sie den Auftraggeber wirklich ändern?", vbYesNo + vbDefaultButton2) = vbNo Then Cancel = True
End Sub | Beim erstmaligen Eintrag von Daten (OldValue=Null) wird nicht nachgefragt.
(9) Es gibt zumindest zwei Gelegenheiten, bei denen der Benutzer vom Auftragsbearbeitungs-Formular zum Kundenformular wechseln möchte:
Erstens: Er möchte sich einfach die Kundendaten noch einmal ansehen.
Zweitens: Der Auftrag ist von einem Kunden, den er noch nicht in seiner DB hat und er möchte ihn jetzt anlegen (Achtung: siehe "Requery-Trick" unter Punkt (8)!)
Damit der Benutzer auf einfache Weise zum Kundenformular kommt, ist die Beschriftung des Kombinationsfeldes als Button ausgelegt. Ein Click auf den Button "Kunde" öffnet das Kundenformular und zeigt auch gleich denjenigen Kunden an, der im Auftragsbearbeitungs-Formular angezeigt wurde. Auch das geschieht nicht automatisch, sondern muss programmiert werden. Der folgende Code übergibt dem Kundenformular den Primärschlüssel des im Kombinationsfeld angezeigten Kunden (=gebundene Spalte des Kombinationsfeldes):
| Code: | Private Sub cmdKunde_Click()
DoCmd.OpenForm "frmKunden", , , , , , Str(Me.cboKunde)
End Sub | Das Öffnungsargument Str(Me.cboKunde) wird dann in der Form_Open-Prozedur des Kundenformulars benutzt, um den Kunden aus dem Kombinationsfeld des Auftragsbearbeitungs-Formulars im Kundenformular anzuzeigen:
| Code: | | Me!lstKunden = OpenArgs |
(10) Die Gestaltung des Unterformulars ist ein Stück Geheimwissenschaft. Dazu lohnt sich wieder mal ein Blick auf's Datenmodell. Aus der m:n-Beziehung zwischen Kundenauftrag und Materialart hat sich ergeben, dass wir eine sog. "Zwischentabelle" tblKauf_Mat brauchen. Das Datenmodell liest sich also jetzt so:
Zu EINEM Kundenauftrag gehören MEHRERE kauf_mat.
Was ist ein "kauf_mat"?? Nun, das ist zunächst mal eine künstliche Tabelle, die sich aus datenbanktechnischen Zwängen ergibt. Man könnte sie aber auch als "Materiallistenzeile" interpretieren. (Achtung: NICHT als "Materialliste"!! Das würde ja bedeuten, dass zu EINEM Kundenauftrag MEHRERE Materiallisten gehören!)
Also: Zu EINEM Kundenauftrag gehören MEHRERE Materiallistenzeilen und zu EINER Materiallistenzeile gehört wiederum EINE Materialart. Der Datenbankentwickler denkt also etwas verquer! Der "gesunde Menschenverstand" sagt uns: "Zu EINEM Kundenauftrag gehören MEHRERE Materialarten; und zwar jeweils eine bestimmte Menge". Der Datenbankentwickler muss sein Denken aber streng am Datenmodell ausrichten - und das sagt ihm: "Zu EINEM Kundenauftrag gehören MEHRERE Materiallistenzeilen (=Mengen!) und zu jeder Materiallistenzeile gehört wiederum EINE Materialart".
Und so entwickelt er auch sein Unterformular: Es ist zunächst mal eine Liste der Zeilen aus der Tabelle tblKauf_Mat (= Unterformular vom Typ „Endlosformular“). Zu jeder einzelnen Zeile gehört laut Datenmodell EINE Materialart. Also fügen wir ein Kombinationsfeld ein, mit dessen Hilfe wir die Materialart auswählen können. Das funktioniert wieder genau so, wie das Kombinationsfeld cboKunde im Auftragsbearbeitungs-Formular: Durch den Click in das Kombinationsfeld cboMaterialart wählen wir einen mat_id aus (=gebundene Spalte des Kombinationsfeldes!) und tragen ihn als Fremdschlüssel in die Tabelle tblKauf_Mat ein.
Wenn das Unterformular fertig ist, fügen wir es in das Hauptformular ein. Dabei ist auf die richtige Verknüpfung zwischen Haupt- und Unterformular zu achten (im zweiten Schritt des Unterformular-Assistenten: „Verknüpfen von: kauf_id_f“ – „Verknüpfen nach: kauf_id“)).
(11) Wo man etwas hinzufügen kann, muss generell auch die Möglichkeit bestehen, es wieder wegzunehmen. Darum gibt es in jeder Zeile des Unterformulars einen "Entf."-Button. Er macht folgendes:
| Code: | strSQLtext = "DELETE FROM tblKauf_Mat" _
& " WHERE kaufmat_id=" & Str(Me!kaufmat_id)
'MsgBox strSQLtext
CurrentDb.Execute strSQLtext
Me.Requery | - natürlich erst nach Rückfrage beim Benutzer! Man beachte wieder eine Kleinigkeit beim Fenster "Wollen Sie wirklich ...?": Dort ist der "Nein"-Button etwas stärker umrandet. Dadurch wird der sog. "DefaultButton" markiert. Er wird betätigt, wenn der Benutzer nicht mit der Maus clickt, sondern einfach auf die Enter-Taste drückt. Dadurch sollte immer die ungefährlichere der beiden Alternativen ausgelöst werden. Dieses Verhalten wird durch "vbDefaultButton2" in "Private Sub cmdLoeschen_Click()" programmiert.
Noch ein Tipp für die VBA-Entwicklung: Wenn man ein SQL-Statement ausführen will, sollte man es sich erst einmal zur Sichtkontrolle anzeigen lassen. Den entspr. Befehl („MsgBox sqltext“) sollte man nicht löschen, sondern mit Kommentarzeichen stehen lassen. Wer weiß, wofür man ihn noch einmal gebrauchen kann!
MfG
A*
PS: Ich habe dieses Tutorial erstmals am 18.8.2009 hier gepostet. Daraufhin bekam ich einige sehr gute Hinweise von derArb und WilliWipp und etwas später auch noch von Bitsqueezer. Großes Dankeschön an alle drei! Dies ist jetzt die komplett überarbeitete Version, in die fast alle Hinweise eingearbeitet wurden! Den alten Text habe ich gelöscht.
_________________ 1. Gebot: Du sollst lange und gründlich über Dein Datenmodell nachdenken!
2. Gebot: Du sollst keine Formulare erstellen ohne gutes Datenmodell!
| Beschreibung: |
|
| Dateigröße: |
68.72 KB |
| Angeschaut: |
1146 mal |

|
| Beschreibung: |
| Das Musterformular (Die Nummern beziehen sich auf den obigen Text.) |
|
| Dateigröße: |
305.87 KB |
| Angeschaut: |
1146 mal |

|
| Beschreibung: |
| Die Beispieldatenbank (2003er Format) |
|
 Download |
| Dateiname: |
formular-styleguide-astern-v06.zip |
| Dateigröße: |
252.93 KB |
| Heruntergeladen: |
165 mal |
| Beschreibung: |
| Der ganze Text nochmal als .pdf |
|
 Download |
| Dateiname: |
formular-styleguide-astern-v06.pdf |
| Dateigröße: |
239.74 KB |
| Heruntergeladen: |
141 mal |
|
|
Du kannst Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen. Du kannst Dateien in diesem Forum nicht posten Du kannst Dateien in diesem Forum herunterladen
|
| |

|
|
| Verwandte Themen |
| Forum / Themen |
Antworten |
Autor |
Aufrufe |
Letzter Beitrag |
 |
Access Programmierung / VBA: Funktion soll Collection zurückgeben |
2 |
Z.Mart |
468 |
18. Jun 2009, 14:42 Z.Mart  |
 |
Access Formulare: Maustaste an Funktion übergeben |
3 |
prello |
116 |
17. Jun 2009, 16:44 MAPWARE  |
 |
Access Tabellen & Abfragen: Vergleiche und Min Funktion |
6 |
ZENN |
112 |
09. Jun 2009, 16:49 MiLie  |
 |
Access Programmierung / VBA: Kompillierungsfehler - Sub oder Funktion nicht definiert |
2 |
knosim |
591 |
23. Apr 2009, 14:36 knosim  |
 |
Access Programmierung / VBA: eval funktion in rich text form. Feld |
3 |
bull1234 |
413 |
26. Jan 2009, 12:03 Nouba  |
 |
Access Programmierung / VBA: Mehrwertsteuer als Funktion |
27 |
jomei |
946 |
02. Jan 2009, 18:09 jomei  |
 |
Access Tabellen & Abfragen: Abfrage mit VBA Funktion nicht mehr in Word-Serienbrief da |
2 |
2000ede |
519 |
18. Dez 2008, 14:24 Willi Wipp  |
 |
Access Programmierung / VBA: instr Funktion; Rückgabewert |
2 |
Illuminati |
1106 |
18. Nov 2008, 07:54 Illuminati  |
 |
Access Tabellen & Abfragen: Funktion in Abfrage verwenden |
1 |
Ben321 |
1249 |
16. Sep 2008, 12:02 SGast  |
 |
Access Programmierung / VBA: Funktion für verschiedene Texbausteine (O2007) |
4 |
badenlover |
319 |
07. Mai 2008, 17:44 badenlover  |
 |
Access Programmierung / VBA: Eigene Funktion für SQL Insert funktioniert nicht |
4 |
Matt1981 |
1476 |
05. Feb 2008, 09:37 Matt1981  |
 |
Access Programmierung / VBA: SQL-Anfügeabfrage aus einer Funktion |
1 |
marcap |
537 |
13. Mai 2007, 12:04 marcap  |
| |
|