IM UNTERNEHMEN RIBBON-KLASSEN SCHNELLER FILTER

Größe: px
Ab Seite anzeigen:

Download "IM UNTERNEHMEN RIBBON-KLASSEN SCHNELLER FILTER"

Transkript

1 Ausgabe 01/2017 ACCESS RIBBON-KLASSEN Programmieren Sie das Ribbon komplett ohne den Einsatz von XML und Callback-Funktionen (ab S. 8). In diesem Heft: TICKETSYSTEM, TEIL III Weiter geht es mit der Ticketverwaltung diesmal mit den Schritten nach Eingang eines Tickets. SCHNELLER FILTER Lernen Sie eine Lösung kennen, mit der Sie mit minimalem Aufwand Datenblätter nach ihren Inhalten filtern. ACCESS PER URL STARTEN Starten Sie eine Access-Anwendung vom Browser aus und übergeben Sie die benötigten Argumente für Formulare und Co. SEITE 65 SEITE 32 SEITE 47 Mat-Nr. H

2 ACCESS INHALTSVERZEICHNIS/IMPRESSUM ERGONOMIE UND BENUTZEROBERFLÄCHE Eigene Ribbons ohne Code 2 Objektorientierte Ribbon-Programmierung 8 ABFRAGEN UND SQL Sichere Filterausdrücke 20 FORMULARE UND STEUERELEMENTE Lookup-Kombinationsfelder nach Texten filtern 26 Schneller Filter 32 INTERAKTIV Access per URL starten 47 SQL SERVER UND CO. RDBMS-Zugriff per VBA: Daten bearbeiten 51 LÖSUNGEN Ticketsystem, Teil III 65 SERVICE Impressum U2 Editorial 1 DOWNLOAD Die Downloads zu dieser Ausgabe finden Sie wie gewohnt unter: Benutzername: ribbon Kennwort: klassen Die Direktlinks zu den Downloads befinden sich am unteren Rand des jeweiligen Beitrags. Impressum ISBN ISSN: Mat.-Nr Access im Unternehmen Haufe-Lexware GmbH & Co. KG, Munzinger Str. 9, Freiburg Kommanditgesellschaft, Sitz Freiburg Registergericht Freiburg, HRA 4408 Komplementäre: Haufe-Lexware Verwaltungs GmbH, Sitz Freiburg, Registergericht Freiburg, HRB 5557; Martin Laqua Geschäftsführung: Isabel Blank, Markus Dränert, Jörg Frey, Birte Hackenjos, Randolf Jessl, Markus Reithwiesner, Joachim Rotzinger, Dr. Carsten Thies Beiratsvorsitzende: Andrea Haufe Steuernummer: 06392/11008 Umsatzsteuer-Identifikationsnummer: DE Redaktion: Dipl.-Inform. (FH) Michael Forster, (Chefredakteur, V.i.S.d.P.), Dipl.-Ing. André Minhorst (Chefredaktion, extern), Sabine Wißler (Redaktionsassistentin), Rita Klingenstein (sprachl. Lektorat), Sascha Trowitzsch (Fachlektorat) Autor: Dipl.-Ing. André Minhorst, Sascha Trowitzsch Anschrift der Redaktion: Postfach , Freiburg, Tel. 0761/898-0, Fax: 0761/ , computer@haufe.de, Internet: Druck: Schätzl Druck & Medien GmbH & Co. KG, Donauwörth Auslieferung und Vertretung für die Schweiz: H.R. Balmer AG, Neugasse 12, CH-6301 Zug, Tel. 041/ , Fax: 041/ Das Update-Heft und alle darin enthaltenen Beiträge und Abbildungen sind urheberrechtlich geschützt. Jede Verwertung, die nicht ausdrücklich vom Urheberrechtsgesetz zugelassen ist, bedarf der vorherigen Zustimmung des Verlags. Das gilt insbesondere für Vervielfältigungen, Bearbeitungen, Übersetzungen, Mikroverfilmung und für die Einspeicherung in elektronische Systeme. Wir weisen darauf hin, dass die verwendeten Bezeichnungen und Markennamen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen. Die im Werk gemachten Angaben erfolgen nach bestem Wissen, jedoch ohne Gewähr. Für mögliche Schäden, die im Zusammenhang mit den Angaben im Werk stehen könnten, wird keine Gewährleistung übernommen. Seite U2

3 EDITORIAL ACCESS Ribbon ohne XML Mit der Einführung des Ribbons unter Access 2007 hat Microsoft ein neues Menüsystem präsentiert. Allerdings ohne ein einziges Tool zu liefern, mit dem sich das Ribbon programmieren lässt. Mittlerweile gibt es ein paar Tools auf dem Markt, zum Beispiel den Ribbon-Admin. Noch cooler wäre es allerdings, wenn man komplett ohne Tools auskommen könnte. Wie das gelingt, zeigen wir Ihnen in dieser Ausgabe von Access im Unternehmen. Dazu finden Sie gleich zwei Beiträge im aktuellen Heft. Unter dem Titel Eigene Ribbons ohne Code ab S. 2 finden Sie eine Möglichkeit, mit der Sie das Ribbon in einem begrenzten Umfang anpassen können ohne dass Sie die Tabelle USysRibbons zum Speichern der Ribbon- Definition anlegen müssen. Der zweite Beitrag zum Thema heißt Ribbonklassen (ab S. 8) und stellt eine Reihe von Klassenmodulen vor, die das Programmieren des Ribbons komplett per VBA ermöglichen. Damit erhalten Sie ein Objektmodell, mit dem Sie die gängigsten Ribbon-Steuerelemente abbilden können, in diesem Fall das Tab-, Group-, Button- und Separator- Steuerelement. Und das mit Ereignisprozeduren, wie Sie sie von Formularen und Steuerelementen her kennen und natürlich inklusive der Anzeige von Bilddateien als Icons. Wer schon einmal Webanwendungen entwickelt hat, die eine Datenbank verwenden und beispielsweise ein Suchformular enthalten, kennt den Begriff SQL-Injection. Dabei versucht der Benutzer, die eigentlich für eine Suche verwendete SQL-Anweisung zu manipulieren, indem er dieser zusätzliche Elemente anhängt. Das gelingt in eingeschränktem Maße auch unter Access zumindest lassen sich so Daten aus anderen Tabellen ermitteln, die möglicherweise nicht für den Benutzer bestimmt sind. Der Beitrag Sichere Filterausdrücke zeigt ab S. 20 einige Beispiele dafür, wie Sie Suchtextfelder dazu nutzen können, die angezeigten Ergebnisse zu manipulieren. Natürlich erfahren Sie dort auch, wie Sie Ihre Anwendung gegen solche Manipulationen schützen. Wenn Sie mit Datenblättern arbeiten, filtern oder sortieren Sie diese sicher oft mit den dafür vorgesehenen Menüs, die sich über einen Klick auf das Symbol rechts in den Spaltenköpfen anzeigen lassen. Eine Funktion, mit der Sie nach dem aktuell markierten Inhalt in einem Feld im Datenblatt filtern können, fehlt allerdings noch. Kein Problem: Wir rüsten diese nach und zeigen Ihnen, wie die Technik dahinter aussieht. Das und mehr lesen Sie im Beitrag Schneller Filter ab S. 32. Passend dazu können Sie nun auch die Inhalte von Nachschlagefeldern im Datenblatt zum Filtern der Inhalte heranziehen: Dazu markieren Sie einfach den kompletten Text oder nur den Ausschnitt, nach dem Sie suchen wollen. Ein Klick auf eine Schaltfläche liefert dann die gewünschten Daten. Mehr dazu lesen Sie unter Lookup-Kombinationsfelder nach Text filtern ab S. 26. In weiteren Beiträgen erfahren Sie zudem, wie Sie Access vom Browser aus durch die Eingabe einer speziellen URL starten können (Access per URL starten, ab S. 47). Außerdem stellen wir Ihnen die Möglichkeiten vor, um per VBA schreibend auf die Daten einer SQL Server-Datenbank zuzugreifen (RDBMS per VBA: Daten bearbeiten, ab S. 51). Den Abschluss macht eine weitere Folge unserer Reihe Ticketsystem, diesmal mit der dritten Folge (ab S. 65). Und nun: Viel Spaß beim Lesen! Ihr Michael Forster Seite 1

4 ACCESS ERGONOMIE UND BENUTZEROBERFLÄCHE EIGENE RIBBONS OHNE CODE Eigene Ribbons ohne Code Zum Gestalten benutzerdefinierter Ribbons für Ihre Access-Anwendung gibt es zwei unterschiedliche Lösungen. Die eine setzt vollständig auf VBA-Code und die Methode LoadCustomUI, die andere verwendet eine ausgeblendete Tabelle USysRibbons, die Sie jeweils mit den XML-Auszeichnungen für die Anpassungen versehen. Dass jedoch auch noch eine dritte Lösung existiert, ist weitgehend unbekannt. DocUICustomization Recherchieren Sie im Netz nach diesem Begriff, so werden Sie nicht viel Erhellendes über ihn finden. Microsoft hat diese Datenbankeigenschaft nicht dokumentiert und andere Entwickler oder Autoren übersahen ihn bisher offenbar. Tatsächlich aber ist er der Schlüssel zu unserer Lösung. Doch fallen wir nicht gleich mit der Tür ins Haus und sehen uns an, wie sie sich in der Beispieldatenbank RibbonOhne TabelleUndCode. accdb präsentiert! Nach ihrem Start zeigt sie sich wie in Bild 1. Hier sind unter dem neuen Tab Aber Hallo! einige Buttons und ein einfaches Menü untergebracht. Die eingebauten Tabs von Access sind ausgeblendet. Der Tab Datei allerdings ist vorhanden und aktiviert den Backstage- Bereich in gewohnter Weise. Beim Klick auf die Elemente der Hallo-Gruppe öffnen sich Meldungsfenster, damit deutlich wird, dass es hier nicht etwa nur um Optik geht, sondern dass wohl funktionierende Callback-Routinen im VBA-Projekt implementiert sind. Der Code ist hier öffnet automatisch das Modul mdloptionen der Datenbank und gibt die Click-Callback- Prozeduren für die Buttons preis. Irgendwelche Routinen Bild 1: Der minimalistische Ribbon der Beispieldatenbank mit einigen Buttons und einem Menüelement zur Modifikation des Ribbon, etwa per LoadCustomUI, werden Sie hingegen vergeblich suchen. Auch eine Tabelle USysRibbons fehlt und die anderen Tabellen sind gleichfalls unauffällig. Wo also versteckt sich der XML-Code für die Anpassungen? Setzen Sie dazu die folgende Zeile im VBA-Direktfenster ab:? CurrentDb.Properties("DocUICustomization").Value Damit wird tatsächlich der ganze zuständige XML-Code herausgegeben! Er unterscheidet sich nicht weiter von den sonst gebräuchlichen Codes, die Sie in der Tabelle USysRibbons oder über LoadCustomUI einsetzen. Wa rum hüllt sich Microsoft in Schweigen über diese Datenbankeigenschaft? Die mögliche Erklärung folgt. Seite 2

5 ERGONOMIE UND BENUTZEROBERFLÄCHE EIGENE RIBBONS OHNE CODE ACCESS Ribbon-Anpassungen über die Oberfläche Seit Access 2010 haben Sie die Möglichkeit, für eine Datenbank zumindest der Schnellzugriffsleiste eigene Elemente zu spendieren. Öffnen Sie dazu bei geladener Datenbank die Optionen von Access und aktivieren den Eintrag Symbolleiste für den Schnellzugriff. Im rechten Bereich des Dialogs findet sich nun die Liste der Elemente der Schnellzugriffsleiste. Das Kombinationsfeld darüber zeigt den unscheinbaren Eintrag Für alle Dokumente (Standard). Doch hier lässt sich auch die aktuelle Datenbank Für 'xyz.accdb' auswählen! Bei Aktivierung leert sich die Liste, falls die Datenbank noch mit keinen Anpassungen versehen wurde. Bild 2: Exklusive Schnellzugriffselemente für geladene Datenbank Es hat den Anschein, als ob Microsoft Entwicklern die Dokumentation dieser Eigenschaft vorenthält, damit sie der manuellen Anpassung vorbehalten bleibt. Sie können nun aus dem Pool der Befehle links der Datenbank Elemente hinzufügen (s. Bild 2). Nach dem Speichern über OK stehen sie in Zukunft immer für diese Datenbank zur Verfügung. Andere Datenbanken zeigen sie nicht. Ergo müssen diese Anpassungen irgendwo in der Datenbank abgespeichert sein. Und deren Eigenschaft DocUICustomization ist eben dieser Ort. Sie können sich den Inhalt dieser Eigenschaft nach der manuellen Anpassung mit der erwähnten VBA-Zeile ausgeben lassen. Der XML-Code für die abgebildete Anpassung sieht so aus wie in Listing 1. Die Struktur ist die übliche, nur dass hier jedem XML-Tag der Namespace mso vorangestellt ist, was, wie wir noch sehen werden, eigentlich überflüssig ist. <mso:customui xmlns:mso=" <mso:ribbon> <mso:qat> <mso:documentcontrols> <mso:control idq="mso:managereplies" visible="true"/> <mso:control idq="mso:filesendasattachment" visible="true"/> </mso:documentcontrols> </mso:qat> </mso:ribbon> </mso:customui> Listing 1: Der XML-Code für die Schnellzugriffsanpassungen aus der Eigenschaft DocUICustomization Zuweisung an DocUICustomization Statt über den Optionen-Dialog können Sie nun die Anpassung auch per Code vornehmen, indem Sie der Eigenschaft einen eigenen Wert zuweisen. Listing 2 zeigt ein extrem kurzes Beispiel. Die Variable sxml nimmt zunächst den XML-Code entgegen. Über Replace entfernt die nächste Zeile alle mso:-strings aus ihrem Inhalt. Dann wird der geänderte Code der Eigenschaft wieder zugewiesen. Dies wirkt sich noch nicht unmittelbar aus. Wie bei manuellen Anpassungen auch muss die Datenbank geschlossen und wieder geöffnet werden. Es zeigt sich dann, dass das Entfernen der mso:-präfixe keine negativen Auswirkungen auf das Funktionieren der Anpassungen hat. Das ist indessen für den XML-Kenner auch nicht verwunderlich, da nun als Standard-Namespace (xmlns) für das XML in der ersten Zeile das Schema direkt deklariert ist, statt über das Präfix mso. Beides kommt im Prinzip auf das Gleiche heraus. Seite 3

6 ACCESS ERGONOMIE UND BENUTZEROBERFLÄCHE EIGENE RIBBONS OHNE CODE Die spannende Frage ist nun, welche Ribbon-Anpassungen sich alle in Doc- UICustomization abseits der Schnellzugriffsleiste unterbringen lassen. Sub ModifyCustUI() Dim sxml As String sxml = CurrentDb.Properties("DocUICustomization").Value sxml = Replace(sXML, "mso:", vbnullstring) CurrentDb.Properties("DocUICustomization").Value = sxml Was geht, was geht nicht? Bei einer Datenbank, deren Schnellzugriffsleiste Sie noch nicht anpassten, führt der Zugriff auf DocUICustomization zu einem Fehler. Es gibt das Property schlicht noch nicht. Sie können es so per VBA erzeugen: Dim prp As DAO.Property Set prp = CurrentDb.CreateProperty( _ "DocUICustomization", dbtext) Currentdb.Properties.Append prp Dies legt die Eigenschaft nur an, ohne ihr einen Wert zuzuweisen. Stattdessen könnten Sie auch gleich prp. Value vor dem Append einen gültigen Anpassungs- XML-Code verabreichen. Später läuft dies etwa so: Dim prp As DAO.Property Dim sxml As String sxml = "<customui..." Set prp = CurrentDb.Properties("DocUICustomization") prp.value = sxml Der Inhalt von sxml ist nun unsere Spielwiese. Es haben sich über Trial & Error nach unzähligen Versuchen einige Ergebnisse herausgestellt. Der deklarierte Namespace in Listing 1 ist der des XML-Schemas von Office Tatsächlich kann problemlos auch das ab Office 2010 gültige Schema angegeben werden: Immerhin gab es ja in diesem XML-Schema so einige Erweiterungen, wie etwa die Definitionen zum Backstage. Listing 2: Modifizieren des XML-Codes für die Schnellzugriffsanpassungen der aktuellen Datenbank Tatsächlich aber funktionieren, wie sich herausstellte, bei Anpassungen über DocUICustomization leider eine ganze Menge Dinge nicht: Der Backstage kann nicht angesprochen werden. Zwar können Sie im XML-Code anstandslos alle hierfür gültigen Auszeichnungen unterbringen, Access ignoriert diese jedoch geflissentlich. Dasselbe gilt für die Schnellzugriffsleiste. Hier kann nur die für die Anwendung vorgesehene Anpassung (documentcontrols) vorgenommen werden, nicht aber eine Modifikation der generellen Elemente (sharedcontrols). Callback-Funktionen werden samt und sonders ignoriert, auch wenn sie angegeben sind. Damit entfällt eine dynamische Steuerung des Ribbons. Callbacks wie onload, getvisible, getimage et cetera bleiben außen vor. Die einzige mögliche Callback-Funktion ist onaction, die Sie für Buttons angeben können. Die Funktion muss in Ihrem VBA-Modul allerdings eine von den üblichen onaction-prozeduren abweichende Syntax aufweisen. Später mehr dazu. startfromscratch steht automatisch immer auf False. Ändern können Sie dies nicht. Dadurch können Sie keinen Ribbon von Grund auf neu erstellen, sondern den eingebauten nur modifizieren. Immerhin kann das Attribut visible für alle Tabs auf False gesetzt werden, wodurch alle eingebauten Ribbon- Tabs verschwinden. Nicht gilt dies allerdings für den Datei-Tab. Der lässt sich ja generell auch über andere Seite 4

7 ERGONOMIE UND BENUTZEROBERFLÄCHE EIGENE RIBBONS OHNE CODE ACCESS Methoden nicht entfernen. Da hier jedoch auch der Backstage von XML-Steuerungen ganz unbeeindruckt bleibt, müssen Sie bei DocUICustomization mit dem vollständigen Datei-Menü leben. Da Sie Callbacks nicht einsetzen können, sind eigene Icons etwa für Schaltflächen nicht möglich. Sie sind hier auf die in Access eingebauten imagemso-bildchen beschränkt. Indirekt führt der Wegfall der Callbacks zu weiteren Einschränkungen. Ein combobox-element etwa lässt sich so nicht auswerten, da dessen item-elemente keine onaction-attribute erlauben. Der Wert des combobox-elements wird in der Regel über sein Callback onchange ausgelesen. Ähnliches gilt für die Elemente checkbox, togglebutton, editbox, drop- Down. Zusammengefasst kann man festhalten, dass diese Einsatzbereiche für Ribbons über DocUICustomization funktionieren: Ausblenden einzelner oder aller eingebauten Tabs. Ebenso können einzelne Elemente selektiv entfernt werden. Anlegen neuer Tabs und Gruppen. Als Elemente innerhalb dieser kommen zunächst Buttons infrage. Das Element button wiederum kann nur in diesen Elternelementen vorkommen: documentcontrols, menu, gallery, group, buttongroup, box. Letztere drei sind lediglich Container zur optischen Gruppierung der Buttons. Mit menu und gallery stehen immerhin zwei Elemente bereit, über die sich Dropdown-Schaltflächen realisieren lassen. Zur Gestaltung lassen sich praktisch nur die Attribute label, size, imagemso, screentip, supertip und keytip einsetzen. Die Attribute visible und enabled ergeben wenig Sinn. Ein ausgetüftelter Ribbon, wie über USysRibbons oder LoadCustomUI erreichbar, lässt sich damit also nicht gestalten. Für einfachere Zwecke sind die Möglichkeiten jedoch ausreichend. Schließlich boten die Menüs und Symbolleisten älterer Office-Versionen ja auch nicht viel mehr... XML-Code der Beispieldatenbank Das lange Listing der folgenden Seite (s. Listing 3) verdeutlicht, wie der XML-Code für DocUICustomization zusammengesetzt wird. Zu der Variablen sxml wird in der Routine SetRibbonCust zeilenweise ein XML-Tag addiert. Der Code wurde über die MZ-Tools für VBA durchnummeriert. Das ist deshalb nützlich, weil sich gerne Fehler in die Auszeichnungen einschleichen. Beim Laden der Datenbank meldet Access dann Fehler, die sich auf eine bestimmte XML-Zeile und -Position beziehen. Über die Nummerierung ist die betreffende Zeile dann schnell ausgemacht. Dazu muss allerdings jede XML-Zeile mit einem Umbruch enden. Das wird dadurch erreicht, dass im Code vor jedem abschließenden Anführungszeichen ein Semikolon eingefügt ist, welches über die Replace- Anweisung unten schließlich in einen Zeilenumbruch (vbcrlf) umgewandelt wird. Die hier nicht abgebildete Hilfsprozedur SetDBProperty schließlich weist der Eigenschaft DocUICustomization den Inhalt von sxml zu. Nach dem Schließen der Datenbank ist die Eigenschaft gültig gespeichert. Bei erneutem Öffnen präsentiert sie sich, wie eingangs demonstriert. Aus dem Modul können Sie nun den Anpassungs-VBA- Code entfernen und lassen nur die Callback-Funktionen für die Buttons übrig. Einmal gesetzt, bleibt der XML- Code auf ewig in der Datenbank gespeichert. Übrigens zeigt sich ein etwas merkwürdiges Verhalten beim Schließen und Öffnen der Datenbank. Öffnen Sie sie im Backstage unmittelbar nach dem Schließen, so scheint die Ribbon-Anpassung außer Kraft gesetzt zu Seite 5

8 ACCESS ERGONOMIE UND BENUTZEROBERFLÄCHE EIGENE RIBBONS OHNE CODE Sub SetRibbonCust() Dim sxml As String 10 sxml = sxml & "<customui xmlns=' 20 sxml = sxml & "<ribbon>;" 30 sxml = sxml & "<qat>;" 40 sxml = sxml & "<sharedcontrols>;" 50 sxml = sxml & "<control idq='filesave' enabled='false' visible='false'/>;" 60 sxml = sxml & "<control idq='fileclose' enabled='false' visible='false'/>;" 70 sxml = sxml & "<control idq='fileopendatabase' enabled='false' visible='false'/>;" 80 sxml = sxml & "</sharedcontrols>;" 90 sxml = sxml & "<documentcontrols>;" 100 sxml = sxml & "<control idq='filecompactandrepairdatabase' screentip='komprimiere mich 1!'/>;" 110 sxml = sxml & "</documentcontrols>;" 120 sxml = sxml & "</qat>;" 130 sxml = sxml & "<tabs>;" 140 sxml = sxml & "<tab idmso='tabhomeaccess' visible='false'/>;" 150 sxml = sxml & "<tab idmso='tabcreate' visible='false'/>;" 160 sxml = sxml & "<tab idmso='tabexternaldata' visible='false'/>;" sxml = sxml & "<tab idmso='tabsourcecontrol' visible='false'/>;" 210 sxml = sxml & "<tab id='tabtest' label='aber Holla' visible='true'>;" 220 sxml = sxml & "<group id='testgruppe2' label='holla-gruppe' visible='true' autoscale='true'>;" 230 sxml = sxml & "<button id='testfunktion1' label='holla-testfunktion'" & _ " imagemso='visibilityvisible' onaction='testfunktion1'/>;" 240 sxml = sxml & "button id='testfunktion2' label='holla-noch eine...'" & _ " imagemso='happyface' onaction='testfunktion2'/>;" 250 sxml = sxml & "button id='testfunktion3' label='der Code ist hier...'" & _ " imagemso='visualbasic' onaction='testfunktion3'/>;" 270 sxml = sxml & "<menu id='men1' label='hier was auswaehlen!' imagemso='chartplotarea' itemsize='normal'>;" 280 sxml = sxml & "<button id='menbtn1' imagemso='framecreatebelow' label='menueintrag 1' onaction='fumenu1'/>;" 290 sxml = sxml & "<button id='menbtn2' imagemso='framecreateleft' label='menueintrag 2' onaction='fumenu2'/>;" 300 sxml = sxml & "<menuseparator id='sep1'/>;" 310 sxml = sxml & "<button id='menbtn3' imagemso='framecreateright' label='menueintrag 3' onaction='fumenu3'/>;" 320 sxml = sxml & "</menu>;" 330 sxml = sxml & "</group>;" 340 sxml = sxml & "</tab>;" 350 sxml = sxml & "</tabs>;" 360 sxml = sxml & "</ribbon>;" 370 sxml = sxml & "</customui>;" sxml = Replace(sXML, ";", vbcrlf) If SetDbProperty("DocUICustomization", sxml) Then MsgBox "Ribbon Customization set" _ Else MsgBox "Ribbon Customization failed" Listing 3: Hauptfunktion zum Leeren der Eigenschaft BillingInformation der Elemente eines Folders Seite 6

9 ERGONOMIE UND BENUTZEROBERFLÄCHE EIGENE RIBBONS OHNE CODE ACCESS Bild 3: Erst beim Umschalten der Ribbon-Tabs aus dem Backstage verschwinden die Anpassungen sein der eingebaute Ribbon erscheint. Bild 3 verdeutlicht das. Oben wurde die Datenbank gerade geschlossen. Dennoch zeigt der zweite Tab die Beschriftung Aber holla. Klicken Sie auf diesen, so verschwindet die Anpassung aber augenblicklich und wechselt in das eingebaute Menüband (untere Abbildung). Erst danach funktioniert DocUICustomization bei neuem Laden der Datenbank wieder. Offenbar benötigt Access erst den Ausstieg aus dem Backstage-Bereich, um die Ribbon- Tabs zu aktualisieren. Allerdings zeigt die Schnellzugriffsleiste für die Aktuelle Datenbank in den Optionen für unser Beispiel eine Anpassungszeile für einen Button. Löscht man diesen manuell, so verschwindet nicht nur er, sondern das Property DocUICustomization komplett! Zu allem Überfluss waren bei uns danach sogar sämtliche Tabs auch in anderen Datenbanken scheinbar verschwunden. Ein Neustart von Access ändert daran nichts! Sie müssen in einem solchen Fall die Tabs in den Optionen wieder manuell aktivieren sie waren auf unsichtbar gestellt worden, was man als Bug bezeichnen könnte, hätte Microsoft die Eigenschaft denn dokumentiert... Natürlich können Sie die Eigenschaft auch über eine externe XML-Datei einstellen, statt über das Zusammenstückeln im Code. Listing 4 zeigt, wie Sie das gegebenenfalls anstellen. Zu den Vorteilen der Lösung gehört die Tatsache, dass der Custom-Ribbon nicht etwa deaktiviert werden kann, indem beim Start der Datenbank die SHIFT-Taste gedrückt wird. Zwar gelingt dies auch bei Ribbon-Anpassungen per Tabelle USysRibbons nicht, doch da muss in den Optionen der Aktuellen Datenbank ein in der Tabelle enthaltener Ribbon unter Name des Menübands ausdrücklich spezifiziert sein. Löscht man diesen Eintrag, so zeigt sich auch kein angepasster Ribbon. Bei DocUICustomization dagegen kann dieser Eintrag leer bleiben. Also gibt es da auch nichts zum Löschen. Sub SetRibbonCustFromFile() Dim sxml As String Open CurrentProject.Path & _ "\ribbon.xml" For Binary As #1 sxml = String(LOF(1), 0) Get #1,, sxml Close #1 If SetDbProperty("DocUICustomization", sxml) Then MsgBox "Ribbon Customization set" Else MsgBox "Ribbon Customization failed" End If Listing 4: Setzen von DocUICustomization über eine XML-Datei Seite 7

10 ACCESS ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG Objektorientierte Ribbon-Programmierung Das Ribbon lässt sich normalerweise nur definieren, indem Sie den XML-Code zur Beschreibung des Ribbons festlegen, in einer bestimmten Tabelle speichern und die dortigen Einträge dann als Anwendungsribbon nutzen oder bestimmten Formularen oder Berichten zuweisen. Mit hier vorgestellten Klassen können Sie das Ribbon von nun an ganz einfach per VBA zusammenstellen und etwa Formularen oder Berichten zuweisen. Wie funktioniert das nun ein Ribbon anzuzeigen, ohne die Tabelle USysRibbons mit Ribbon-Definitionen zu füllen beziehungsweise gänzlich ohne statisch definierte Ribbons? Grundsätzlich ist das ganz einfach: Sie benötigen lediglich einige Objekte aus der Beispieldatenbank zu diesem Beitrag, die Sie über die Importieren-Funktion in die Datenbank importieren, die Sie mit Ribbons ausstatten möchten. Dabei handelt es sich um die folgenden Objekte: clsstartribbon: Klasse, in der Sie das Hauptribbon einer Anwendung definieren Ribbons: Klasse, mit der Sie die Ribbons einer Anwendung verwalten clsribbon: Klasse, die die Eigenschaften und Ereignisse des Ribbon-Elements bereitstellt clsbutton: Klasse, welche die Funktionen des Button- Elements kapselt clsseparator: Klasse, welche die Eigenschaften eines Separator-Elements kapselt mdlribbons: Stellt einige globale Variablen bereit sowie die Funktionen zur Anzeige von Bilddateien mdlribbonenums: Modul, das einige Enumerationen enthält mdltools: Enthält einige allgemeine Funktionen wie etwa zum Ermitteln von GUIDs oder zum Erstellen von formatierten XML-Dokumenten mdlzwischenablage: Enthält Funktionen, um Daten in die Zwischenablage zu kopieren und von dort abzufragen clstabs: Klasse, welche die Tabs verwaltet clstab: Klasse, welche die Eigenschaften des Tab- Elements bereitstellt clsgroups: Klasse, welche die Gruppen eines Tab- Elements verwaltet clsgroup: Klasse, welche die Eigenschaften und Ereignisse des Group-Elements bereitstellt clscontrols: Klasse, welche die Steuerelemente innerhalb einer Gruppe verwaltet Bild 1: Verweis auf die Office-Bibliothek Seite 8

11 ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG ACCESS Außerdem sind noch drei Schritte nötig: Sie fügen den Verweisen der Anwendung den Eintrag Microsoft Office x.0 Object Library hinzu (s. Bild 1). Sie tragen für die Eigenschaft Name des Menübands in den Access- Optionen den Wert Main ein (s. Bild 2). Bild 2: Option, um das Anwendungsribbon einzustellen Sie aktivieren die Option Fehler von Benutzeroberflächen-Add-Ins anzeigen in den Access-Optionen (s. Bild 3). Ribbons anzeigen Zum Anzeigen von Ribbons gibt es auf herkömmlichem Wege zwei Möglichkeiten: Entweder Sie tragen den Namen der gewünschten Ribbon-Definition, die Sie in der Tabelle USysRibbons gespeichert haben, für die oben genannte Eigenschaft in den Access-Optionen ein dann wird das entsprechende Ribbon gleich beim Anwendungsstart angezeigt. Oder Sie möchten, dass das Ribbon mit einem Formular oder Bericht eingeblendet wird. Dann tragen Sie den Namen des Ribbons für die entsprechende Eigenschaft des Formulars oder Berichts ein. Wir verwenden in der hier vorgestellten Lösung zwar keine Tabelle namens USysRibbons zum Speichern von Ribbon-Definitionen, aber wir können dennoch beide Methoden zum Anzeigen von Ribbons nutzen. Für die erste Methode ist die obige Angabe des Standardribbons nötig, beispielsweise über den Namen Main. Außerdem brauchen Sie ein AutoExec-Makro, das dafür sorgt, dass das Ribbon auch direkt beim Öffnen der Anwendung angezeigt wird. Bild 3: Aktivieren der Anzeige von Ribbon-Fehlern Dieses Makro definieren Sie wie in Bild 4. Der einzige Makrobefehl lautet AusführenCode und ruft die Funktion RibbonLaden Seite 9

12 ACCESS ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG der wir ein Ribbon namens Main definieren, das dann beim Öffnen der Anwendung gleich angezeigt werden soll. Public Sub CreateRibbon()... Bild 4: AutoExec-Makro, das ein Ribbon einblenden soll In dieser deklarieren wir nun zunächst ein Objekt des Typs clsribbon, das unsere Ribbon-Deklaration aufnimmt. Dim objribbon As clsribbon Bild 5: Anpassen der Klasse Ribbons, damit diese nicht instanziert werden muss auf. Diese Funktion ist im Modul mdlribbons definiert und sieht wie folgt aus: Public Function RibbonLaden() Startribbon.CreateRibbon End Function Hier wird also die Methode CreateRibbon eines Objekts namens Startribbon aufgerufen. Wie Sie weiter oben sehen, haben wir die entsprechende Klasse Startribbon bereits in der Beispieldatenbank vorbereitet. Aber wie können wir die Methode CreateRibbon dieser Klasse nutzen, ohne dass wir diese instanzieren? Das ist kein Problem: Wie müssen die Klasse nur einmal in eine Textdatei exportieren und dann einige Eigenschaften, die im VBA-Editor nicht sichtbar sind, anpassen. In Bild 5 haben wird dies für die Klasse Ribbons durchgeführt, die ebenfalls ohne Instanzierung genutzt werden soll. Nun wollen wir in der Klasse StartRibbon eine Methode namens CreateRibbon anlegen, mit Wir wollen als Erstes lediglich ein leeres Tab-Element hinzufügen, um zu zeigen, dass die Klassen auch wirklich ihr Werk verrichten. Dazu deklarieren wir auch noch ein Element des Typs clstab: Dim objtab As clstab Das clsribbon-element füllen wir dann mit einem Objekt, das die Add-Methode der Ribbons-Klasse liefert: Set objribbon = Ribbons.Add("Main") Dieses Objekt besitzt wie das entsprechende Element in der Ribbon-Definition eine Eigenschaft namens Start- FromScratch, die wir auf den Wert True einstellen: Public Sub CreateRibbon() Dim objribbon As clsribbon Dim objtab As clstab Set objribbon = Ribbons.Add("Main") With objribbon.startfromscratch = True Set objtab =.Tabs.Add("Beispieltab") With objtab.label = "Beispieltab" End With End With Ribbons.CreateStartRibbon "Main" Listing 1: Die Methode CreateRibbon füllen Sie mit eigenen Anweisungen. Seite 10

13 ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG ACCESS With objribbon.startfromscratch = True Dann fügen wir dem clsribbon-element mit der Add-Methode der Tabs-Auflistung ein neues Tab-Element hinzu. Den Namen übergeben wir als Parameter: Set objtab =.Tabs.Add("Beispieltab") Für dieses Objekt legen wir die Beschriftung mit der Eigenschaft label fest: With objtab.label = "Beispieltab" End With End With Danach rufen wir die Methode CreateStartRibbon der Klasse Ribbons auf und übergeben dieser den Namen des zu erstellenden Ribbons, also Main: Bild 6: Das erste per VBA-Code erzeugte Ribbon Ribbons.CreateStartRibbon "Main" Die vollständige Fassung dieser Prozedur finden Sie in Listing 1. Anschließend schauen wir uns an, ob diese Art der Ribbon-Definition auch wirklich funktioniert. Dazu schließen Sie die Access- Datei und öffnen diese erneut, damit das Auto- Exec-Makro ausgeführt wird. Es reicht nicht aus, einfach das Makro auszuführen, da das Main-Ribbon möglicherweise zu diesem Public Sub CreateRibbon() Dim objribbon As clsribbon Dim objtab As clstab Dim objgroup As clsgroup Set objribbon = Ribbons.Add("Main") With objribbon.startfromscratch = True Set objtab =.Tabs.Add("Beispieltab") With objtab.label = "Beispieltab" Set objgroup =.Groups.Add("Beispielgruppe") With objgroup.label = "Beispielgruppe" Set objbutton1 =.Controls.Add(msoRibbonControlButton, "Beispielbutton") objbutton1.label = "Button 1" Set objbutton2 =.Controls.Add(msoRibbonControlButton, "Beispielbutton1") objbutton2.label = "Button 2" End With End With End With Ribbons.CreateStartRibbon "Main" Listing 2: Anlegen eines Ribbons, diesmal mit einer Gruppe und zwei Schaltflächen Seite 11

14 ACCESS ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG Zeitpunkt schon installiert wurde und erneutes Anlegen zu einem Fehler führt (dies können Sie durch erneutes Aufrufen des AutoExec- Makros ausprobieren). Beim erneuten Öffnen der Anwendung sollte nun die Ansicht aus Bild 6 erscheinen. Es funktioniert! Gruppen und Schaltflächen hinzufügen Nun gehen wir einen Schritt weiter und wollen dem Tab-Element eine Gruppe hinzufügen und natürlich auch noch Button-Elemente. Die einfachste Variante sieht nun wie in Listing 2 aus. Sie sehen hier, dass wir ein weiteres Objekt des Typs clsgroup mit dem Variablennamen obj- Group deklariert haben. Dieses fügen wir dann über die Add-Methode der Groups-Auflistung des clstab-objekts zum Ribbon hinzu. Auch dieses Objekt hält eine Eigenschaft namens label bereit, mit der Sie die Beschriftung der Gruppe festlegen können. Diese legen wir hier mit dem Ausdruck Beispielgruppe fest. Bild 7: Deklarieren von Button-Elementen und Implementieren ihrer Ereignisse Eigenschaft label die Bezeichnung des Button-Elements fest. Wenn Sie aufgepasst haben, fällt Ihnen auf, dass die Prozedur gar keine Deklaration für die beiden Button- Elemente objbutton1 und objbutton2 enthält. Danach folgen die beiden Button-Elemente: Das erste fügen wir mit der Methode Add der Auflistung Controls des Objekts objgroup hinzu. Dabei geben wir als ersten Parameter den Wert msoribboncontrolbutton an, was bewirkt, dass hier ein Button-Element angelegt wird. Mit dem zweiten Parameter übergeben wir den Namen dieses Button-Elements. Danach legen wir wiederum mit der Das hat folgenden Grund: Unter anderem soll die Verwendung dieser Lösung dafür sorgen, dass Sie richtige Ereignisprozeduren für die Steuerelemente des Ribbons hinterlegen können. Das heißt, dass Sie die Ribbon- Elemente, die Ereignisse auslösen, mit dem Schlüsselwort WithEvents deklarieren und dafür entsprechende Ereignisprozeduren anlegen können. Das gelingt, indem Sie zunächst die Deklaration im Kopf des Klassenmoduls vornehmen für unsere beiden Schaltflächen also wie folgt: Dim WithEvents objbutton1 As clsbutton Dim WithEvents objbutton2 As clsbutton Bild 8: Test der neuen Button-Elemente Dann verwenden Sie die beiden Kombinationsfelder im Kopf des Modulfensters und wählen dort im linken Element den Namen des betroffenen Steuerelements, zum Beispiel objbutton1, und im rechten den Eintrag OnAction aus. Dies legt dann automatisch die passende Seite 12

15 ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG ACCESS dem Sie einen Grafik einfügen-dialog öffnen können (s. Bild 9). Bild 9: Hinzufügen von Bildern zur Tabelle MSysResources Ereignisprozedur an, die wir mit einer Test-Meldung füllen (s. Bild 7): Damit fügen Sie dann die Bilddateien hinzu für kleine Schaltflächen in der Größe 16x16, für große Schaltflächen in 32x32. Wenn Sie zuvor nicht das Formular anklicken, werden die Bilddateien noch nicht einmal direkt zum Formular hinzugefügt, sondern nur zur Tabelle MSysResources. Private Sub objbutton1_onaction(control As RibbonControl) MsgBox control.id Private Sub objbutton2_onaction(control As IRibbonControl) MsgBox control.id In dieser Prozedur können wir dann gleich den Verweis auf das entsprechende Steuerelement nutzen, der mit dem Parameter control geliefert wird. Dies bietet zum Beispiel mit der Eigenschaft id den Namen des angeklickten Steuerelements an, den wir hier gleich per Meldungsfenster ausgeben. Das Ergebnis des aktuellen Stands finden Sie in Bild 8. Diese sieht dann etwa wie in Bild 10 aus. Die Definition passen wir nun wie folgt an: Set objbutton1 =.Controls.Add(µ With objbutton1.label = "Button 1" msoribboncontrolbutton, "Beispielbutton").Size = msoribboncontrolsizelarge.image = "add" End With Set objbutton2 =.Controls.Add(µ msoribboncontrolbutton, "Beispielbutton1") Große Schaltflächen mit Bildern Nun wollen wir natürlich auch die optischen Möglichkeiten des Ribbons nutzen und die Schaltflächen größer und mit Bildern anzeigen. Die dazu benötigten Bilder fügen Sie zur Systemtabelle MSysResources hinzu. Das gelingt am einfachsten, indem Sie ein Formular in der Entwurfsansicht öffnen und dann im Ribbon den Eintrag Entwurf Steuerelemente Bild einfügen anklicken. Hier erscheint dann ein Eintrag namens Durchsuchen..., mit Bild 10: Bilder in der Tabelle MSysResources Bild 11: Anzeige benutzerdefinierter Bilder Seite 13

16 ACCESS ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG Eigenschaften nutzen am Beispiel Ein-/ Ausblenden Wenn Sie schon einmal ein Ribbon programmiert haben und eine einzelne Schaltfläche etwa in Abhängigkeit von den angezeigten Daten ein- oder ausblenden wollten, wissen Sie, wie mühselig das Programmieren dieser Elemente ist. Bild 12: Auswahl eingebauter Icons per IntelliSense With objbutton2.label = "Button 2".Size = msoribboncontrolsizelarge.image = "close" End With Mit der hier vorgestellten Lösung ist dies wesentlich einfacher als mit der von Microsoft vorgesehenen Variante. Um von außen etwa auf die Eigenschaft Visible einer der Schaltflächen zuzugreifen, fügen Sie der Klasse Startribbon einfach nur eine entsprechende Property Get-Prozedur je betroffenem Steuerelement hinzu also etwa so: Wir haben hier erstens die Größe der Schaltflächen mit dem Wert msoribboncontrolsizelarge für die Eigenschaft Size auf große Icons umgestellt. Außerdem haben wir der Eigenschaft image jeweils den Namen des anzuzeigenden Bildes hinzugefügt. Das Ergebnis sieht dann wie in Bild 11 aus. Eingebaute Icons Sie können auch eines der zahlreichen eingebauten Icons nutzen. Diese lassen sich per IntelliSense abrufen, wenn Sie die Eigenschaft imagemso verwenden (s. Bild 12). Beachten Sie: Sie können nur eine der beiden Eigenschaften zum Anzeigen von Bildern verwenden, also entweder image oder imagemso. Public Property Get Button1() As clsbutton Set Button1 = objbutton1 End Property Public Property Get Button2() As clsbutton Set Button2 = objbutton2 End Property Wichtig ist, dass Sie den Variablentyp clsbutton für den Rückgabewert angeben. Wenn Sie das Ribbon nun aktualisieren, indem Sie die Anwendung schließen und wieder öffnen (das geht übrigens am schnellsten durch Aufruf der Funktion Datenbank komprimieren und reparieren), können Sie einmal das VBA-Fenster neben dem Access-Fenster platzieren und folgende Anweisung in das Direktfenster eingeben: Startribbon.Button1.Enabled = False Bild 13: Steuerelemente aktivieren und deaktivieren Damit deaktiveren Sie die erste Schaltfläche, die nun wie in Bild 13 ausgegraut erscheint. Sie können die Schaltfläche sogar ausblenden, indem Sie die Eigenschaft Visible auf False einstellen: Seite 14

17 ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG ACCESS Beispielaufrufe aus einem Formular heraus Im Formular aus Bild 14 haben wir drei Schaltflächen untergebracht, die alle Änderungen an der aktuellen Konstellation des Ribbons vornehmen. Der Code hinter den Schaltflächen sieht wie folgt aus. Neu ist hier insbesondere die Möglichkeit, das angezeigte Icon per Mausklick zu ändern: Private Sub cmdbilder_click() If Startribbon.Button1.Image = "add" Then Startribbon.Button1.Image = "close" Else Startribbon.Button1.Image = "add" End If Bild 14: Steuern des Ribbons vom Formular aus Private Sub cmdenabled_click() Startribbon.Button1.Enabled = _ Not Startribbon.Button1.Enabled Startribbon.Button1.visible = False Mit den folgenden beiden Anweisungen blenden Sie die Schaltfläche wieder ein und aktivieren diese: Startribbon.Button1.Enabled = true Startribbon.Button1.visible = true Das gelingt natürlich nicht nur vom Direktfenster aus, sondern auch etwa über die Ereignisprozeduren eines Formulars oder auch über die der Ribbon-Steuerelemente selbst. Statten wir doch die zweite Schaltfläche mit einer Prozedur aus, welche die erste Schaltfläche abwechselnd aktiviert und deaktiviert je nachdem, welcher Zustand gerade vorherrscht: Private Sub objbutton2_onaction(control As IRibbonControl) objbutton1.enabled = Not objbutton1.enabled Private Sub cmdvisible_click() Startribbon.Button1.Visible = _ Not Startribbon.Button1.Visible Praxistipps zum Startribbon In der Praxis bietet das Startribbon immer die für das Aufrufen der Funktionen der Anwendung benötigten Schaltflächen an. Gelegentlich gibt es noch weitere Steuerelemente wie etwa Kombinationsfelder oder auch Menüs, mit denen weitere Schaltflächen angeboten werden können. Die aktuelle Version der hier vorgestellten Lösung bietet dies nicht an. Gegebenenfalls wird es jedoch eine erweiterte Fassung geben, die weitere Steuerelemente bereitstellt und auf amvshop.de erworben werden kann. Wenn Sie den Wunsch haben, direkt beim Start etwa je nach angemeldetem Benutzer bestimmte Steuerelemente zu aktivieren/deaktivieren oder auch ein-/auszublenden, legen Sie die benötigten Steuerelemente alle zunächst an und stellen dann beim Starten die Eigenschaften Visible und Enabled auf die gewünschten Werte ein. Ein nachträgliches, dynamisches Hinzufügen von Steuerelementen ist nicht möglich. Seite 15

18 ACCESS ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG Diese Variante ist den Ribbons vorbehalten, die Sie zur Laufzeit in Zusammenhang mit der Anzeige von Formularen oder Berichten einblenden, wie die folgenden Abschnitte zeigen werden. Ribbons für Formulare Sobald Sie über das Startribbon ein Formular geöffnet haben, gibt es zwei Möglichkeiten: Entweder Sie behalten das aktuelle Ribbon bei, etwa weil das Formular keine weiteren Steuerelemente im Ribbon benötigt oder Sie zeigen ein eigenes Ribbon an, welches formularspezifische Steuerelemente anbietet. Wie dies gelingt, zeigt unser folgendes Beispiel. Dazu haben wir ein Formular namens frmeinfachesbeispiel erstellt, das wir natürlich über unser Hauptribbon öffnen wollen. Bei der Gelegenheit haben wir dem Startribbon auch gleich einen Eintrag hinzugefügt, mit dem die aktuelle Anwendung komprimiert werden kann, was die schnellste Methode ist, die Datenbank zu schließen und wieder zu öffnen und somit die neue Ribbon-Definition bereitzustellen (siehe weiter unten). Das Ribbon sieht nun wie in Bild 15 aus. Die Erweiterung für die Schaltfläche zum Öffnen des Formulars in der Methode CreateRibbon der Klasse StartRibbon sieht so aus:... Set objgroup =.Groups.Add("grpFormulare") With objgroup.label = "Formulare" Set objbuttonform = _.Controls.Add(msoRibbonControlButton, _ "btnfrmeinfachesbeispieloeffnen") With objbuttonform.label = "frmeinfachesbeispiel öffnen".size = msoribboncontrolsizelarge.image = "form" End With End With... Bild 15: Zwei neue Ribbon-Befehle Die Methode, die durch das Anklicken dieser Schaltfläche ausgelöst werden soll, sieht wie folgt aus: Public Property Get ButtonForm() As clsbutton Set ButtonForm = objbuttonform End Property Auch hier müssen Sie natürlich wieder eine entsprechende Objektvariable deklarieren: Dim WithEvents objbuttonform As clsbutton Außerdem fügen wir die Ereignisprozedur hinzu, die beim Anklicken des Buttons ausgelöst wird: Private Sub objbuttonform_onaction(control As _ Office.IRibbonControl) DoCmd.OpenForm "frmeinfachesbeispiel" Ribbon mit Formular anzeigen Dies zeigt dann das Formular wie in Bild 16 an. Hier erkennen Sie auch gleich das Ribbon, das beim Einblenden des Formulars automatisch erstellt und mit eingeblendet wird. Damit das Ribbon beim Laden des Formulars eingeblendet wird, legen Sie die folgende Prozedur an, die durch das Ereignis Beim Laden ausgelöst wird: Private Sub Form_Load() CreateRibbon Die hier aufgerufene Methode finden Sie in Listing 3. Sie deklariert wieder die für die Erstellung benötigten Seite 16

19 ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG ACCESS Bild 16: Ribbon, das gleichzeitig mit einem Formular eingeblendet wird Variablen für Objekte der Typen clsribbon, clstab und clsgroup. Dann erstellt sie ein neues Ribbon namens Formularribbon (dies entspricht auch gleichzeitig der im Reiter angezeigten Bezeichnung). Das Ribbon-Objekt erhält für die Eigenschaft StartFromScratch wieder den Wert True, damit die eingebauten Steuerelemente des Ribbons ausgeblendet werden. Dann fügt die Prozedur einen neuen Tab namens tabformularribbon hinzu und legt darunter eine Gruppe namens grpformularfunktionen an. Private Sub CreateRibbon() Dim objribbon As clsribbon Dim objtab As clstab Dim objgroup As clsgroup Set objribbon = Ribbons.Add("Formularribbon") With objribbon.startfromscratch = True Set objtab =.Tabs.Add("tabFormularribbon") With objtab.label = "Formularribbon" Set objgroup =.Groups.Add("grpFormularfunktionen") With objgroup.label = "Formularfunktionen" Set objbuttonclose =._ Controls.Add(msoRibbonControlButton, "btnformularschliessen") With objbuttonclose.label = "Formular schließen".size = msoribboncontrolsizelarge.image = "close" End With End With End With End With Me.RibbonName = Ribbons.GetRibbon("Formularribbon") Listing 3: Anlegen eines Ribbons für ein Formular Die Gruppe erhält die Beschriftung Formularfunktionen sowie eine neue Schaltfläche namens btnformularschliessen mit der Beschriftung Formular schließen. Die Größe der Schaltfläche stellen wir wieder auf msoribboncontrolsizelarge und somit auf groß ein. Dann fügen wir ein Icon mit einem Schließen-Symbol und dem Namen close.png zur Tabelle USysResources hinzu und stellen die Eigenschaft Image auf den Namen des Eintrags in der Tabelle ein (hier close). Diesmal rufen wir nicht einfach die Methode GetRibbon der Klasse Ribbons auf, sondern weisen das Ergebnis dieser Methode gleichzeitig der Eigenschaft RibbonName des Formulars zu. GetRibbon erstellt nämlich nicht nur das Ribbon, sondern liefert auch Seite 17

20 ACCESS ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG Bild 17: IntelliSense-Unterstützung beim Anlegen eingebauter Steuerelemente gleichzeitig den in der Anwendung hinterlegten Namen des Ribbons zurück. Für die einzige Schaltfläche des Ribbons definieren wir die folgende Variable: Im VBA-Editor unterstützt uns IntelliSense dabei, die passende Konstante anzugeben, in diesem Fall msofile- CompactAndRepairDatabase (s. Bild 17). Dim WithEvents objbuttonclose As clsbutton Beim Anklicken der Schaltfläche soll die folgende Ereignisprozedur ausgelöst werden, welche das aktuelle Formular schließt: Private Sub objbuttonclose_onaction(control As _ Office.IRibbonControl) DoCmd.Close acform, Me.Name Und auch hier stellen wir mit einer Property Get-Methode den Zugriff auf das Steuerelement bereit, um es ein- oder ausblenden beziehungsweise aktivieren oder deaktivieren zu können: Der Code für dieses Element in der CreateRibbon-Methode der Klasse StartRibbon sieht so aus:... Set objgroup =.Groups.Add("grpTools") With objgroup.label = "Tools" Set objbuttoncompact = _.Controls.Add(msoRibbonControlButton,, msofilecompactandrepairdatabase) With objbuttoncompact.size = msoribboncontrolsizelarge End With End With... Public Property Get ButtonClose() As clsbutton Set ButtonClose = objbuttonclose End Property Für diese Schaltfläche legen wir auch eine Objektvariable an, aber diese soll keine Ereignisse auslösen können. Wir benötigen also das Schlüsselwort WithEvents nicht: Komprimieren-Schaltfläche Die Komprimieren-Schaltfläche ist eine Schaltfläche mit einer eingebauten Access-Funktion. Diese können wir anlegen, indem wir den zweiten Parameter der Add- Methode der Controls-Auflistung des Group-Elements auslassen und stattdessen den dritten Parameter nutzen. Dim objbuttoncompact As clsbutton Es handelt sich ja um ein eingebautes Element, das sein Ereignis automatisch auslöst. Wir brauchen also keine Ereignisprozedur zu implementieren. Wozu aber benötigen wir die Variable? Ganz einfach: Um eine Property Get- Seite 18

21 ERGONOMIE UND BENUTZEROBERFLÄCHE OBJEKTORIENTIERTE RIBBON-PROGRAMMIERUNG ACCESS Prozedur zu definieren, mit der wir von außen auf den Button zugreifen können: Public Property Get ButtonCompact() As clsbutton Set ButtonCompact = objbuttoncompact End Property Immerhin wollen wir dieses Element ja auch ein- oder ausblenden können oder dieses aktivieren oder deaktivieren. Zur Strukturierung: Trenner Ein weiteres Element, das Sie mit den Ribbon-Klassen der hier vorgestellten Lösung nutzen können, ist der sogenannte Separator. Er erlaubt es, mehrere andere Steuerelemente voneinander zu trennen und das Ribbon somit auch innerhalb einer Gruppe übersichtlicher zu gestalten. Solche Separator-Elemente fügen Sie mit der Add-Methode der Controls-Auflistung unter Verwendung des Wertes msoribboncontrolseparator als ersten Parameter zum Ribbon hinzu, wie folgendes Beispiel zeigt:.controls.add msoribboncontrolseparator, "sep1" Set objbutton3 = _.Controls.Add(msoRibbonControlButton,, _ msoaccessformdatasheet) objbutton3.size = msoribboncontrolsizelarge.controls.add msoribboncontrolseparator, "sep2" Set objbutton4 = _.Controls.Add(msoRibbonControlButton,, _ msoaccessformmodaldialog) objbutton4.size = msoribboncontrolsizelarge.controls.add msoribboncontrolseparator, "sep3" Set objbutton5 = _.Controls.Add(msoRibbonControlButton,, _ msoaccessformwizard) objbutton5.size = msoribboncontrolsizelarge End With Das Ergebnis sieht dann etwa wie in Bild 18 aus. Zusammenfassung und Ausblick Mit den hier vorgestellten Klassen und Techniken können Sie einfache Ribbons zusammenstellen, ohne auch nur eine Zeile XML-Code zu schreiben, der normalerweise für die Definition von Ribbons nötig ist. Set objgroup =.Groups.Add("grpSeparator") With objgroup.label = "Beispiel für Separatoren" Set objbuttoncompact = _.Controls.Add(msoRibbonControlButton,, _ msofilecompactandrepairdatabase) With objbuttoncompact.size = msoribboncontrolsizelarge End With Stattdessen verwenden Sie eine Reihe einfacher Klassen mit ihren Eigenschaften, Methoden und Ereignissen. Das hier vorgestellte Set von Ribbon-Klassen erlaubt den Einsatz von Tab-, Group- und Button-Elementen. Dies sind die meistverwendeten Elemente im Ribbon andere werden kaum genutzt, da Elemente wie Kombinationsfelder, Menüs et cetera meist direkt in den jeweiligen Formularen abgebildet werden. Bild 18: Beispiel für den Einsatz von Separatoren Seite 19

22 ACCESS ABFRAGEN UND SQL SICHERE FILTERAUSDRÜCKE Sichere Filterausdrücke Filterausdrücke können schnell gefährlich werden, zumindest wenn Sie nicht aufpassen. Dafür sorgt die sogenannte SQL-Injection, bei der davon ausgegangen wird, dass der Benutzer einen Ausdruck in die Benutzeroberfläche eingibt, die dann als Teil eines SQL-Ausdrucks verwendet wird und Schaden anrichten könnte. Dieser Beitrag erläutert, was SQL-Injection eigentlich ist und wie Sie sich dagegen wappnen können. Die SQL-Injection ist eine Methode, aus eigentlich harmlosen SQL-Anweisungen schädliche SQL-Anweisungen zu machen oder damit an Informationen zu gelangen, die der Benutzer normalerweise nicht erhalten sollte. Dabei ist die Voraussetzung immer ein Eingabefeld in einer Webseite oder, in diesem Falle, in einem Access- Formular, in das der Benutzer beispielsweise einen Vergleichswert für einen Vergleichsausdruck eingeben soll im einfachsten Fall etwa die ID eines Kunden. Beispielformular Als Spielwiese für die folgenden Beispiele verwenden wir ein einfaches Access-Formular namens frmkunden samt Unterformular sfmkunden, wobei das Unterformular die Daten der Tabelle tblkunden anzeigt und das Hauptformular ein Eingabefeld für einen Suchbegriff liefert, der zunächst die Suche nach Werten im Feld KundeID ermöglicht (s. Bild 1). Bild 1: Beispielformular in der Entwurfsansicht Für das Textfeld txtkundenid legen wir die folgende Ereignisprozedur an: Private Sub txtkundenid_afterupdate() Dim strsql As String strsql = "SELECT * FROM tblkunden WHERE KundeID = " µ Me!sfmKunden.Form.RecordSource = strsql & Me!txtKundenID Bild 2: Filtern nach dem Wert 1 im Feld KundeID Diese sorgt dafür, dass eine neue SQL-Anweisung zusammengestellt wird, die den Datensatz der Tabelle tblkunden mit einem bestimmten Wert im Feld KundeID liefert und diesen der Eigenschaft RecordSource des Unterformular zuweist. Dies gelingt auch reibungslos, wenn der Benutzer einfach nur einen Zahlenwert eingibt (s. Bild 2). Ergebnismenge mit UNION erweitern Was an unserer Methode soll also gefährlich sein schließlich kann man doch nur Zahlenwerte eingeben und erhält das gewünschte Ergebnis? Mitnichten: Schon die Kenntnis der Datenstruktur (oder ein wenig experimentelle Seite 20

23 ABFRAGEN UND SQL SICHERE FILTERAUSDRÜCKE ACCESS SELECT * FROM tblkunden WHERE KundeID = 1 UNION SELECT * FROM tblkunden Bild 3: Erweiterung des Ergebnisses per UNION Arbeit) erlauben es, ein ganz anderes Ergebnis zu erhalten als geplant. Geben Sie beispielsweise einmal den folgenden Ausdruck in das Suchfeld ein: 1 UNION SELECT * FROM tblkunden WHERE KundeID = 10 Das Ergebnis ist überraschend: Es gibt keinen Fehler, sondern es erscheinen zwei Datensätze (s. Bild 3)! Allzu verwunderlich ist dies jedoch gar nicht, denn der resultierende Ausdruck in der Variablen strsql, den Sie sich einfach im Debugbereich ausgeben lassen können, sieht wie folgt aus: SELECT * FROM tblkunden WHERE KundeID = 1 UNION SELECT * FROM tblkunden WHERE KundeID = 11 Gut, der Schaden ist gering, denn wir greifen so nur auf Datensätze zu, die wir auch anderweitig hätten lesen können. Aber wenn wir keine Bedingung angeben, liefert der resultierende SQL-Ausdruck uns sogar wieder alle Datensätze: Dies könnte interessant werden, wenn der eigentliche Filterausdruck noch dafür sorgt, dass beispielsweise nur die Kunden des aktuell angemeldeten Mitarbeiters angezeigt werden sollen. Daten anderer Tabellen auslesen Noch spannender wird es, wenn Sie hinter dem UNION -Schlüsselwort nicht auf die gleiche, sondern direkt auf eine andere Tabelle zugreifen, die unter Umständen kritische Daten enthält. Im Beispiel wollen wir einfach einmal probieren, Daten aus der Tabelle tblartikel zusätzlich auszugeben, und testen den folgenden Suchausdruck: 1 UNION SELECT ArtikelID as KundeID, Artikelname as Kundencode FROM tblartikel Dies liefert im ersten Anlauf noch den Fehler aus Bild 4, weil der zweite Teil der UNION-Abfrage nicht die gleiche Anzahl Felder wie der erste Teil zurückliefert. Aber was geschieht, wenn wir einfach ein paar fixe Werte entsprechend der Anzahl der Felder des ersten Teils der Abfrage hinzufügen und so notfalls durch Experimentieren auf die richtige Anzahl Felder kommen? So ergibt sich nach kurzem Test der folgende Ausdruck: 1 UNION SELECT ArtikelID as KundeID, Artikelname as Kundencode, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 FROM tblartikel Das Ergebnis ist einigermaßen erschreckend: Wir gelangen so tatsächlich an die Informationen der Tabelle tblartikel, die eigentlich gar nicht zur Datenherkunft des Unterformulars gehört (s. Bild 5). Und natürlich könnten wir diese Tabelle auch durch eine Tabelle mit wirklich sensiblen Daten wie etwa der Benutzertabelle ersetzen. Bild 4: Fehler, wenn nicht alle Seiten der UNION-Abfrage die gleiche Anzahl Felder enthalten So lassen sich auch leicht die Tabellen einer Datenbank ausgeben und zwar mit diesem Suchbegriff: Seite 21

24 ACCESS ABFRAGEN UND SQL SICHERE FILTERAUSDRÜCKE Bild 6: Ermitteln der Daten einer anderen Tabelle, hier die Tabellennamen Bild 5: Ermitteln der Daten einer anderen Tabelle 1 UNION SELECT Name, Type, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 FROM MSysObjects WHERE Type = 1 Gefährliche Strings Gibt es ebenfalls Probleme, wenn man ein Suchfeld für die Eingabe einer Zeichenkette verwendet? Ja. Dazu fügen wir dem Formular ein Textfeld namens txtfirma hinzu, das nach dem Aktualisieren die folgende Ereignisprozedur auslöst: Dies liefert uns, wie in Bild 6 zu erkennen, wie gewünscht alle Tabellennamen der Datenbank. Der resultierende SQL-Ausdruck endet nun zwar mit einem unerwünschten Hochkomma, doch das hindert Access nicht an seiner Interpretation: SELECT * FROM tblkunden WHERE Firma LIKE 'A*' UNION SELECT Name, Type, 3,4,5,6,7,8,9,10,11,12 FROM MSysObjects' Private Sub txtfirma_afterupdate() Dim strsql As String strsql = "SELECT * FROM tblkunden µ Debug.Print strsql WHERE Firma LIKE '" & Me!txtFirma & "'" Me!sfmKunden.Form.RecordSource = strsql Geben Sie hier etwa A* ein, liefert dies alle Einträge der Tabelle tblkunden, deren Feld Firma einen Wert enthält, der mit A beginnt. Wie können wir hier beispielsweise mit dem UNION-Schlüsselwort auf die Daten einer zweiten Tabelle wie etwa MSysObjects zugreifen? Dazu fügen wir einfach hinter dem Vergleichswert ein schließendes Hochkomma ein und hängen die UNION-Klausel hinten an: Würde der SQL-Ausdruck wie folgt zusammengesetzt, also mit einem Sternchen am Ende, würde dies allerdings einen Fehler auslösen: strsql = "SELECT * FROM tblkunden WHERE Firma LIKE '" µ & Me!txtFirma & "*'" Der resultierende SQL-Ausdruck würde nämlich dann wie folgt aussehen: SELECT * FROM tblkunden WHERE Firma LIKE '' UNION SELECT Name, Type, 3,4,5,6,7,8,9,10,11,12 FROM MSysObjects*' Auch dies können wir jedoch umgehen, wenn wir folgenden Suchausdruck eingeben: A*' UNION SELECT Name, Type, 3,4,5,6,7,8,9,10,11,12 FROM MSysObjects ' UNION SELECT Name, Type, 3,4,5,6,7,8,9,10,11,12 FROM MSysObjects WHERE ' Seite 22

25 ABFRAGEN UND SQL SICHERE FILTERAUSDRÜCKE ACCESS Das Ergebnis sieht dann so aus: SELECT * FROM tblkunden WHERE Firma LIKE '' UNION SELECT Name, Type, 3,4,5,6,7,8,9,10,11,12 FROM MSysObjects WHERE '*' Verhindern von Fehlern und Manipulationen Es gibt verschiedene Möglichkeiten, die Wirkung der oben genannten Eingaben zu verhindern. Der Grundtenor lautet: Fügen Sie die Eingaben des Benutzers nicht ungeprüft in den SQL-Code ein. Die verschiedenen Varianten schauen wir uns nun an. Fehler durch unerwartete Eingaben Die Einsatz eines einfachen Hochkommas muss gar nicht unbedingt in böser Absicht erfolgen, denn nicht immer dient dies dem Manipulieren einer SQL-Anweisung. Manchmal möchte der Benutzer einfach nach einem Namen wie Paul's Boutique suchen. Das Ergebnis unserer ungesicherten Variante können Sie in Bild 7 begutachten Access meldet einen Syntaxfehler. Kein Wunder: Es denkt, das zweite Hochkomma hinter Paul im folgenden Ausdruck würde den Vergleichsausdruck der Bedingung beenden. Mit den danach folgenden Zeichen s Boutique kann es dann nichts mehr anfangen und meldet einen Fehler: SELECT * FROM tblkunden WHERE Firma LIKE 'Paul's Boutique*' Noch mehr Möglichkeiten auf dem SQL Server Wenn Sie mit Auswahl- oder auch Aktionsabfragen auf die Daten eines SQL Servers zugreifen, die einen beispielsweise wie in diesem Fall per Textfeld eingegebenen Vergleichswert enthalten, kann der Benutzer damit noch viel größeren Schaden anrichten. Der SQL Server erlaubt nämlich im Gegensatz zu Access die Angabe mehrerer durch Semikolon getrennter Anweisungen. So ließen sich sogar Anweisungen einbauen, mit denen Daten (oder gar komplette Tabellen) aus der Datenbank gelöscht oder geändert oder Skripte auf dem Server ausgeführt werden. Wir wollen hier nicht ins Detail gehen, was die Möglichkeiten angeht, sondern uns lieber darum kümmern, wie Sie solche Aktionen verhindern können. Hochkommata escapen Wer mit Textfeldern als Vergleichskriterium arbeitet, weiß, dass es mit der Eingabe von Hochkommata zu Problemen kommen kann. Die Lösung ist denkbar einfach: Sie müssen die Hochkommata einfach»verdoppeln«, indem Sie zu jedem Hochkomma noch ein weiteres hinzufügen. Dies gelingt ganz schnell mit der Replace-Funktion. In unserem Beispiel des Textfeldes txtfirma sieht das wie in Listing 1 aus. Wir haben den Inhalt dieses Textfeldes zunächst in die Variable strvergleichswert geschrieben, damit die Vorgehensweise übersichtlicher wird. In der folgenden Anweisung haben wir den Inhalt von strvergleichswert mit der Replace-Anweisung bearbeitet, und zwar so, dass jedes Auftreten eines Hochkommas durch zwei Hochkommata ersetzt wurde. Wenn der Benutzer also Paul's Boutique eingeben würde, enthielte strvergleichswert anschließend den Wert Paul''s Boutique. Aber was Bild 7: Fehler durch ein Hochkomma Seite 23

26 ACCESS ABFRAGEN UND SQL SICHERE FILTERAUSDRÜCKE gewinnen wir nun dadurch? Ganz einfach: Access-SQL interpretiert die doppelten Hochkommata nun wie ein einfaches Zeichen und nicht mehr als Steuerzeichen, wie es das öffnende und schließende Zeichen einer Zeichenkette ist: Private Sub txtfirma_afterupdate() Dim strsql As String Dim strvergleichswert As String strvergleichswert = Me!txtFirma strvergleichswert = Replace(strVergleichswert, "'", "''") strsql = "SELECT * FROM tblkunden WHERE Firma LIKE '" & strvergleichswert & "*'" Debug.Print strsql Me!sfmKunden.Form.RecordSource = strsql Listing 1: Entschärfen von Hochkommata durch»verdoppeln«select * FROM tblkunden WHERE Firma LIKE 'Paul''s Boutique*' Wenn Sie also mit doppelten Anführungszeichen zur Markierung des Vergleichsausdrucks arbeiten, sollten Sie die Anführungszeichen im Suchbegriff verdoppeln also wie zuvor die Hochkommata. Dies sieht mit Replace dann so aus: Die Zeile zum Zusammenstellen der SQL-Anweisung hätten wir natürlich auch gleich wie folgt umgestalten können: strsql = "SELECT * FROM tblkunden WHERE Firma LIKE """ & strvergleichswert & "*""" Hier haben wir nun das öffnende und schließende Hochkomma für das als Vergleichswert angegebene Literal durch doppelte Anführungszeichen ersetzt. Der resultierende SQL-Ausdruck sieht nun so aus (die Replace- Funktion zum Verdoppeln des Hochkommas haben wir hier nicht benötigt): SELECT * FROM tblkunden WHERE Firma LIKE "Paul's Boutique*" In diesem Fall könnte uns der Benutzer aber wieder einen Strich durch die Rechnung machen, indem er ein Anführungszeichen in den Suchbegriff integriert. Dies würde in folgender SQL-Anweisung resultieren, die wiederum den Teil hinter dem zweiten Anführungszeichen nicht interpretieren könnte: SELECT * FROM tblkunden WHERE Firma LIKE "Paul"s Boutique*" strvergleichswert = Replace(strVergleichswert, """", """""") Dies wäre dann der SQL-Ausdruck mit unserem Beispielvergleichswert: SELECT * FROM tblkunden WHERE Firma LIKE "Paul""s Boutique*" UNION-Abfragen entschärfen Wenn Sie, wie weiter oben gezeigt, einem Textvergleichswert per UNION eine weitere Abfrage hinzufügen wollen, um Werte anderer Tabellen zu ermitteln, reicht das Verdoppeln der Hochkommata als Vorbeugung bereits aus. Der Wert im Suchfeld sieht wie folgt aus: A*' UNION SELECT Name, Type, 3,4,5,6,7,8,9,10,11,12 FROM MSysObjects Dann erhalten wir mit Verdoppeln des Hochkommas die folgende SQL-Abfrage: SELECT * FROM tblkunden WHERE Firma LIKE 'A*'' UNION SELECT Name, Type, 3,4,5,6,7,8,9,10,11,12 FROM MSysObjects' Nun endet der Vergleichswert mit dem Feld Firma nicht hinter A*, sondern das Verdoppeln des Hochkommas Seite 24

27 ABFRAGEN UND SQL SICHERE FILTERAUSDRÜCKE ACCESS sorgt dafür, dass dieses als Teil des Vergleichswertes interpretiert wird. Das Ergebnis der Abfrage ist nun leer, es sei denn, das Feld Firma enthielte den Wert A*'' UNION SELECT Name, Type, 3,4,5,6,7,8,9,10,11,12 FROM MSysObjects, was nicht der Fall sein dürfte. Zahlenwerte sichern Weiter oben hatten wir ja auch die Möglichkeit aufgezeigt, in Access-SQL-Abfragen einfach mit UNION weitere Abfragen an eine bestehende Abfrage anzuhängen, die einen Zahlenwert als Vergleichswert nutzt. Hier ist das Verdoppeln von Hochkommata oder Anführungszeichen im Vergleichsausdruck wie etwa im obigen Beispiel nutzlos, denn der Vergleichsausdruck enthält ja keine solchen. Hier könnten Sie bereits bei der Eingabe prüfen, ob der angegebene Wert ein Zahlenwert ist. Falls nicht, geben Sie einfach eine entsprechende Meldung aus. Dies realisieren Sie am einfachsten auf folgende Art und Weise: Private Sub txtkundenid_afterupdate() Dim strsql As String If IsNumeric(Me!txtKundenID) Then Else End If strsql = "SELECT * FROM tblkunden µ Debug.Print strsql WHERE KundeID = " & Me!txtKundenID Me!sfmKunden.Form.RecordSource = strsql MsgBox "Bitte geben Sie einen Zahlenwert ein." Die Abfrage für das Unterformular unseres Beispiels sähe etwa wie in Bild 8 aus. Sie soll alle Felder der Tabelle tblkunden zurückliefern, aber nur für solche Datensätze, deren Feld Firma mit dem Wert des mit [prmfirma] gekennzeichneten Parameters übereinstimmt. Die Filterfunktion, welche diese Abfrage (und ein weiteres Textfeld namens txtfirmaparameter, siehe Beispieldatenbank) als Basis nutzt, sieht wie folgt aus: Private Sub txtfirmaparameter_afterupdate() Dim qdf As DAO.QueryDef Dim db As DAO.Database Dim rst As DAO.Recordset Set db = CurrentDb Set qdf = db.querydefs("qrykundennachfirma") qdf.parameters("prmfirma") = Me!txtFirmaParameter Set rst = qdf.openrecordset Set Me!sfmKunden.Form.Recordset = rst Sie setzt den Vergleichswert einfach als Wert des betreffenden Elements der Parameters-Auflistung des entsprechenden QueryDef-Objekts ein. Dieses wird gar nicht interpretiert, sondern direkt als Vergleichswert genutzt. Der Benutzer kann hier also beliebige Ausdrücke eingeben, diese werden auf keinen Fall ausgeführt. Diese Variante setzt natürlich voraus, dass das Ergebnis als Recordset zugewiesen kann. Mit Parametern arbeiten Sollten Sie auf Nummer sicher gehen wollen, können Sie mit Parameterabfragen arbeiten. Eine solche Abfrage müssen Sie entsprechend vorbereiten, was aber auch den schönen Nebeneffekt von Performancegewinnen mit sich bringen kann. Bild 8: Abfrage mit Parameter Seite 25

28 ACCESS FORMULARE UND STEUERELEMENTE LOOKUP-KOMBINATIONSFELDER NACH TEXTEN FILTERN Lookup-Kombinationsfelder nach Texten filtern Wenn Sie die Datensätze eines Unterformulars in der Datenblattansicht filtern wollen, gelingt die Eingabe in Text-, Zahlen- und Datumsfelder recht einfach. Wenn Sie jedoch ein Suchfeld für die Werte eines Lookup-Kombinationsfeldes programmieren wollen, stoßen Sie schnell an die Grenzen. Sie können die Feldinhalte nämlich nicht einfach mit den in den Feldern angezeigten Werten vergleichen, denn diese stammen ja aus den Lookup-Tabellen, mit denen solche Steuerelemente gefüllt werden. Dieser Beitrag zeigt, wie auch das Filtern nach den Werten in Kombinationsfeldern zum Kinderspiel wird. Als Erstes zeigen wir Ihnen, wie es nicht gelingt und was somit das Problem beim Filtern nach den Inhalten von Lookup-Kombinationsfeldern ist. In unserem Beispielformular haben wir ein Textfeld zur Eingabe des Suchbegriffs, eine Suchen-Schaltfläche namens cmdsuchen sowie ein Unterformular namens sfmartikel_ Lookupfilter angelegt (s. Bild 1). In einem ersten, naiven Ansatz wollen wir die Suche wie für ein normales Feld des Datentyps String durchführen. Die dazugehörige Prozedur sieht dann wie in Listing 1 aus. Das Ergebnis sieht wie in Bild 2 aus: Es liefert keinerlei Datensätze, obwohl das Kombinationsfeld doch einige Werte anzeigt, die mit dem Suchbegriff E* beginnen. Eine Analyse des resultierenden Filterausdrucks lässt eine Vorahnung aufkommen. Das Feld LieferantID enthält doch nur Zahlenwerte? LieferantID LIKE 'E*' Private Sub cmdsuchen_click() Dim strlieferant As String strlieferant = Me!txtLieferant Bild 1: Aufbau des Beispielformulars Me!sfmArtikel_Lookupfilter.Form.Filter = "LieferantID LIKE '" & strlieferant & "'" Me!sfmArtikel_Lookupfilter.Form.FilterOn = True Listing 1: Erster Ansatz zum Filtern der angezeigten Werte im Kombinationsfeld Es ist also klar: Auch, wenn das Kombinationsfeld die Hersteller anzeigt, sind dies nicht die Werte des an das Steuerelement gebundenen Feldes LieferantID. LieferantID gibt lediglich an, mit welchem Datensatz der Tabelle tbllieferanten der aktuelle Datensatz der Tabelle tblartikel verknüpft ist. Das Feld enthält also, wenn wie in diesem Fall referenzielle Integrität definiert ist, mit Sicherheit nur Zahlenwerte. Sie könnten nun den Wert 1 als Suchbegriff in das Seite 26

29 FORMULARE UND STEUERELEMENTE LOOKUP-KOMBINATIONSFELDER NACH TEXTEN FILTERN ACCESS gibt es verschiedene Ansätze, die wir uns nun ansehen. Bild 2: Erster, erfolgloser Anlauf Textfeld txtfirma eingeben. Das Ergebnis wäre für den Benutzer eher noch verwirrender als das vorherige: Es erscheinen nämlich alle Datensätze mit dem Lieferanten Exotic Liquids. Im Grunde ist es ganz einfach: Wir müssen lediglich die Abfrage in der Ereignisprozedur der Schaltfläche cmdsuchen anpassen, um das Ergebnis aus Bild 4 zu erhalten! Die entsprechende Zeile sieht dann wie folgt aus: Me!sfmArtikel_Lookupfilter.Form.Filter = "LieferantID IN (SELECT LieferantID FROM tbllieferanten WHERE Firma LIKE '" & strlieferant & "')" Dieser hat wiederum, wie Sie vielleicht erahnen, den Wert 1 im Feld LieferantID der Tabelle tbllieferanten, also zeigt das Unterformular nach dem Filtern alle Datensätze der Tabelle tblartikel an, die diesem Lieferanten zugeordnet sind. Noch verwirrender wird es, wenn Sie beispielsweise den Wert 1* eingeben. Dann erscheint das Ergebnis aus Bild 3. Bild 3: Alle Artikel, die zu einem Lieferanten gehören, der mit einer LieferantID wie 1* beginnt Der Benutzer würde dies nun gar nicht mehr einordnen können, aber wir wissen: Das sind alle Artikel, deren Feld Lieferant- ID einen Wert enthält, der mit 1 beginnt. Filtern nach dem angezeigten Wert Damit wenden wir uns dem eigentlichen Ziel dieses Beitrags zu: Wir wollen ja nicht nach den Fremdschlüsselwerten filtern, sondern nach dem Wert, der im Kombinationsfeld angezeigt wird. Hier Bild 4: Diese Artikel wollen wir sehen. Seite 27

30 ACCESS FORMULARE UND STEUERELEMENTE LOOKUP-KOMBINATIONSFELDER NACH TEXTEN FILTERN Der nackte SQL-Ausdruck etwa nach Eingabe des Suchausdrucks E* sieht wie folgt aus: LieferantID IN ( SELECT LieferantID FROM tbllieferanten WHERE Firma LIKE 'E*' ) Was bedeutet das nun? Wir verwenden hier immer noch das Feld LieferantID als Vergleichsfeld. Allerdings unterscheidet sich der Rest gravierend von der vorherigen Version. Der erste Unterschied ist, dass wir nicht den Operator LIKE (oder auch das Gleichheitszeichen) verwenden, sondern den IN-Operator. Dieser erwartet eine Menge von einem oder mehreren Vergleichswerten, die immer in Klammern angegeben werden müssen. Die Vergleichswerte können als kommaseparierte Liste angegeben werden, also etwa so: IN (1, 2, 3) In diesem Fall ist die Menge der Vergleichswerte jedoch dynamisch und von den Werten der Tabelle tbllieferanten abhängig, daher nutzen wir in Klammern eine SELECT- Abfrage: SELECT LieferantID FROM tbllieferanten WHERE Firma LIKE 'E*' Die Abfrage liefert alle Werte des Feldes LieferantID der Tabelle tbllieferanten, deren Feld Firma mit dem Buchstaben E beginnt (LIKE E*) hier steckt also nun ein weiterer Vergleichsoperator, der diesmal festlegt, mit welchem Suchbegriff die im Kombinationsfeld angezeigten Werte verglichen werden sollen. Dies ist die einfachste Lösung, um ein Lookup-Kombinationsfeld nach den angezeigten Werten zu filtern. Bild 5: Erweiterte Datenherkunft des Formulars Variante mit erweiterter Datenherkunft Die zweite Variante setzt ein wenig Vorarbeit voraus, dafür ist die Gestaltung des Filter-Kriteriums wesentlich einfacher. Wir verwenden hier im Formular frmartikel_lookupfilter_ii die Abfrage aus Bild 5 als Datenherkunft des Unterformulars. Der Unterschied zum Einsatz der reinen Tabelle tblartikel ist, dass wir die Tabelle tbllieferanten noch hinzugefügt und aus dieser Tabelle das Feld Firma in das Entwurfsraster gezogen haben. Im Entwurf des Unterformulars müssen wir allerdings keine Änderungen vornehmen, denn wir wollen die Firma aus der Tabelle tbllieferanten ja nicht direkt in einem Textfeld, sondern nach wie vor als Kombinationsfeld anzeigen. Hinweis: Der Name des Unterformular-Steuerelements im Formular frmartikel_lookupfilter_ii lautet nach wie vor sfmartikel_lookupfilter, aber es zeigt das neue Unterformular frmartikel_lookupfilter_ii an. Die Ereignisprozedur, die durch die Schaltfläche cmdsuchen ausgelöst wird, sieht nun so aus: Private Sub cmdsuchen_click() Dim strlieferant As String Seite 28

31 FORMULARE UND STEUERELEMENTE LOOKUP-KOMBINATIONSFELDER NACH TEXTEN FILTERN ACCESS strlieferant = Me!txtLieferant Me!sfmArtikel_Lookupfilter.Form.Filter = µ "Firma LIKE '" & strlieferant & "'" Me!sfmArtikel_Lookupfilter.Form.FilterOn = True Flexible Suche Nun wollen wir den Code noch so anpassen, dass wir den Filterausdruck nicht mehr manuell erstellen müssen, sondern nur noch das Kombinationsfeld und den Vergleichswert angeben müssen. Der Filterausdruck sieht nun nur noch so aus: "Firma LIKE '" & strlieferant & "'" Dies ist nun so einfach, weil das Feld Firma ja nun zur Datenherkunft des Unterformulars gehört. Dazu verwenden wir eine Hilfsfunktion, und zwar die aus Listing 2. Um die Suche auszuprobieren, legen wir eine zweite Schaltfläche namens cmdsucheflexibel an, für die wir die folgende Ereignisprozedur hinterlegen: Private Function LookupSearchstring(cbo As ComboBox, strvalue As String) As String Dim strboundcolumn As String Dim rst As DAO.Recordset Dim fldbound As DAO.Field Dim strsql As String Dim strboundtable As String Dim strforeignkey As String Dim strfieldcriteria As String Dim strcolumnwidths() As String Dim i As Integer Dim dblcolumnwidth As Double Set rst = cbo.recordset Set fldbound = rst.fields(cbo.boundcolumn - 1) strboundtable = fldbound.sourcetable strboundcolumn = fldbound.sourcefield strforeignkey = cbo.controlsource strcolumnwidths = Split(cbo.ColumnWidths, ";") For i = 0 To cbo.columncount - 1 dblcolumnwidth = 1 On Error Resume Next dblcolumnwidth = Split(cbo.ColumnWidths, ";")(i) On Error GoTo 0 If dblcolumnwidth > 0 Then Exit For End If Next i strfieldcriteria = rst.fields(i).name strsql = strforeignkey & " IN (SELECT " & strboundcolumn & " FROM " & strboundtable & " WHERE " _ & strfieldcriteria & " LIKE '" & strvalue & "')" LookupSearchstring = strsql End Function Listing 2: Flexibles Suchen von Zeichenketten in der Datensatzherkunft von Kombinationsfeldern Seite 29

32 ACCESS FORMULARE UND STEUERELEMENTE LOOKUP-KOMBINATIONSFELDER NACH TEXTEN FILTERN Private Sub cmdsucheflexibel_click() Dim strlieferant As String Dim strsearchstring As String strlieferant = Me!txtLieferant strsearchstring = LookupSearchstring(Me!µ sfmartikel_lookupfilter.form!lieferantid, strlieferant) Me!sfmArtikel_Lookupfilter.Form.Filter = strsearchstring Me!sfmArtikel_Lookupfilter.Form.FilterOn = True Die Funktion LookupSearchstring soll uns also den Filterausdruck liefern, den wir dann nur noch der Eigenschaft Filter des Unterformulars zuweisen müssen. Dazu übergeben wir der Funktion zwei Parameter: einen Verweis auf das betroffene Kombinationsfeld und den Suchbegriff. das Recordset des Kombinationsfeldes mit der Objektvariablen rst. Dann liest sie den Namen der gebundenen Spalte des Kombinationsfeldes aus, in der Regel das Primärschlüsselfeld. Den Namen der gebundenen Spalte erhalten wir über die Position, welche die Eigenschaft BoundColumn des Kombinationsfeldes liefert (cbo.boundcolumn - 1), in der Fields-Auflistung des Recordsets. In unserem Beispiel hat BoundColumn den Wert 1. Ziehen wir hier 1 ab, erhalten wir den Indexwert 0 die Position des Feldes LieferantID. Mit dem gebundenen Feld ausgestattet ermitteln wir die gebundene Tabelle (aus der Eigenschaft SourceTable des Feldes der gebundenen Spalte) sowie den Namen des gebundenen Feldes in der Tabelle (mit der Eigenschaft SourceField). Das Ziel der Funktion ist es, die Datenherkunft des Kombinationsfeldes zu analysieren und daraus folgende Eigenschaften zu ermitteln: Name des Feldes der gebundenen Spalte Name des angezeigten Feldes Name der Tabelle der Datenherkunft Diese Informationen benötigen wir, um den Filterausdruck zusammenzustellen. Die Funktion referenziert zunächst Den Namen des als Fremdschlüssel verwendeten Feldes ermitteln wir über die Eigenschaft ControlSource des Kombinationsfeldes, in diesem Fall LieferantID (das Fremschlüsselfeld muss nicht zwingend den gleichen Namen wie das Primärschlüsselfeld der Lookuptabelle aufweisen, aber hier ist es der Fall). Schließlich ermitteln wir den Namen des ersten Feldes, dessen Inhalt im Kombinationsfeld angezeigt wird. Meist legen Sie mit einer Einstellung der Eigenschaften Spaltenbreiten und Spaltenanzahl fest, dass die gebundene Spalte ausgeblendet wird und welche Spalte angezeigt Bild 6: Filtern nach dem Feld LieferantID mit flexibler Ermittlung des Filterausdrucks Seite 30

33 FORMULARE UND STEUERELEMENTE LOOKUP-KOMBINATIONSFELDER NACH TEXTEN FILTERN ACCESS werden soll. Meist enthält die Datensatzherkunft nur zwei Felder, nämlich die gebundene Spalte und die anzuzeigende Spalte. Dann reicht es, die Eigenschaft Spaltenanzahl auf den Wert 2 und Spaltenbreiten auf den Wert 0 einzustellen das zweite Feld nimmt dann automatisch die komplette Breite des Kombinationsfeldes ein. Manchmal enthält das Kombinationsfeld aber auch mehr als zwei Spalten. Dann kann Spaltenbreiten auch Werte wie 0cm;5cm;0cm;0cm enthalten. Die folgenden Zeilen ermitteln in jedem Fall die erste angezeigte Spalte. Dazu speichert die Funktion die Auflistung aus der Eigenschaft Spaltenbreiten in einem Array namens strcolumn- Widths(). Eine For...Next-Schleife durchläuft alle Indexwerte für die Anzahl der Spalten des Kombinationsfeldes, die wir mit ColumnCount ermitteln (und um 1 vermindern, da wir mit dem nullbasierten Index arbeiten wollen). In dieser Schleife tragen wir jeweils die Spaltenbreite aus dem Array strcolumnwidths in die Variable dblcolumn- Widths ein. Dies führt, wenn wir zwar zwei Spalten haben, aber die Eigenschaft Spaltenbreiten nur eine Spaltenbreite enthält (0cm), zu einem Fehler, weshalb wir die eingebaute Fehlerbehandlung deaktivieren und zuvor den Wert der Eigenschaft dblcolumnwidth auf 1 einstellen. So enthält dblcolumnwidth nicht nur nach dem ersten Auftreten einer Spaltenbreite größer als 0cm einen anderen Wert als 0, sondern auch dann, wenn keine explizite Spaltenbreite für die aktuell untersuchte Spalte mehr angegeben ist. Wir ermitteln also auf jeden Fall die erste Spalte, deren Spaltenbreite größer als 0 ist auch wenn die Breite nicht explizit angegeben wurde. In diesem Fall verlässt die Funktion die Schleife. Die folgende Anweisung speichert dann den Namen der Fields-Auflistung des Recordsets für den zuvor ermittelten Index in der Variablen strfieldcriteria. Dann folgt das Zusammenstellen des SQL-Ausdrucks mit den Werten der Variablen strboundcolumn, strboundtable, strfieldcriteria und strvalue, der etwa wie folgt aussehen könnte: LieferantID IN ( ) SELECT Firma FROM tbllieferanten WHERE Firma LIKE 'E*' Dieser Ausdruck wird dann als Funktionswert an die aufrufende Routine zurückgegeben. Das Ergebnis sieht dann genau wie gewünscht aus, wie Bild 6 zeigt. Einsatzzweck Der Sinn dieser Funktion ist, dass wir nun nur noch eine Funktion mit zwei leicht verständlichen Parametern aufrufen müssen, statt eine doch gerade für unerfahrene Entwickler komplizierte Abfrage von Hand zusammenzustellen. Sie können diese Funktion natürlich für verschiedene Kombinationsfelder im gleichen Formular einsetzen. Zusammenfassung und Ausblick Dieser Beitrag hat mehrere Möglichkeiten aufgezeigt, wie Sie die Datensätze eines Unterformulars nach dem angezeigten Wert von Kombinationsfeldern anzeigen können. Die zuletzt vorgestellte Funktion dient als Basis für eine Erweiterung der Lösung des Beitrags Schneller Filter ( Seite 31

34 ACCESS FORMULARE UND STEUERELEMENTE SCHNELLER FILTER Schneller Filter Formulare in der Datenblattansicht bieten alle Filter- und Sortiermöglichkeiten, die das Benutzerherz begehrt. Allerdings sind diese nicht unbedingt immer schnell erreichbar hier und da könnte es noch ein wenig fixer gehen. Ein Beispiel ist ein Filter, der nur die Datensätze anzeigt, die den Wert des aktuell markierten Feldes im jeweiligen Feld enthalten. Wenn Sie also etwa eine Reihe von Artikeln anzeigen, die einer bestimmten Kategorie angehören und schnell nur noch die Artikel dieser Kategorie sehen wollen, benötigen Sie dazu mehrere Mausklicks. Dieser Beitrag zeigt, wie Sie verschiedene Suchen mit einem einfachen Klick auf eine Schaltfläche erledigen. Die Datenblattansicht von Access-Formularen bietet eine Reihe von Möglichkeiten, schnell nach Daten zu suchen oder diese zu sortieren. Dazu klicken Sie einfach auf das nach unten zeigende Dreieck rechts im Spaltenkopf der jeweiligen Spalte. Hier sehen Sie auf Anhieb zwei Einträge zum Sortieren in verschiedenen Richtungen oder zum Selektieren verschiedener, im aktuellen Feld enthaltener Werte (s. Bild 1). Der Untereintrag Textfilter liefert etwa für Textfelder weitere Möglichkeiten: Hier können Sie beispielsweise nach Datensätzen suchen, deren markiertes Feld einen benutzerdefinierten Wert enthält. Bei Textfeldern können hier etwa die Vergleichsoperatoren Gleich, Nicht gleich, Beginnt mit, Beginnt Bild 2: Filtern nach benutzerdefinierten Vergleichswerten Bild 1: Filtern nach allen vorhandenen Werten nicht mit, Enthält und weitere verwendet werden (s. Bild 2). Andere Felddatentypen halten dem Datentyp entsprechende Vergleichskriterien bereit. Datensätze mit gleichem Feldwert finden Was aber hier fehlt, ist die einfache Möglichkeit, schnell alle Einträge anzuzeigen, die den gleichen Wert im zurzeit markierten Feld aufweisen wie der aktuelle Datensatz. Und genau diese Funktion wollen wir nun nachrüsten. Für das Feld Artikelna- Seite 32

35 FORMULARE UND STEUERELEMENTE SCHNELLER FILTER ACCESS me macht dies natürlich recht wenig Sinn, aber beim Lieferanten oder bei der Kategorie finden sich schnell Einsatzmöglichkeiten. Warum nicht beim Artikelnamen? Nun: Dabei handelt es sich um ein Feld mit einem eindeutigen Index. Da nur jeweils ein Datensatz mit dem aktuellen Wert vorhanden ist, macht es wenig Sinn, danach zu filtern... außer natürlich, wenn Sie etwa aus Gründen der Übersicht nur diesen einen Datensatz anzeigen möchten. Also nehmen wir diese einfache Variante einfach mit hinzu. Später wollen wir jedoch gerade für Textfelder noch eine schnelle Filterfunktion hinzufügen, mit der Sie sogar alle Datensätze anzeigen können, die den aktuell markierten Wert enthalten. Der Filter soll dann wie im Beispiel aus Bild 3 funktionieren. Der Benutzer markiert den Wert, nach dem die Daten gefiltert werden sollen, und klickt auf die Schaltfläche Schneller Filter. Daraufhin werden alle Datensätze ausgeblendet, deren Inhalt im betroffenen Feld nicht mit dem Vergleichswert übereinstimmt. Eine weitere Schaltfläche soll den Filter wieder deaktivieren. Schaltfläche zum schnellen Filtern Beginnen wir doch einfach mit einer Schaltfläche, die wir cmdschnellerfilter nennen und mit der Beschriftung Schneller Filter versehen. Diese soll die Datenherkunft des Unterformulars in dem Formular, in dem sich die Schaltfläche befindet, nach dem Wert des zuvor markierten Feldes filtern. Wie sich herausstellt, ist dies gar nicht so einfach, denn wir finden erst gar nicht heraus, welches Feld gerade überhaupt markiert war. Unsere erste Idee war es nämlich, das Steuerelement mit dem besagten Filtervergleichswert einfach über den Ausdruck Screen.PreviousControl.Name zu ermitteln und diesen per Debug.Print im Direktbereich auszugeben. Dazu haben wir die Beim Klicken-Ereignisprozedur der Schaltfläche cmdschnellerfilter wie folgt ausgestattet: Private Sub cmdschnellerfilter_click() Debug.Print Screen.PreviousControl.Name Das Ergebnis lieferte aber leider nicht das gesuchte Steuerelement, sondern den Namen des Unterformulars: Bild 3: So soll der Filter nach einem Feldwert arbeiten. Seite 33

36 ACCESS FORMULARE UND STEUERELEMENTE SCHNELLER FILTER sfmartikel_schnellerfilter Zuletzt aktives Feld ermitteln Wir stehen nun also vor dem Problem, zwar zum Zeitpunkt den Inhalt des zuletzt aktivierten Feldes im Unterformular zu benötigen, dieses aber nicht mehr zu kennen. Es gibt nun diverse Möglichkeiten, die gewünschte Information zu erhalten. Eine davon lautet, irgendwo eine Variable vorzuhalten, die wir mit einem Verweis auf das jeweils aktive Steuerelement des Unterformulars füllen und dann beim Mausklick auf die Schaltfläche cmd- SchnellerFilter über diese Variable auf das Feld und seinen Inhalt zugreifen. Das ist allerdings mit einigem Aufwand verbunden, wenn wir es auf dem einfachen Weg erledigen. Dieser sieht vor, eine Variable zum Speichern des zuletzt verwendeten Feldes im Klassenmodul des Hauptformulars zu deklarieren. Außerdem legen wir für jedes Steuerelement im Unterformular eine Ereigniseigenschaft namens Bei Fokuserhalt an und hinterlegen dafür eine Ereignisprozedur, welche einen Verweis auf das jeweilige Steuerelement in die Variable im Klassenmodul des Hauptformulars einträgt. Mit der Ereignisprozedur Beim Klicken der Schaltfläche cmdschnellerfilter können Sie dann aus der Variablen den Wert ermitteln und nach dem Feld, an welches das Steuerelement aus der Variablen gebunden ist, filtern. Wir müssen nur für jedes betroffene Steuerelement im Unterformular eine entsprechende Ereignisprozedur für das Ereignis Bei Fokuserhalt hinterlegen. Und ebenso für alle Steuerelemente der Unterformulare in anderen Formularen, die Sie mit der Funktion ausstatten möchten. Lösung mit Klasse Nun ist Access im Unternehmen aber weniger bekannt dafür, den Leser mit Fleißarbeit auszustatten. Wir suchen eher nach einer Lösung, die der Leser in wenigen Minuten implementieren kann. Also greifen wir, wie schon ein paar Mal geschehen, auf Klassenmodule zurück, in denen wir die gewünschte Funktionalität unterbringen. Das Klassenmodul des Hauptformulars soll nur mit wenigen Zeilen Code ausgestattet werden, die zum größten Teil in der Ereignisprozedur Form_Load landen. Insgesamt sieht der benötigte Code wie folgt aus. Als Erstes benötigen wir eine Objektvariable, welche den Verweis auf die gleich noch erläuterte Klasse clsfastfilter aufnimmt: Dim objfastfilter As clsfastfilter Als Nächstes folgt dann die Ereignisprozedur Form_Load, die wir folgt aussieht: Private Sub Form_Load() Set objfastfilter = New clsfastfilter With objfastfilter Set.Subform = Me!sfmArtikel_SchnellerFilter.Form Set.FastFilterButton = Me!cmdSchnellerFilter End With Sie erstellt zunächst ein neues Objekt auf Basis der Klasse clsfastfilter und speichert den Verweis darauf in der Variablen objfastfilter. Dann weist sie den beiden Eigenschaften Subform und FastFilterButton Verweise auf das Unterformular mit den zu durchsuchenden Datensätzen (Achtung: <Unterformularname>.Form liefert die richtige Referenz) und auf die Schaltfläche zu, welche den Filter erstellen soll. Zur Abrundung fügen Sie noch eine Ereignisprozedur für die Schaltfläche mit der Beschriftung Filter leeren hinzu, welche schlicht den Filter des Unterformulars leert und somit wieder alle Datensätze anzeigt: Private Sub cmdfilterleeren_click() Me!sfmArtikel_SchnellerFilter.Form.Filter = "" Wichtige Vorbereitung Wenn Sie Klassen erstellen, die Objekte wie etwa Formulare oder die enthaltenen Steuerelemente referenzieren und deren Ereignisse implementieren wollen, muss für das Seite 34

37 FORMULARE UND STEUERELEMENTE SCHNELLER FILTER ACCESS Ereignisprozeduren für das Steuerelement implementieren können. Desweiteren benötigen wir noch zwei weitere Variablen. Die erste ist eine Collection und nimmt die Instanzen der Wrapper-Klassen auf, von denen wir für jedes filterbare Steuerelement eine erstellen und in die Collection schreiben: Private colcontrols As Collection Bild 4: Hinzufügen eines Klassenmoduls per Eigenschaft jeweilige Formular (und somit auch für Unterformulare) auch ein Klassenmodul vorliegen! In unserem Fall haben wir etwa für das Unterformular mit dem Datenblatt noch kein Klassenmodul angelegt. Dies erfolgt automatisch, sobald Sie für eine der Ereigniseigenschaften des Formulars ein Ereignis anlegen und dieses über die Schaltfläche mit den drei Punkten im VBA-Editor öffnen. Sie können dies aber auch durch einfaches Einstellen der Eigenschaft Enthält Modul erledigen. Diese Eigenschaft finden Sie im Reiter Andere des jeweiligen Formulars (s. Bild 4). Die Klasse clsfastfilter Diese Klasse ist die Steuerzentrale der Lösung. Sie nimmt die Verweise auf die beteiligten Elemente entgegen, also das Unterformular sowie die Schaltfläche zum Auslösen des Filters. Das Unterformular wird mit der folgenden Variablen referenziert, die im Kopf des Klassenmoduls deklariert wird: Private m_subform As Form Die Schaltfläche zum Auslösen des Filters landet per Verweis in dieser Variablen: Private WithEvents m_fastfilterbutton As CommandButton Die Variable ist mit dem Schlüsselwort WithEvents deklariert, was dafür sorgt, dass wir in diesem Klassenmodul Außerdem brauchen wir noch die besagte Variable, welche das zuletzt durch den Benutzer angeklickte Steuerelement im Unterformular aufnimmt: Private m_currentcontrol As Control Damit die Wrapper-Objekte, die jeweils eines der gebundenen Steuerelemente im Unterformular aufnehmen, einen Verweis auf das zuletzt durch den Benutzer angeklickte Element in die Variable m_currentcontrol schreiben können, stellen wir eine Property Set-Methode in der Klasse clsfastfilter bereit, die wie folgt aussieht: Public Property Set CurrentControl(ctl As Control) Set m_currentcontrol = ctl End Property Damit wir die Funktion, welche die Schaltfläche cmdfast- Filter auslöst, auch in der Klasse clsfastfilter unterbringen können und diese nicht in jedem neuen Formular erneut schreiben müssen, füllen wir die lokale Variable m_fastfilterbutton über die Property Set-Prozedur FastFilterButton mit einem Verweis auf die jeweilige Schaltfläche. Die Prozedur sieht so aus: Public Property Set FastFilterButton(cmd As CommandButton) Set m_fastfilterbutton = cmd cmd.onclick = "[Event Procedure]" End Property Sie erwartet einen Verweis auf die Schaltfläche als Parameter und trägt diese in die Variable m_fastfilterbutton Seite 35

38 ACCESS FORMULARE UND STEUERELEMENTE SCHNELLER FILTER ein. Außerdem legt sie noch fest, dass in diesem Klassenmodul eine Implementierung des Ereignisses On- Click vorliegen könnte und beim Auslösen entsprechend berücksichtigt werden soll. Die Hauptarbeit in der Klasse übernimmt die Property Set-Methode Subform, mit welcher die Form_Load- Ereignisprozedur der Klasse clsfastfilter das zu verwendende Unterformular zuweist. Sie erwartet das Formular als Parameter und sieht wie folgt aus: Public Property Set Subform(frm As Form) If Len(strControlSource) > 0 Then End If Next ctl End Property Set objfastfiltercontrol = µ New clsfastfiltercontrol With objfastfiltercontrol Set.Control = ctl Set.MyParent = Me colcontrols.add objfastfiltercontrol End With Dim ctl As Control Die Prozedur stellt zunächst die Variable m_subform Dim objfastfiltercontrol As clsfastfiltercontrol auf das übergebene Formular ein. Dann instanziert sie Dim strcontrolsource As String eine neue Collection und speichert diese in der Variablen Set m_subform = frm colcontrols. Schließlich durchläuft sie alle Steuerelemente des mit frm angegebenen Unterformulars. In der dafür Set colcontrols = New Collection For Each ctl In m_subform.controls verwendeten For Each-Schleife prüft die Prozedur, ob es strcontrolsource = "" sich beim aktuell durchlaufenen Steuerelement überhaupt On Error Resume Next um ein gebundenes Steuerelement handelt. Andere Steuerelemente wie etwa Bezeichnungsfelder brauchen wir strcontrolsource = ctl.controlsource On Error GoTo 0 gar nicht zu berücksichtigen. Dazu leert die Prozedur eine Variable namens strcontrolsource und versucht Private Sub m_fastfilterbutton_click() Dim rst As DAO.Recordset dann, diese bei deaktivierter Fehlerbehandlung Dim fld As DAO.Field Dim strcontrolsource As String mit dem Wert der Eigenschaft ControlSource des strcontrolsource = m_currentcontrol.controlsource Set rst = m_subform.recordset aktuellen Steuerelements Select Case rst.fields(strcontrolsource).type aus ctl zu füllen. Enthält Case dbtext m_subform.filter = strcontrolsource & " = '" _ strcontrolsource danach & Replace(m_CurrentControl.Value, "'", "''") & "'" keine leere Zeichenkette, m_subform.filteron = True handelt es sich um ein Case dbdate gebundenes Steuerelement m_subform.filter = strcontrolsource & " = " & CDbl(m_CurrentControl.Value) und es kann in Form des m_subform.filteron = True Wrapper-Objekts auf Basis Case Else m_subform.filter = strcontrolsource & " = " & m_currentcontrol.value der Klasse clsfastfilterm_subform.filteron = True Control referenziert und End Select zur Collection colcontrols hinzugefügt werden. Dann Listing 1: Implementierung des Beim Klicken-Ereignisses der Filter-Schaltfläche erstellt die Prozedur ein Seite 36

39 FORMULARE UND STEUERELEMENTE SCHNELLER FILTER ACCESS neues Objekt auf Basis von clsfastfiltercontrol, füllt dessen Eigenschaft Control mit einem Verweis auf das aktuelle Steuerelement und die Eigenschaft MyParent mit einem Verweis auf sich selbst, also die Instanz der Klasse clsfastfilter. Wozu dies nötig ist, erfahren Sie gleich bei der Beschreibung der Klasse clsfastfiltercontrol. Nun müssen wir nur noch dafür sorgen, dass das Wrapper- Objekt, das ja lokal innerhalb der Property Set-Prozedur deklariert wurde und anderenfalls mit dem Ende der Prozedur erlöschen würde, nicht im Nirwana verschwindet. Dazu fügt die Prozedur es zur Collection colcontrols hinzu. Gehen wir an dieser Stelle vereinfachend davon aus, dass die Wrapper-Objekte beim Anklicken durch den Benutzer dafür sorgen, dass ein Verweis auf das angeklickte gebundene Steuerelement in der Variablen m_currentcontrol landet. Dann können wir uns die Ereignisprozedur ansehen, die durch einen Mausklick auf die mit der Variablen m_fast- FilterButton referenzierte Schaltfläche ausgelöst wird. Die Prozedur sieht wie in Listing 1 aus. Sie liest zunächst den Namen des Feldes, das dem mit der Variablen m_currentcontrol referenzierten und zuletzt durch den Benutzer angeklickten Steuerelement angehört, in die Variable strcontrolsource ein. Dann füllt sie eine Recordset-Variable namens rst mit dem Recordset des zu filternden Unterformulars. Sie ermittelt dann für das Element der Fields-Auflistung mit dem Namen aus strcontrolsource den Datentyp und gleicht diesen in einer Select Case-Bedingung mit verschiedenen Werten ab. Wir haben hier nur dbtext für Textfelder, dbdate für Datumsfelder und alle übrigen Datentypen untersucht, obwohl hier noch weitere Unterscheidungen möglich wären. Im Falle des Wertes dbtext stellt die folgende Anweisung einen Vergleichsausdruck zusammen, der aus dem Feldnamen aus strcontrolsource, dem Gleichheitszeichen und dem in Hochkommata eingefassten Wert des Steuerelements aus m_current- Control besteht, also etwa Artikelname = 'Chai'. Dieser Vergleichsausdruck landet in der Eigenschaft Filter des Unterformulars. Das Einstellen einer weiteren Eigenschaft namens FilterOn auf den Wert True sorgt schließlich dafür, dass der Filter auch auf die aktuellen Daten angewendet wird. Wichtig ist an dieser Stelle noch der Hinweis auf das eventuelle Auftreten von Hochkommata oder Anführungszeichen innerhalb der Zeichenkette. Diese führen beim Zusammensetzen des Filterausdrucks zu Fehlern, was nicht geschieht, wenn Sie diese verdoppeln in diesem Fall durch entsprechenden Einsatz der Replace-Funktion. Im Falle eines Datums würde, wenn wir dieses einfach wie eine Zahl behandeln, ein Ausdruck wie Auslaufdatum = verwendet, was zu einem Fehler führt. Daher wandeln wir den Wert des Datumsfeldes zuvor mit der CDbl-Funktion in einen Double-Wert um, der dann keinen Fehler mehr auslöst. Für alle übrigen Datentypen soll einfach das Feld mit dem jeweiligen Wert verglichen werden. Die Klasse clsfastfiltercontrol Es fehlt noch ein genauerer Blick auf die Klasse clsfast- FilterControl. Diese wird für jedes gebundene Steuerelement im Unterformular genau einmal instanziert, denn wir wollen ja für jedes dieser Steuerelemente das Ereignis Bei Fokuserhalt implementieren, um dort das aktuelle Steuerelement in der Variablen m_currentcontrol des Objekts basierend auf der Klasse clsfastfilter zu speichern. Die Klasse clsfastfiltercontrol soll zunächst einmal einen Verweis auf das erstellende Objekt speichern, hier also das Objekt auf Basis der Klasse clsfastfilter. Dazu verwenden wir die folgende Variable: Private m_parent As clsfastfilter Die benötigte Property Set-Methode sieht so aus: Seite 37

40 ACCESS FORMULARE UND STEUERELEMENTE SCHNELLER FILTER Public Property Set MyParent(obj As clsfastfilter) Set m_parent = obj End Property Außerdem wollen wir bei den Steuerelementen des Datenblatts unter den Steuerelementtypen Textfeld, Kombinationsfeld und Kontrollkästchen unterscheiden (andere Steuerelemente machen im Datenblatt auch keinen Sinn). Daher deklarieren wir die folgenden drei Variablen innerhalb der Klasse, da wir ja noch nicht wissen, welchen Steuerelementtyp die Klasse aufnehmen soll: Private WithEvents txt As TextBox Private WithEvents cbo As ComboBox Private WithEvents chk As CheckBox Nun fehlt noch die Property Set-Methode, die das zu untersuchende Steuerelement entgegennimmt und einige weitere Schritte durchführt. Diese sieht wie folgt aus: Public Property Set Control(ctl As Control) Select Case ctl.controltype Case actextbox Set txt = ctl txt.ongotfocus = "[Event Procedure]" Case accombobox Set cbo = ctl cbo.ongotfocus = "[Event Procedure]" Case accheckbox End Select End Property Set chk = ctl chk.ongotfocus = "[Event Procedure]" Die Prozedur prüft zunächst anhand der Eigenschaft ControlType, um welchen Typ es sich handelt. Im Falle von actextbox füllt sie beispielsweise die Variable txt mit einem Verweis auf das mit dem Parameter ctl gelieferte Steuerelement. Außerdem stellt sie die Eigenschaft OnGotFocus auf den Wert [Event Procedure] ein, was im Eigenschaftsfenster des Formulars dem Einstellen der Eigenschaft Bei Fokuserhalt auf [Ereignisprozedur] entspricht. Für die beiden anderen Typen, die mit den Konstanten dbcombobox und dbcheckbox geprüft werden, verläuft dies ähnlich. Schließlich müssen wir noch das Ereignis Bei Fokuserhalt für die drei deklarierten Objektvariablen für die drei Steuerelementtypen Textfeld, Kombinationsfeld und Kontrollkästchen implementieren, was wir wie folgt erledigen: Private Sub cbo_gotfocus() Set m_parent.currentcontrol = cbo Private Sub chk_gotfocus() Set m_parent.currentcontrol = chk Private Sub txt_gotfocus() Set m_parent.currentcontrol = txt In diesen drei Prozeduren weisen wir jeweils der Eigenschaft CurrentControl des in m_parent gespeicherten Objekts (also unsere Instanz der Klasse clsfastfilter) eine Referenz auf das auslösende Steuerelement zu, die dann wiederum in der Variablen m_currentcontrol der Klasse clsfastfilter landet. Damit ist die Programmierung der grundlegenden Funktion bereits erledigt: Wir können das Datenblatt nun nach den Werten einzelner Felder filtern. Filtern nach»enthält...«für den Einsatz mit Textfeldern wäre es noch interessant, wenn wir auch Vergleiche mit Teilausdrücken erlauben würden. Wie wäre es etwa, wenn Sie nur ein paar Buchstaben des Feldinhalts eines Textfeldes markieren und danach in allen Datensätzen suchen wollten? Das wäre schon praktisch, also machen wir uns an die Arbeit. Zu diesem Zweck fügen wir dem Formular eine weitere Schaltfläche namens cmdschnellerfilterteilausdruck hinzu. Die Prozedur Form_Load erweitern wir entsprechend um die Zuweisung dieser Schaltfläche zur gleich Seite 38

41 FORMULARE UND STEUERELEMENTE SCHNELLER FILTER ACCESS Private Sub m_fastfiltercontainsbutton_click() Dim rst As DAO.Recordset Dim strcontrolsource As String strcontrolsource = m_currentcontrol.controlsource Set rst = m_subform.recordset Select Case rst.fields(strcontrolsource).type Case dbtext m_subform.filter = strcontrolsource & " LIKE '*" & Replace(m_Searchstring, "'", "''") & "*'" m_subform.filteron = True Case Else MsgBox "Dieses Feature funktioniert nur mit Textfeldern." End Select Listing 2: Implementierung des Beim Klicken-Ereignisses der Schaltfläche cmdschnellerfilterteilausdruck noch anzulegenden Eigenschaft in der Klasse clsfast- Filter: Im gleichen Zuge fügen wir eine Variable hinzu, welche die Teilzeichenkette speichert: Private Sub Form_Load() Set objfastfilter = New clsfastfilter With objfastfilter... Set.FastFilterContainsButton = µ Me!cmdSchnellerFilterTeilausdruck End With Private m_searchstring As String Um diese einzustellen, legen wir eine öffentliche Eigenschaft per Property Let-Prozedur fest: Public Property Let Searchstring(str As String) m_searchstring = str End Property Die Klasse clsfastfilter erhält eine neue Variable zum Speichern der Schaltfläche: Private WithEvents m_fastfiltercontainsbutton µ As CommandButton Um diese Schaltfläche der Variablen zuzuweisen, benötigen wir eine entsprechende Property Set-Prozedur: Public Property Set FastFilterContainsButton(µ Set m_fastfiltercontainsbutton = cmd With cmd.onclick = "[Event Procedure]" End With End Property cmd As CommandButton) Damit die Schaltfläche cmdschnellerfilterteilausdruck auch etwas bewirkt, legen wir die Ereignisprozedur aus Listing 2 in der Klasse clsfastfilter an. Diese berücksichtigt nur Steuerelemente, deren gebundenes Feld den Felddatentyp dbtext aufweist. Für diese stellt es einen Vergleichsausdruck zusammen, der aus dem Feldnamen, dem LIKE-Operator sowie dem Vergleichswert besteht. Dieser stammt aus der Variablen m_searchstring, bei dem eventuell enthaltene Hochkommata verdoppelt wurden. Der Vergleichswert wird außerdem in Sternchen und einfache Anführungszeichen eingeschlossen. Wenn der Benutzer nun etwa die Zeichenkette an des Artikels Chang markiert und auf die Schaltfläche klickt, soll der Filterausdruck so aussehen: Artikelname LIKE '*an*' Seite 39

42 ACCESS FORMULARE UND STEUERELEMENTE SCHNELLER FILTER Das Ergebnis sieht dann etwa wie in Bild 5 aus. Erweiterung der Klasse clsfastfiltercontrol Eine Kleinigkeit fehlt natürlich noch: Wir müssen noch dafür sorgen, dass nach der Markierung eines Teilausschnitts eines Textfeldes auch der entsprechende Ausschnitt in der Variablen m_searchstring der Klasse clsfastfilter landet. Leider können wir die notwendigen Zeilen nicht in der Ereignisprozedur txt_gotfocus unterbringen, die ja bereits das zuletzt aktive Steuerelement in die Variable m_currentcontrol einträgt. Der Grund ist, dass dieses Ereignis ja bereits beim Fokuserhalt des Textfeldes ausgelöst wird und nicht erst, wenn der Benutzer den gewünschten Ausschnitt markiert hat. Welches andere Ereignis können wir dafür nutzen? Bei Fokusverlust wäre eine erste Idee, aber die hilft nicht weiter, weil das Beim Klicken-Ereignis der Schaltfläche vorher feuert. Also müssen wir ein wenig mehr ins Detail gehen und die Mausund Tastatur-Ereignisse hinzuziehen. Der Abschluss einer Markierung wird immer entweder mit dem Loslassen der Maustaste oder der Tastatur abgeschlossen. Also nutzen wir einfach die beiden Ereignisse Bei Maustaste auf und Bei Taste auf. Zunächst füllen wir die entsprechenden Ereignisprozeduren in der Property Set-Prozedur Control: Public Property Set Control(ctl As Control) Select Case ctl.controltype Case actextbox Set txt = ctl txt.ongotfocus = "[Event Procedure]" Bild 5: Filtern nach einem Teilausdruck einer Zeichenkette '... End Select End Property txt.onkeyup = "[Event Procedure]" txt.onmouseup = "[Event Procedure]" Die beiden Ereignisprozeduren sehen nun wie folgt aus: Private Sub txt_keyup(keycode As Integer, Shift As Integer) SetSearchstring Private Sub txt_mouseup(button As Integer, µ SetSearchstring Shift As Integer, X As Single, Y As Single) Beide würden die gleichen Anweisungen enthalten, also haben wir diese in die Prozedur SetSearchstring ausgelagert: Private Sub SetSearchstring() If txt.sellength > 0 Then Seite 40

43 FORMULARE UND STEUERELEMENTE SCHNELLER FILTER ACCESS Else End If m_parent.searchstring = Mid(txt.Value, µ txt.selstart + 1, txt.sellength) m_parent.searchstring = Nz(txt.Value, "") Die Prozedur prüft anhand des Wertes der Eigenschaft SelLength des Textfeldes, ob überhaupt eine Markierung vorliegt. Falls ja, trägt sie für die Eigenschaft Search- String der Klasse clsfastfilter eine Zeichenkette ein, die dem markierten Text entspricht. Diesen ermitteln wir, indem wir vom kompletten Text des Textfeldes den Teil mit der Mid-Funktion auslesen, der an der Position des Starts der Markierung beginnt (txt.selstart + 1) und die Länge der Markierung aufweist (txt.sellength). Sollte keine Markierung im Text vorhanden sein, landet einfach der komplette Text in der Eigenschaft Search- String der Klasse clsfastfilter und somit in der Variablen m_searchstring dieser Klasse. Filtern nach Zeichenketten von Lookupfeldern Machen wir es nun ein wenig interessanter: Wir wollen nun auch nach Teilzeichenketten in Kombinationsfeldern suchen. So möchten wir also beispielsweise alle Lieferanten anzeigen, deren Name mit A beginnt. Wie dies gelingt, sehen Sie in der Beispieldatenbank. Die grundlegende Technik erläutern wir im Beitrag Lookup-Kombinationsfelder nach Texten filtern ( In unserem Fall müssen wir zunächst die Klasse clsfast- FilterControl so erweitern, dass auch Kombinationsfelder die Ereignisse Bei Taste auf und Bei Maustaste auf auslösen. Dazu erweitern wir die Property Set-Prozedur Control wie folgt: Public Property Set Control(ctl As Control) Select Case ctl.controltype... Case accombobox Set cbo = ctl cbo.ongotfocus = "[Event Procedure]" cbo.onkeyup = "[Event Procedure]" cbo.onmouseup = "[Event Procedure]"... End Select End Property Diese beiden Ereignisprozeduren implementieren Sie wie folgt: Private Sub cbo_keyup(keycode As Integer, Shift As Integer) SetSearchstringComboBox Private Sub cbo_mouseup(button As Integer, µ Shift As Integer, X As Single, Y As Single) SetSearchstringComboBox Wie Sie sehen, verwenden wir hier den Methodennamen SetSearchstringComboBox, wo wir bei den entsprechenden Methoden für die Textfelder nur SetSearchstring genutzt haben. Dementsprechend benennen wir SetSearchstring in SetSearchstringTextbox um (und passen die Aufrufe an). Die Prozedur SetSearchstringComboBox sieht wie in Listing 3 aus. Sie prüft, ob das Kombinationsfeld eine Markierung enthält oder nicht. Im ersten Fall stellt sie den Wert der Eigenschaft Searchstring der Klasse clsfast- Filter mithilfe der Funktion LookupSearchstring zusammen, die wir im oben genannten Beitrag ausführlich abbilden und erläutern. Der Searchstring enthält dabei unkonsequenterweise nicht mehr nur den Vergleichswert wie etwa *Ge*, sondern gleich den kompletten Filterausdruck wie folgt: KategorieID IN (SELECT KategorieID FROM tblkategorien WHERE Kategoriename LIKE '*Ge*') Seite 41

44 ACCESS FORMULARE UND STEUERELEMENTE SCHNELLER FILTER Private Sub SetSearchstringCombobox() If cbo.sellength > 0 Then Else End If m_parent.searchstring = LookupSearchstring(cbo, "*" & Mid(cbo.Text, cbo.selstart + 1, cbo.sellength) & "*") m_parent.searchstring = LookupSearchstring(cbo, "*" & Nz(cbo.Text, "") & "*") Listing 3: Methode zum Zusammenstellen des Filterausdrucks für ein Kombinationsfeld mit markierter Teilzeichenfolge Diese kleine Ungenauigkeit gleichen wir in der Anpassung der Prozedur m_fastfiltercontainsbutton_click wieder aus (s. Listing 4). Hier haben wir die Prüfung, ob es sich bei dem geprüften Feld um eines mit dem Datentyp db- Text handelt, durch eine andere Prüfung ersetzt. Nun untersuchen wir den Steuerelementtyp des Steuerelements aus dem Unterformular, das zum Zeitpunkt des Klicks auf die Schaltfläche aus m_fastfiltercontainsbutton aktiv war. Im Falle einer Textbox ändert sich nichts, wir verwenden die bereits zuvor für den Fall des Datentyps actext genutzten Anweisungen. Wenn es sich jedoch um ein Kombinationsfeld handelt (accombobox), dann stellt die Prozedur nicht wie beim Textfeld erst den Filterausdruck zusammen, sondern übernimmt diesen direkt komplett aus der Variablen m_searchstring und trägt diesen in die Eigenschaft Filter ein, bevor dieser mit FilterOn = True aktiviert wird. Private Sub m_fastfiltercontainsbutton_click() Dim rst As DAO.Recordset Dim strcontrolsource As String strcontrolsource = m_currentcontrol.controlsource Set rst = m_subform.recordset Select Case m_currentcontrol.controltype Case actextbox m_subform.filter = strcontrolsource & " LIKE '*" _ & Replace(m_Searchstring, "'", "''") & "*'" m_subform.filteron = True Case accombobox End Select m_subform.filter = m_searchstring m_subform.filteron = True Listing 4: Erweiterung der Ereignisprozedur für FastFilterContainsButton Weitere Möglichkeiten: Filter kombinieren Nun stellt sich die Frage, ob wir diese hilfreichen Filterausdrücke nicht auch in Kombination nutzen können. Bislang ersetzen wir den Wert der Eigenschaft Filter des Unterformulars immer durch den neuen Wert, aber berücksichtigen den vorherigen Wert nicht mehr. Wie cool wäre es denn, wenn wir entscheiden könnten, ob wir den Filter neu setzen oder aber diesen per AND- oder OR- Parameter mit dem bestehenden Filter verknüpfen? Wir müssten allerdings vorher noch einige Modalitäten festlegen. Die erste ist: Wollen wir die Verknüpfung per AND oder OR oder sogar beide Möglichkeiten bereitstellen? Wenn wir nur eine der beiden anbieten, können wir die einzelnen Bedingungen einfach durch das entsprechende Schlüsselwort voneinander getrennt aneinanderhängen. Wenn wir sowohl AND als auch OR bereitstellen wollen, müssen wir uns überlegen, wie die Priorität gesetzt wird. Wenn der Benutzer willkürlich AND und OR durcheinandermischt, liefert dies vermutlich nur schwer kontrollierbare Ergebnisse. Also lassen wir diese Variante direkt außen vor. Die zweite Überlegung ist: Wie soll der Benutzer uns mitteilen, ob er die aktuell hinzuzufügende Bedingung zusätzlich zur bestehenden Bedingung nutzen oder die bestehende Bedingung ersetzen möchte? Am einfachsten ist wohl ein Kontrollkästchen, mit dem der Benutzer den Modus einstellen kann. Dies erledigen wir, indem Seite 42

45 FORMULARE UND STEUERELEMENTE SCHNELLER FILTER ACCESS Bild 6: Steuerelemente zur Angabe, ob Filterausdrücke kombiniert werden sollen wir einfach ein Kontrollkästchen namens chkfilterkombinieren hinzufügen. Ein zweites Steuerelement könnte dem Benutzer ermöglichen, festzulegen, ob er mit AND oder OR verknüpfen möchte. Dazu eignet sich wohl am besten eine Optionsgruppe namens ograndoror mit zwei entsprechenden Optionsschaltflächen. Im Entwurf sieht das Formular nun wie in Bild 6 aus. Das Kontrollkästchen chkfilterkombinieren erhält den Standardwert False. Die Optionsgruppe erhält den Standardwert 1 (die beiden Optionen sollen die Werte 1 und 2 aufweisen). Damit die Optionsgruppe deaktiviert ist, wenn das Kontrollkästchen chkfilterkombinieren den Wert False enthält, legen wir folgende Ereignisprozedur an, die durch das Ereignis Nach Aktualisierung des Kontrollkästchens ausgelöst wird: Private Sub chkfilterkombinieren_afterupdate() Me!ogrANDOrOR.Enabled = Me!chkFilterKombinieren Der Klasse clsfastfilter fügen wir zwei Variablen hinzu, mit denen wir die beiden neuen Steuerelemente referenzieren können: Private m_combinefilter As CheckBox Private m_andoror As OptionGroup Diese Variablen können vom instanzierenden Formular über die folgenden beiden Property Set-Eigenschaften zugewiesen werden: Public Property Set CombineFilterCheckbox(chk As CheckBox) Set m_combinefilter = chk End Property Public Property Set ANDOrOROptiongroup(ogr As OptionGroup) Set m_andoror = ogr End Property Nun passen wir als Erstes die Ereignisprozedur m_fast- FilterButton_Click an, die ja die Filterausdrücke für komplette Inhalte zusammenstellt. Die neue Version finden Sie in Listing 5. Die Prozedur ermittelt nach wie vor den Namen des gebundenen Feldes des zum Zeitpunkt der Betätigung der Schaltfläche aktiven Steuerelements im Unterformular und füllt eine Recordset-Variable mit dem Recordset des Unterformulars. Dann prüft es den Datentyp des enthaltenen Feldes und stellt den Filterausdruck zusammen. Dieser landet jedoch nicht direkt in der Eigenschaft Filter des Unterformulars, sondern in der Variablen strfilter. Bevor der Filterausdruck angewendet wird, müssen Sie noch prüfen, ob die Filter-Eigenschaft bereits einen Filterausdruck enthält, und den neuen Ausdruck gegebenenfalls an den bestehenden Ausdruck anhängen. Dies erledigen wir mit der Funktion CombineFilter aus Listing 6. Diese Seite 43

46 ACCESS FORMULARE UND STEUERELEMENTE SCHNELLER FILTER Private Sub m_fastfilterbutton_click() Dim rst As DAO.Recordset Dim strcontrolsource As String Dim strfilter As String strcontrolsource = m_currentcontrol.controlsource Set rst = m_subform.recordset Select Case rst.fields(strcontrolsource).type Case dbtext strfilter = strcontrolsource & " = '" & Replace(m_CurrentControl.Value, "'", "''") & "'" m_subform.filter = CombineFilter(strFilter) m_subform.filteron = True Case dbdate strfilter = strcontrolsource & " = " & CDbl(m_CurrentControl.Value) m_subform.filter = CombineFilter(strFilter) m_subform.filteron = True Case Else End Select strfilter = strcontrolsource & " = " & m_currentcontrol.value m_subform.filter = CombineFilter(strFilter) m_subform.filteron = True Listing 5: Angepasste Version der Methode m_fastfilterbutton_click für kombinierte Filter erwartet lediglich den neuen Filterausdruck aus strfilter als Parameter. Private Function CombineFilter(strFilter As String) As String Dim strandoror As String If m_combinefilter Is Nothing Then Else CombineFilter = strfilter If Not m_combinefilter Then Else End If End If End Function CombineFilter = strfilter If Not Len(m_Subform.Filter) = 0 Then If m_andoror = 1 Then Else End If End If strandoror = " AND " strandoror = " OR " CombineFilter = m_subform.filter & strandoror & strfilter Listing 6: Diese Funktion fügt Filterausdrücke zusammen Sie prüft zunächst, ob die Variable m_combinefilter überhaupt gefüllt ist denn es kann ja auch sein, dass der Entwickler dieses Feature für das aktuelle Formular gar nicht implementiert und somit kein entsprechendes Kontrollkästchen angegeben hat. Anderenfalls untersucht die Funktion, ob das in der Variablen m_combinefilter referenzierte Steuerelement den Wert False enthält, was der Fall ist, wenn der Benutzer das Kontrollkästchen chkfilterkombinieren nicht auf den Wert True eingestellt hat. Ist m_combinefilter also False, dann wird der Rückgabewert der Funktion einfach mit dem Inhalt von strfilter gefüllt. Dieser landet dann in der Prozedur m_fastfilterbutton in der Filter-Eigenschaft des Unterformulars, woraufhin nur noch der Filter mit FilterOn = True aktiviert werden muss. Seite 44

47 FORMULARE UND STEUERELEMENTE SCHNELLER FILTER ACCESS Es kann jedoch auch geschehen, dass der Benutzer das Kontrollkästchen chkfilterkombinieren aktiviert hat. In diesem Fall kommt der zweite Teil der If...Then-Bedingung in der Funktion CombineFilter zum Einsatz. Hier prüft die Funktion zuerst, ob für das Unterformular bereits ein Filter festgelegt wurde (Len(m_Subform.Filter) = 0). Falls ja, untersucht die nächste If...Then-Bedingung, ob der Benutzer die Filter-Bedingungen mit AND oder OR verknüpfen möchte. Die dafür benötigte Information finden wir in der Variablen m_andoror, die wiederum aus der Optionsgruppe ograndoror gefüllt wird. Ist dort der Wert 1 eingestellt, soll der AND-Operator verwendet werden, sonst der OR-Operator. Im jeweiligen Fall wird einer der beiden Operatoren in die Variable strandoror eingetragen. Diese landet später wiederum als Bestandteil des neuen, kombinierten Ausdrucks in der Variablen CombineFilter, die gleichzeitig als Rückgabewert der Funktion dient. man doch auch prüfen, ob der Benutzer die Einfügemarke einfach nur in das Feld gesetzt hat, um mit dem kompletten Ausdruck zu vergleichen, oder ob er einen Teil des Textes markiert hat? Der Grund ist, dass die beiden Schaltflächen ja nicht nur erstens komplette Einträge und zweitens Teileinträge zum Filter hinzufügen sollen, sondern die zweite Schaltfläche für die Teileinträge stellt ja auch einen Filter ein, nach dem nur die Elemente durchsucht werden, die den Teilausdruck enthalten und nicht komplett mit ihm übereinstimmen. SQL-Ausdruck anzeigen Zur Orientierung der Benutzer könnte man den aktuellen Filter noch in einem Textfeld anzeigen, das beispielsweise oberhalb des Datenblatts platziert ist (s. Bild 7). Dieses Textfeld berücksichtigen wir wieder mit einer entsprechenden Variablen in der Klasse clsfastfilter: Von der Ereignisprozedur m_fastfiltercontainsbutton_click, die in unserem Beispiel beim Klick auf die Schaltfläche cmdschnellerfilterteilausdruck ausgelöst wird, rufen wir die Funktion CombineFilter auf die gleiche Weise auf und stellen damit den Filterausdruck zusammen (s. Listing 7). Wir können so also auch die beiden unterschiedlichen Schaltflächen zur Case actextbox Definition von Filterausdrücken miteinander kombinieren. Warum zwei Schaltflächen? Eines aber stört noch: Warum sollen wir überhaupt mit zwei Schaltflächen arbeiten immerhin könnte Private Sub m_fastfiltercontainsbutton_click() Dim rst As DAO.Recordset Dim strcontrolsource As String Dim strfilter As String Private m_filtertextbox As TextBox strcontrolsource = m_currentcontrol.controlsource Set rst = m_subform.recordset Select Case m_currentcontrol.controltype Dort legen wir auch die Property Set-Eigenschaft fest: strfilter = strcontrolsource & " LIKE '*" _ & Replace(m_Searchstring, "'", "''") & "*'" m_subform.filter = CombineFilter(strFilter) m_subform.filteron = True Case accombobox End Select strfilter = m_searchstring m_subform.filter = CombineFilter(strFilter) m_subform.filteron = True Listing 7: Angepasste Version der Methode m_fastfiltercontainsbutton_ok für kombinierte Filter Seite 45

48 ACCESS FORMULARE UND STEUERELEMENTE SCHNELLER FILTER Public Property Set FilterTextbox(txt As TextBox) Set m_filtertextbox = txt txt.enabled = False txt.locked = True End Property Damit das Textfeld mit dem Filterausdruck gefüllt wird, fügen wir zu den beiden Ereignisprozeduren m_fastfilterbutton_click und m_fastfiltercontainsbutton_click die folgenden Zeilen am Ende hinzu: Bild 7: Textfeld, das den aktuellen Filterausdruck anzeigt Optionsgruppe, mit der AND oder OR festgelegt wird (optional) Textfeld zur Ausgabe des Filterausdrucks (optional) If Not m_filtertextbox Is Nothing Then End If m_filtertextbox = m_subform.filter Lösung einsetzen Wenn Sie diese Lösung in eigenen Anwendungen einsetzen wollen, fügen Sie die beiden Klassen clsfastfilter und clsfastfiltercontrol zur Anwendung hinzu. Außerdem benötigen Sie ein Formular mit den folgenden Steuerelementen: Unterformular, das gefiltert werden soll, in der Datenblattansicht Schaltfläche zum Filtern nach dem kompletten Feldinhalt Schaltfläche zum Filtern nach einem Teilinhalt (optional) Schaltfläche zum Leeren des Filters (optional) Kontrollkästchen zur Angabe, ob Filter kombiniert werden sollen (optional) Die Steuerelemente legen Sie dann in der Beim Laden- Ereignisprozedur des Formulars fest. Beispiele finden Sie in den Formularen frmkunden, frmartikel und frmpersonal (Letzteres verwendet beispielsweise die Minimalkonfiguration). Davon abgesehen müssen Sie nur die Objektvariable für die Klasse clsfastfilter deklarieren hier in der minimalen Einstellung: Dim objfastfilter As clsfastfilter Private Sub Form_Load() Set objfastfilter = New clsfastfilter With objfastfilter Set.FastFilterButton = Me!cmdFiltern Set.Subform = Me!sfmPersonal.Form End With Zusammenfassung und Ausblick Dieser Beitrag zeigt eine Lösung, mit der Sie einem Formular samt Unterformular eine schnelle Möglichkeit zum Filtern des Unterformular-Inhalts nach den dort enthaltenen Werten hinzufügen können. Dank der Kapselung der Funktionalität in eine Klasse sind nur wenige Zeilen Code nötig, um die Features zum Formular hinzuzufügen. Seite 46

49 INTERAKTIV ACCESS PER URL STARTEN ACCESS Access per URL starten Ein interessanter Anwendungsfall ist das Starten einer Access-Anwendung über den Aufruf einer URL etwa im Browser zum Beispiel von einer Internetanwendung aus, die einen speziell vorbereiteten Link enthält. Die Betätigung dieses Links soll dann Access starten, die gewünschte Datenbank öffnen und gegebenenfalls sogar noch einen oder mehrere Parameter an die Anwendung übergeben. Wie das gelingt, schauen wir uns im vorliegenden Beitrag an. Im konkreten Fall schickte uns ein Leser die Anforderung, bei der eine Internetanwendung eine Möglichkeit bietet, über eine Schaltfläche beziehungsweise einen Link eine eigene URL anzugeben, die bei Betätigung geöffnet wird. Lässt sich dies realisieren, und wenn ja, wie? Die Lösung ist nicht besonders nahe liegend, denn wir können die Anwendung ja nicht über einen herkömmlichen Batch-Befehl starten, wie es etwa über eine Dateiverknüpfung erfolgt. Der Trick ist, dass wir einen eigenen Datei-Handler definieren, wie er normalerweise in die URL-Leiste des Browsers eingegeben wird, also beispielsweise http: oder ftp:. Dem lassen wir dann die Daten folgen, die als Parameter an die aufzurufende Access-Datenbank übergeben werden sollen. Aber wie definieren, dass etwa bei Übergabe des Handlers accessdb: auch wirklichen eine Access- Datenbank geöffnet wird, und welche dies ist? Dies legen wir in der Registry fest, und zwar etwa wie in Bild 1. Wir fügen also dem Zweig HKEY_CLASSES_ROOT ein neues Element namens accessdb hinzu. Dafür legen wir als Standardwert URL:accessdb Protocol fest und ein weiteres Element namens URL Protocol, das aber leer bleibt. Darunter folgen drei weitere verschachtelte Elemente, nämlich shell, open und command. Letzteres erhält die auszuführende Batch-Anweisung, in diesem Fall den Aufruf der Anwendung MSACCESS.EXE und die Angabe der zu öffnenden Datenbankdatei mit Pfad. Danach folgt noch der Ausdruck /cmd %1, den wir später erläutern: "C:\Program Files (x86)\microsoft Office\root\Office16\ MSACCESS.EXE" "C:\Users\User\Dropbox\Daten\URLTest.accdb" /cmd %1 Um diese Einträge automatisch per Doppelklick auf eine Datei namens accessdb.reg zur Registry hinzuzufügen, füllen Sie diese Datei mit dem Code aus Listing 1. Wenn Sie nun im Browser die Adresse accessdb: gefolgt von einem beliebigen Ausdruck eingeben (oder auch nur accessdb:), wird die Access-Datenbank geöffnet und in Access angezeigt. Beim ersten Aufruf kann es vorkom- Bild 1: Einstellungen in der Registry Seite 47

50 ACCESS INTERAKTIV ACCESS PER URL STARTEN men, dass die Meldung aus Bild 2 erscheint und Sie diese zunächst noch bestätigen müssen. Wenn Sie hier die Option Auswahl für accessdb-links speichern aktivieren, erscheint diese Meldung anschließend nicht mehr. Dies funktionert wie beschrieben mit Firefox. Unter \"C:\\Users\\User\\Dropbox\\Daten\\URLTest.accdb\" /cmd %1" Listing 1: Inhalt der Datei accessdb.reg zum Anlegen der benötigten Registrierungswerte Microsoft Edge muss die Meldung jedes Mal erneut bestätigt werden, Chrome will Access gar nicht öffnen. Der Internet Explorer bietet auch die Möglichkeit, die Einstellung zu speichern. Wer die Registry bearbeitet, um vom Browser aus eine Access-Datenbank zu starten, wird sich aber vermutlich auch mit der Auswahl eines der funktionierenden Browser anfreunden können. Parameter übergeben Nun kommen wir zum interessanten Teil, denn wir wollen ja nicht nur einfach irgendeine Access-Datenbank öffnen, sondern gegebenenfalls auch noch Parameter übergeben. Windows Registry Editor Version 5.00 Protocol" "URL Protocol"="" [HKEY_CLASSES_ROOT\accessdb\shell] [HKEY_CLASSES_ROOT\accessdb\shell\open] [HKEY_CLASSES_ROOT\accessdb\shell\open\command] Die Vorbereitungen, um diese Parameter zu übergeben, haben wir in der Registry bereits getroffen. Dort haben wir nämlich den Ausdruck / cmd %1 an den Batch-Aufruf angehängt. %1 entspricht dabei der kompletten URL. Wenn Sie die Datenbank also mit dem Link accessdb:test öffnen, wird accessdb:test auch als cmd-parameter Files (x86)\\microsoft Office\\root\\Office16\\MSACCESS.EXE\" Bild 2: Warnmeldung beim Ausführen von Access via Browser wechseln Sie testweise zum Direktbereich des VBA-Editors (Strg + G) und geben dort den folgenden Befehl ein: Debug.Print Command() accessdb:test Den cmd-parameter können Sie in der geöffneten Anwendung dann mit der Funktion Command() auslesen. Wenn Sie die Anwendung also mit dem Link geöffnet haben, Den Parameter wollen Sie natürlich nun noch professioneller auswerten beispielsweise, wenn der Aufruf die Angabe etwa einer Datensatznummer enthält und die Seite 48

51 INTERAKTIV ACCESS PER URL STARTEN ACCESS heißt AusführenCode und erhält als Parameter den Namen der aufzurufenden VBA-Funktion, in diesem Fall ParameterAuswerten(). Bild 3: Makro, das beim Start der Anwendung ausgeführt wird Access-Anwendung den entsprechenden Datensatz beim Öffnen anzeigen soll. Dazu geben wir hinter dem URL-Handler nun direkt den Parameter mit Name und Wert an etwa wie folgt, um einen Kunden mit dem Wert 3 für das Feld KundeID zu öffnen: VBA-Funktion Die benötigte VBA-Funktion sieht wie in Listing 2 aus. Sie weist zunächst den Inhalt der Funktion Command(), also den mit dem Parameter cmd beim Öffnen übergebenen Wert, der Variablen strcommand zu. Dann prüft sie, ob strcommand überhaupt einen Wert enthält. Falls ja, entfernt sie aus diesem Parameter das führende accessdb: und speichert das Ergebnis in der Variablen strparameter. Dann liest sie den Teil vor dem Gleichheitszeichen mit der Left-Funktion aus und speichert diesen in strparametername. Der Teil hinter dem Gleichheitszeichen landet in der Variablen strparameterwert. accessdb:kundeid=3 Damit die Datenbank nun gleich das Formular mit dem entsprechenden Wert anzeigt, benötigen wir drei Dinge: ein AutoExec-Makro, das beim Start der Anwendung geöffnet wird, eine VBA-Funktion, die durch das Makro aufgerufen wird und die URL auswertet sowie ein Formular, das schließlich den gewünschten Kunden anzeigt. Startmakro Das Makro namens Auto- Exec sieht wie in Bild 3 aus und soll nur eine einzige Anweisung enthalten. Diese Public Function ParameterAuswerten() Dim strcommand As String Dim strparameter As String Dim strparametername As String Dim strparameterwert As String strcommand = Command() If Len(strCommand) > 0 Then End If End Function Damit ausgestattet, können wir nun ein Formular mit einer WhereCondition öffnen, welche den im Formular angezeigten Datensatz auf den gewünschten Kunden einstellt. Das Formular Das Formular der Beispielanwendung sieht im Entwurf wie in Bild 4 aus. Es heißt frmkunden und enthält eine einfache Beispieltabelle namens tblkunden mit den vier strparameter = Replace(strCommand, "accessdb:", "") strparametername = Left(strParameter, InStr(1, strparameter, "=") - 1) strparameterwert = Mid(strParameter, InStr(1, strparameter, "=") + 1) DoCmd.OpenForm "frmkunden", WhereCondition:="KundeID = " _ & strparameterwert, WindowMode:=acDialog Listing 2: Funktion zum Auswerten der Parameter und Öffnen des Formulars mit dem angegebenen Kunden Seite 49

52 ACCESS INTERAKTIV ACCESS PER URL STARTEN Hier sehen wir den Fall vor, dass nicht nur das Kundenformular mit einem Kunden nach dem angegebenen Wert für das Feld KundeID geöffnet werden soll, sondern auch noch andere Parameter verwendet werden können. In diesem Fall haben wir die If...Then-Bedingung durch eine Select Case-Bedingung ersetzt, welche den Inhalt der Variablen strparametername prüft. Wenn Sie nun beispielsweise keinen Kunden, sondern einen Artikel anzeigen wollten, müssten Sie einen Seitenaufruf wie accessdb:artikelid=25 angeben. Dies sollte dann ein weiteres Formular mit dem entsprechenden Datensatz öffnen. Bild 4: Formular der Beispielanwendung im Entwurf Zusammenfassung und Ausblick Die Lösung ist sehr praktisch, wenn Sie mit Webanwendungen arbeiten, die eine Möglichkeit zum Angeben von URLs zum Anstoßen weiterer Aktionen anbieten. Feldern KundeID, Firma, Vorname und Nachname als Parameter. Die Felder haben wir einfach in den Detailbereich des Formularentwurfs gezogen. Damit lässt sich per Link eine Access-Anwendung öffnen, die gleich die gewünschte Aktion wie etwa die Anzeige eines Datensatzes erledigt. Das Formular zeigt beim Öffnen den mit dem Parameter KundeID angegebenen Kunden an. Verschiedene Parameter und Formulare In Listing 3 haben wir noch eine weitere Version der Funktion ParameterAuswerten vorbereitet. Public Function ParameterAuswerten() Dim strcommand As String Dim stralleparameter As String Dim strparameter As String Dim strparametername As String Dim strparameterwert As String Dim i As Integer strcommand = Command() If Len(strCommand) > 0 Then stralleparameter = Replace(strCommand, "accessdb:", "") For i = LBound(Split(strAlleParameter, ";")) To UBound(Split(strAlleParameter, ";")) strparameter = Split(strAlleParameter, ";")(i) strparametername = Left(strParameter, InStr(1, strparameter, "=") - 1) Select Case strparametername Case "KundeID" strparameterwert = Mid(strParameter, InStr(1, strparameter, "=") + 1) DoCmd.OpenForm "frmkunden", WhereCondition:="KundeID = " _ & strparameterwert, WindowMode:=acDialog Case "ArtikelID" '... End Select Next i End If End Function Listing 3: Funktion, die verschiedene Bedingungen unterstützt Seite 50

53 SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN ACCESS RDBMS-Zugriff per VBA: Daten bearbeiten Im Beitrag»RDBMS-Zugriff per VBA: Verbindungen«haben wir die Grundlage für den Zugriff auf SQL Server-Datenbanken geschaffen. Zudem zeigt der Beitrag»RDBMS-Zugriff per VBA: Daten abfragen«, wie Sie die Daten einer SQL Server-Datenbank ermitteln. Im vorliegenden Teil dieser Beitragsreihe erfahren Sie nun, wie Sie die Daten einer SQL Server-Datenbank bearbeiten. Aktionsabfragen Aktionsabfragen sind Abfragen, die Daten ändern also Lösch-, Aktualisierungs- und Anfügeabfragen. In reinen Access-Datenbanken führen Sie solche Abfragen aus, indem Sie diese mit dem Abfrage-Entwurf erstellen und direkt ausführen oder per VBA aufrufen oder indem Sie die gewünschte Abfrage als SQL-Ausdruck per Code zusammenstellen und dann mit der Execute-Methode des Database-Objekts ausführen. Für SQL Server-Daten gibt es die folgenden Arten der Ausführung: Erstellen einer Aktionsabfrage in Access, die sich auf die Daten einer per ODBC verknüpften Tabelle des SQL Servers bezieht, Erstellen einer Pass-Through-Abfrage, welche die Aktionsabfrage enthält und diese direkt an den SQL Server übermittelt, Erstellen einer gespeicherten Prozedur, welche die Aktionsabfrage enthält und die notwen digen Parameter entgegen nimmt also beispielsweise die ID eines zu löschenden Daten satzes, und die über eine Pass- Through-Abfrage aufgerufen wird. Wenn es um die Performance geht, ist die erste Variante die langsamste, die zweite Version ist etwas schneller und die dritte Version ändert die Daten in der Regel am schnellsten. Aus diesem Grund schauen wir uns nachfolgend lediglich die zweite und die dritte Variante an. Datensatz löschen per SQL Bei der ersten Variante legen Sie eine Pass-Through- Abfrage mit der auszuführenden DELETE-Anweisung an (s. Bild 1). Dazu sind folgende Schritte nötig: Bild 1: Die neue PassThrough-Abfrage zum Löschen eines Datensatzes im SQL Server Seite 51

54 ACCESS SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN Erstellen einer neuen, leeren Abfrage und Schließen des Dialogs Tabelle anzeigen Wechseln des Abfragetyps auf Pass-Through Einstellen der Eigenschaft ODBC-Verbindung auf die gewünschte Verbindungszeichenfolge (hier ODBC;DRIVER={SQL Server Native Client 11.0};SERVER=(localdb)\MSSQLLocalDB;DATABASE =Suedsturm;Trusted_Connection=Yes) Einstellen der Eigenschaft Liefert Datensätze auf Nein Eintragen der DELETE-Anweisung Die DELETE-Anweisung soll in unserem Fall wie folgt lauten: DELETE FROM tblkategorien WHERE KategorieID = 12 Die Abfrage können Sie dann per VBA mit einer einzigen Anweisung ausführen: Public Sub KategorieLoeschen_PT(lngKategorieID As Long) Dim db As DAO.Database Dim qdf As DAO.QueryDef Set db = CurrentDb Set qdf = db.querydefs("qryptdeletekategorie") qdf.sql = "DELETE FROM dbo.tblkategorien µ qdf.execute Set qdf = Nothing Set db = Nothing WHERE KategorieID = " & lngkategorieid Mit dieser Prozedur referenzieren wir die soeben erstellte Abfrage qryptdeletekategorie und ändern die enthaltene SQL-Anweisung so, dass diese als Kriterium den per Parameter übergebenen Primärschlüsselwert enthält. Danach führen wir die geänderte Abfrage mit der Execute- Anweisung aus. Der Aufruf dieser Prozedur sieht etwa so aus: KategorieLoeschen_PT 104 CurrentDb.Execute "qryptdeletekategorie" Diese Variante hat noch folgende Nachteile: Sie können auch die Variante mit dem QueryDefs-Objekt verwenden: CurrentDb.QueryDefs("qryPTDeleteKategorie").Execute Damit haben Sie allerdings noch nicht viel gewonnen: Die Anweisung löscht ja nur genau den Datensatz, dessen ID Sie als Kriterium angegeben haben. Immerhin haben wir aber bereits eine Abfrage erstellt, die den richtigen Typ aufweist, die Ver bin dungszeichenfolge enthält und deren Eigenschaft Liefert Datensätze auf Nein eingestellt ist. Diese nutzen wir nun, um gezielt einen bestimmten Datensatz zu löschen. Die folgende Prozedur (wie auch die weiteren Beispiele im Modul mdlrdbmszugriff_datenbearbeiten) erwartet den Primärschlüsselwert des zu löschenden Datensatzes als Parameter: Die an den SQL Server übergebene SQL-Anweisung wird dynamisch zusammengesetzt. Wenn sich die SQL-Anweisung dabei von einer bereits verwendeten unterscheidet, also etwa ein anderer Parameterwert zum Einsatz kommt, muss der Ausführungsplan neu erstellt werden. Die Verbindungszeichenfolge ist in der Abfrage gespeichert. Wenn sich diese ändert, muss sie in jeder Abfrage angepasst werden. Wir erfahren nicht, ob die Aktion erfolgreich war und wie viele Datensätze gelöscht wurden. In den folgenden beiden Abschnitten kümmern wir uns um diese Nachteile. Seite 52

55 SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN ACCESS Die gespeicherte Prozedur spdelete- KategorieNachID erwartet den Primärschlüsselwert des zu löschenden Datensatzes als Parameter. Wenn Sie die gespeicherte Prozedur direkt vom Abfragefenster des SQL Servers aus ausführen wollten, würden Sie dies mit folgender Anweisung erledigen: EXEC dbo.spdeletekategorienachid 105 Bild 2: Anlegen einer gespeicherten Prozedur per Access-Formular Datensatz löschen per gespeicherter Prozedur Als Erstes sorgen wir dafür, dass der SQL Server unabhängig vom übergebenen Parameter nur einen Ausführungsplan für die Abfrage erstellt, speichert und bei weiteren Aufrufen wiederverwendet. Dazu erstellen wir eine gespeicherte Prozedur, und zwar mit folgendem SQL-Skript: Sie können auch diese Abfrage im Formular frmsqlbefehle absetzen, aber es gibt noch eine andere Variante zum Beispiel für den Fall, dass Sie diese gespeicherte Prozedur per Code aufrufen wollen. Also erstellen Sie zunächst eine neue Abfrage, wandeln diese in eine Pass-Through-Abfrage um und legen den SQL-Ausdruck aus Bild 3 fest. In dieser Abfrage müssen Sie nun natürlich ebenfalls den Primärschlüsselwert des zu löschenden Datensatzes als Parameter angeben. Dies erledigen Sie ähnlich wie oben: CREATE PROCEDURE dbo.spdeletekategorienachid (@KategorieID int) AS SET NOCOUNT ON; DELETE FROM tblkategorien WHERE KategorieID Dieses Skript können Sie, wenn Sie es von Access aus ausführen möchten, in das Formular frmsqlbefehle eingeben und dann mit der Ausführen-Schaltfläche ausführen (s. Bild 2). Ob die gespeicherte Prozedur erfolgreich angelegt wurde, können Sie mit der folgenden Anweisung, ebenfalls in diesem Formular abgesetzt, prüfen: SELECT * FROM Suedsturm.information_schema.routines WHERE routine_type = 'PROCEDURE' Public Sub KategorieLoeschen_PT_SP(lngKategorieID As Long) Dim db As DAO.Database Dim qdf As DAO.QueryDef Set db = CurrentDb Set qdf = db.querydefs("qryptdeletekategorie") qdf.sql = "EXEC dbo.spdeletekategorienachid " µ & lngkategorieid qdf.execute Set qdf = Nothing Set db = Nothing Der Aufruf sieht beispielsweise wie folgt aus: KategorieLoeschen_PT_SP Seite 53

56 ACCESS SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN Dies ändert zunächst den SQL-Ausdruck der Abfrage ptkategorieloeschen wie folgt: EXEC dbo.spdeletekategorienachid 106 Dieser Aufruf wird direkt an den SQL Server gesendet, der dann die gespeicherte Prozedur spdeletekategorie- NachID mit dem angegebenen Parameter ausführt und den entsprechenden Datensatz löscht. Pass-Through-Abfrage mit dynamischer Verbindungszeichenfolge Nun soll noch die Verbindungszeichenfolge direkt aus der Tabelle tblverbindungszeichenfolgen bezogen werden (Erläuterungen zu dieser Tabelle siehe RDBMS- Zugriff per VBA: Verbindungen, Dazu übergeben Sie der VBA-Prozedur noch die ID der Verbindungszeichenfolge als weiteren Parameter. Dieser Parameter wird an die in dem oben erwähnten Beitrag erläuterte Funktion Ver bin dungs zeichenfolgenachid übergeben, die dann die Verbindungszeichenfolge zurückliefert. Das Ergebnis landet direkt in der Eigenschaft Connect des QueryDef- Objekts, was dem Zuweisen der Verbindungszeichenfolge zur Eigenschaft ODBC-Verbindung entspricht. Die Prozedur finden Sie in Listing 1. Auch hier noch ein Beispielaufruf: KategorieLoeschenNachID_PT_SP_Connection 107, 9 Bild 3: Aufruf einer gespeicherten Abfrage per Passthrough-Abfrage Dies löscht den Datensatz mit dem Wert 107 im Feld KategorieID und verwendet die Verbindungszeichenfolge mit dem Wert 9 im Feld VerbindungszeichenfolgeID der Tabelle tblverbindungszeichenfolgen. Sie können die Verbindungszeichenfolge natürlich auch mit der Funktion Standardverbindungszeichenfolge ermitteln. Dazu ersetzen Sie die Zeile mit der Connect- Eigenschaft wie folgt: qdf.connect = Standardverbindungszeichenfolge Oder Sie übergeben die Standardverbindungszeichenfolge beim Aufruf: Public Sub KategorieLoeschenNachID_PT_SP_Connection(lngKategorieID As Long, lngverbindungszeichenfolgeid As Long) Dim db As DAO.Database Dim qdf As DAO.QueryDef Set db = CurrentDb Set qdf = db.querydefs("qryptdeletekategorie") qdf.connect = VerbindungszeichenfolgeNachID(lngVerbindungszeichenfolgeID) qdf.sql = "EXEC dbo.spdeletekategorienachid " & lngkategorieid qdf.execute Set qdf = Nothing Set db = Nothing Listing 1: Aufruf einer gespeicherten Prozedur mit dynamischer Verbindungszeichenfolge Seite 54

57 SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN ACCESS KategorieLoeschenNachID_PT_SP_Connection 108, µ StandardverbindungszeichenfolgeID Löschen mit Bestätigung Schließlich möchten Sie vielleicht noch wissen, ob der Löschvorgang überhaupt erfolgreich war beziehungsweise wie viele Datensätze von der Aktionsabfrage betroffen waren. T-SQL bietet mit der Funktion ein Mittel, um die Anzahl der von der zuletzt ausgeführten Abfrage betroffenen Datensätze zu ermitteln. Dies bezieht sich auf die Aktionsabfragen der aktuellen Verbindung. Die folgende gespeicherte Prozedur löscht wie in den obigen Beispielen einen Datensatz mit dem übergebenen Wert für das Feld KategorieID, gibt aber als Ergebnis die Anzahl der betroffenen Datensätze zurück: CREATE PROCEDURE INT AS SET NOCOUNT ON; DELETE FROM tblkategorien WHERE KategorieID AS RecordsAffected; Wenn Sie diese gespeicherte Prozedur im Abfragefenster im SQL Server Management Studio aufrufen, sieht das Bild 4: Ergebnis einer gespeicherten Prozedur im SQL Server Management Studio Ergebnis wie in Bild 4 aus. Um dieses Ergebnis von Access aus zu nutzen, ist eine kleine Änderung am Entwurf der Pass-Through-Abfrage nötig. Wir haben die Abfrage von oben unter dem Namen qryspdeletekategorienachidmitergebnis kopiert und die Eigenschaft Liefert Datensätze auf den Wert Ja eingestellt (s. Bild 5). Anderenfalls liefert die Abfrage das Ergebnis der SELECT-Abfrage mit der Anzahl der betroffenen Datensätze nicht zurück! Führen Sie diese Abfrage direkt aus, liefert sie das Ergebnis aus Bild 6. Bild 5: Entwurf der Passthrough-Abfrage zum Löschen eines Datensatzes mit Rückgabe der betroffenen Datensätze Dies ist ein Ergebnis, mit dem wir auch unter VBA arbeiten können. Die Prozedur aus Listing 2 verwendet wieder die KategorieID und ermittelt die Verbindungszeichenfolge mit der Funktion Standardverbindungszeichenfolge. Sie erzeugt wie gewohnt ein QueryDef-Objekt auf Basis einer neuen gespeicherten Access- Abfrage namens spdeletekategorie- NachIDMitErgebnis und ermittelt die gewünschte Verbindungszeichenfolge. Dann weist sie wie zuvor den neuen SQL-Ausdruck zu, führt die Abfrage aber diesmal nicht mit Execute aus. Stattdes- Seite 55

58 ACCESS SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN neu anlegen der Performance-Unterschied dürfte sich in Grenzen halten. Es gibt jedoch auch die Möglichkeit, ein QueryDef-Objekt komplett temporär zu erzeugen. Bild 6: Ergebnis der gespeicherten Prozedur innerhalb einer Pass- Through-Abfrage in Access sen erstellt sie ein neues Recordset-Objekt und füllt es über die OpenRecordset-Methode mit dem Ergebnis der gespeicherten Prozedur. Dies erzeugt ein herkömmliches Recordset-Objekt, das nur einen Datensatz mit einem Feld enthält und dieses wird mit rst!recordsetaffected ausgelesen und in einem Meldungsfenster ausgegeben. Dynamische Aktionsabfrage ohne Rückgabewert Die bisherigen Ansätze gingen davon aus, dass die Access-Datenbank eine gespeicherte Access-Abfrage mit den wichtigsten Eigenschaften zum Ausführen der gespeicherten Prozedur per Pass-Through-Abfrage enthält. Je mehr solcher Abfragen Sie verwenden, desto unübersichtlicher wird es im Navigationsbereich. Und davon abgesehen ändern wir ohnehin zumindest den SQL-Code jeder Pass-Through-Abfrage, die eine gespeicherte Prozedur mit Parametern ausführt. Dann könnten wir diese auch gleich Was benötigen wir also im Vergleich zur vorherigen Variante? Eigentlich müssen wir nur den Namen der zu verwendenden gespeicherten Prozedur zusätzlich übergeben, die Ver bin dungs zeichenfolge und die Parameter werden ja bereits verarbeitet. Außerdem referenzieren wir nicht über die QueryDefs-Auflistung eine bestehende Abfrage, sondern erstellen mit CreateQueryDef eine neue und zwar mit einer leeren Zeichenkette als Name. Die Prozedur sieht nun wie in Listing 3 aus. Sie erstellt mit der CreateQueryDef-Methode eine temporäre Abfrage, was wir dadurch erreichen, dass wir eine leere Zeichenfolge als Parameter übergeben. Für die Parameterliste verwenden wir im Kopf der Prozedur einen Parameter namens varparameter des Typs ParamArray, dem man beliebig viele durch Kommata getrennte Parameterwerte übergeben kann. Die damit übergebenen Werte setzt die Prozedur in einer For Each-Schleife über alle Elemente von varparameter zusammen und stellt jeweils ein Komma voran. Das erste Komma wird danach gegebenenfalls abgeschnitten. Die Public Sub KategorieLoeschenNachID_PT_SP_Connection_MitErgebnis(lngKategorieID As Long) Dim db As DAO.Database Dim qdf As DAO.QueryDef Dim rst As DAO.Recordset Dim lnganzahl As Long Set db = CurrentDb Set qdf = db.querydefs("qryptspdeletekategorienachidmitergebnis") qdf.connect = Standardverbindungszeichenfolge qdf.sql = "EXEC dbo.spdeletekategorienachidmitergebnis " & lngkategorieid Set rst = qdf.openrecordset(dbopensnapshot) lnganzahl = rst!recordsaffected MsgBox "Es wurden " & lnganzahl & " Datensätze gelöscht." Set rst = Nothing Set qdf = Nothing Set db = Nothing Listing 2: Aufruf einer gespeicherten Prozedur mit Rückgabewert Seite 56

59 SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN ACCESS Public Sub TemporaerePTSPMitParameterAusfuehren(strStoredProcedure As String, lngverbindungszeichenfolgeid As Long, _ ParamArray varparameter() As Variant) Dim db As DAO.Database Dim qdf As DAO.QueryDef Dim strparameter As String Dim var As Variant Set db = CurrentDb Set qdf = db.createquerydef("") For Each var In varparameter strparameter = strparameter & ", " & var Next var If Len(strParameter) > 0 Then strparameter = Mid(strParameter, 3) End If With qdf.connect = VerbindungszeichenfolgeNachID(lngVerbindungszeichenfolgeID).ReturnsRecords = False.SQL = "EXEC " & strstoredprocedure & " " & strparameter.execute End With Set db = Nothing Listing 3: Prozedur, welche die angegebene gespeicherte Prozedur mit gegebener Verbindungszeichenfolge und Parametern ausführt folgenden Zeilen stellen die Ver bin dungszeichenfolge ein, legen für ReturnRecords den Wert False fest und fügen das Schlüssel wort EXEC, den Namen der gespeicherten Prozedur und die Parameterliste zusammen. Die Execute-Methode führt die Abfrage schließlich durch. Ein Beispielaufruf sieht etwa so aus: gilt nur für die VBA-Varianten: also das Hinzufügen mit der DAO-Methode AddNew oder mit der per Execute aufgerufenen INSERT INTO- oder SELECT INTO-SQL-Anweisung. Die folgenden Abschnitte zeigen die Variante für lokale Tabellen sowie für das Hinzufügen von Datensätzen zu Tabellen einer SQL Server-Datenbank. TemporaerePTSPMitParameterAusfuehren µ "spdeletekategorienachidmitergebnis",9,112 Um auch hier die Standardverbindungszeichenfolge zu verwenden, nutzen Sie folgenden Aufruf: TemporaerePTSPMitParameterAusfuehren µ "spdeletekategorienachidmitergebnis", µ StandardverbindungszeichenfolgeID, 112 Variante I: AddNew/Update Die erste Variante ist die oft verwendete DAO-Methode mit den beiden Anweisungen AddNew und Update, wobei zwischen diesen beiden Anweisungen die neuen Feldwerte angegeben werden. Dies ist die Access-Variante, bei welcher der Wert des Primärschlüsselfelds für den neuen Datensatz bereits nach dem Aufruf der AddNew-Methode belegt ist und ausgelesen werden kann hier am Beispiel der lokalen Tabelle tblkategorien_lokal: Autowert des zuletzt hinzugefügten Datensatzes ermitteln Für verschiedene Zwecke ist es interessant, den Autowert des zuletzt hinzugefügten Datensatzes zu ermitteln. Dies Public Sub NeuerDatensatzMitID_Lokal() Dim db As DAO.Database Dim rst As DAO.Recordset Set db = CurrentDb Seite 57

60 ACCESS SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN Set rst = db.openrecordset(µ rst.addnew "SELECT * FROM tblkategorien_lokal", µ dbopendynaset) Debug.Print "Neue KategorieID: " & rst!kategorieid rst!kategoriename = "Beispielkategorie" rst!beschreibung = "Beispielbeschreibung" rst.update rst.close Set rst = Nothing Set db = Nothing Führen wir diese Prozedur in einer Beispieldatenbank aus, die eine per ODBC verknüpfte SQL Ser ver-ta belle namens tblkategorien enthält, liefert dies den Fehler aus Bild 7. Dies besagt, dass Sie auf eine SQL Server-Tabelle mit einem Primärschlüsselwert (IDENTITY) nur zu grei fen können, wenn Sie den Parameter dbseechanges verwenden. Diesen Fehler beheben wir in der folgenden Variante. Dort fügen wir in der Zeile mit der OpenRecordset-Methode zunächst den Wert dbseechanges für den dritten Parameter hinzu: Set rst = db.openrecordset("select * FROM tblkatgorien", dbopendynaset, dbseechanges) Die Prozedur läuft nun durch, die Debug.Print-Anweisung liefert jedoch keinen Wert für das Feld KategorieID, also das durch den SQL Server mit einem Autowert zu füllende Primärschlüsselfeld: Debug.Print "Neue KategorieID: " & rst!kategorieid Neue KategorieID: Der Grund liegt darin, dass der SQL Server den Autowert für das Primärschlüsselfeld erst vergibt, wenn der Datensatz gespeichert wird. Hilft es also, wenn wir den Wert des Primärschlüsselfeldes erst nach dem Aufruf von Update abfragen? Nein AddNew und Update verschieben den Da ten satzzeiger nicht auf den neuen Datensatz, sondern Bild 7: Fehler beim Versuch, ein Recordset ohne die Option dbsee- Changes zu öffnen dieser verbleibt auf dem Datensatz, auf den der Zeiger vor dem Anlegen bereits zeigte (in diesem Fall auf den ersten Datensatz des Recordsets): rst.addnew rst!kategoriename = "Beispielkategorie1" rst!beschreibung = "Beispielbeschreibung" rst.update Debug.Print "Neue KategorieID: " & rst!kategorieid Hier kommt die Eigenschaft LastModified des DAO-Recordsets zum Einsatz. Stellen wir die Ei gen schaft Bookmark nach dem Einfügen des Datensatzes auf den Wert der Eigenschaft LastModified ein, wird der Datensatzzeiger auf den neuen Datensatz verschoben und wir können den Primärschlüsselwert auslesen: rst.update rst.bookmark = rst.lastmodified Debug.Print "Neue KategorieID: " & rst!kategorieid Fehler ermitteln unter DAO Wenn Sie das obige Beispiel zweimal hintereinander ausführen, ohne den Code zu ändern, löst dies den Fehler aus Bild 8 aus. Die Fehlermeldung ODBC-Aufruf fehlgeschlagen weist lediglich darauf hin, dass beim Zugriff auf den SQL Server ein Fehler aufgetreten ist. Genauere Informationen erhalten Sie über die Errors-Auflistung der DAO-Bibliothek. Die Count-Eigenschaft liefert die Anzahl Seite 58

61 SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN ACCESS der Einträge im Direktbereich etwa so (nach dem Quittieren der obigen Fehlermeldung mit einem Klick auf die Schaltfläche Debuggen):? DAO.Errors.Count 3 Der letzte Fehler der Auflistung mit dem Index 2 ist der, den auch das Err-Objekt und die Fehlermeldung liefern:? DAO.Errors(2).Number, DAO.Errors(2).Description 3146 ODBC-Aufruf fehlgeschlagen. Der zweite Fehler basiert auf folgender Nummer und Meldung:? DAO.Errors(1).Number, DAO.Errors(1).Description 3621 [Microsoft][SQL Server Native Client 11.0][SQL Server]Die Anweisung wurde beendet. Auch diese Meldung liefert keine entscheidenden Informationen. Bleibt noch die Meldung mit dem Index 0:? DAO.Errors(0).Number, DAO.Errors(0).Description 2601 [Microsoft][SQL Server Native Client 11.0] [SQL Server]Eine Zeile mit doppeltem Schlüssel kann in das dbo.tblkategorien-objekt mit dem eindeutigen IX_tblKategorien-Index nicht eingefügt werden. Der doppelte Schlüsselwert ist (Beispielkategorie1). Wir haben also schlicht versucht, einen Wert in das Feld Kategoriename einzutragen, der bereits vorhanden war. Und da für dieses Feld ein eindeutiger Index festgelegt wurde, löst der SQL Server einen entsprechenden Fehler aus. Variante II: Execute/INSERT INTO Unter Access verwendet man bei Datensatzänderungen, die durch INSERT INTO- und SELECT INTO-Abfragen durchgeführt wurden, eine spezielle Abfrage zum Ermitteln des Primärschlüsselwertes des zuletzt hinzugefügten Datensatzes. Ein Beispiel sieht wie folgt aus: Bild 8: Fehler beim Versuch, einen bereits vorhandenen Wert in ein eindeutiges Feld zu schreiben Public Sub NeuerDatensatzExecute() Dim db As DAO.Database Dim lngid As Long Set db = CurrentDb db.execute "INSERT INTO tblkategorien(kategoriename, µ Beschreibung) VALUES('Beispielkategorie', µ 'Beispielbeschreibung')", dbfailonerror lngid = Debug.Print "Neuer Datensatz: " & lngid Die Codezeilen legen zunächst einen neuen Datensatz in der Tabelle tblkategorien an. Im Gegensatz zur entsprechenden Vorgehensweise mit den DAO-Methoden Add- New und Update erhalten Sie hier nicht automatisch die ID des neu angelegten Datensatzes. Diese ist aber häufig erforderlich, da gegebenenfalls weitere Datensätze angelegt werden sollen, die per Fremd schlüsselwert auf diesen Datensatz verweisen, oder der neu angelegte Datensatz soll gleich nach dem Aktualisieren der Datenherkunft im Formular angezeigt werden. Access liefert die ID mit der die Sie in Form einer SELECT- Abfrage abfragen. Neue ID per SQL Server abfragen Der SQL Server bietet glücklicherweise genau die gleiche Funktion an, um den zuletzt hinzugefügten Autowert zu ermitteln. Allein die Übergabe an Access stellt eine kleine Hürde dar, die wir allerdings leicht nehmen zunächst mit der folgenden gespeicherten Prozedur als Beispiel: Seite 59

62 ACCESS SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN CREATE PROCEDURE NVAR- CHAR(255) AS SET NOCOUNT ON; INSERT INTO tblkategorien(kategoriename, Wenn wir nun die gespeicherte Prozedur aufrufen und anschließend die bereits unter Access verwendete Abfrage zur Abfrage des neuen Identitätswertes, sieht das wie folgt aus: EXEC dbo.spinsertintokategorie 'Neue Kategorie', 'Neue Beschreibung' SELECT AS KategorieID; Bild 9: Anlegen eines neuen Datensatzes plus Ausgabe der neuen ID IDENT_CURRENT(): Der Vollständigkeit halber stellen wir auch noch diese Funktion vor. Sie liefert den zuletzt zu einer bestimmten Tabelle hinzugefügten Autowert. Sie erwartet die Angabe des Tabellennamens als Parameter, zum Beispiel IDENT_ CURRENT('tblKategorien'). Das Abfragefenster zeigt nun den Wert des Feldes KategorieID des neuen Datensatzes an (s. Bild 9). Neben bietet T-SQL noch zwei Alternativen. Es gibt insgesamt drei Funktionen, die ein ähnliches Ergebnis liefern: Liefert den Wert des zuletzt angelegten Autowertes. Meist ist dies der Wert, den wir suchen aber es gibt Ausnahmen! Wenn Sie einen Trigger einsetzen, der durch das Anlegen des neuen Datensatzes ausgelöst wird und der wiederum einen neuen Datensatz in einer Tabelle mit einem Autowert-Feld einträgt, liefert den durch den Trigger erzeugten Autowert zurück. Die Funktion ist auf die aktuelle Session begrenzt und liefert nicht etwa durch andere Benutzer angelegte Autowerte zurück. SCOPE_IDENTITY(): Diese Funktion ist ebenfalls auf die aktuelle Session beschränkt, aber berücksichtigt nur die Autowerte, die explizit erzeugt wurden also durch INSERT INTO oder SELECT INTO-Anweisungen. Durch Trigger erzeugte Datensätze werden nicht berücksichtigt. Verwenden wir also statt die Funktion SCOPE_IDENTITY() die Klammern dürfen übrigens nicht weggelassen werden: SELECT SCOPE_IDENTITY() AS KategorieID Wie Bild 10 zeigt, funktioniert die Funktion nicht wie IDENTITY. In der Tat beschränkt sich der Gültigkeitsbereich von SCOPE_IDENTITY auf eine gespeicherte Prozedur, eine benutzerdefinierte Funktion oder einen Batch. Kein Problem: Packen wir die Rückgabe des Autowertes des neu angelegten Datensatzes also einfach mit in die gespeicherte Prozedur: CREATE PROCEDURE NVARCHAR(255) AS SET NOCOUNT ON; INSERT INTO tblkategorien(kategoriename, SELECT SCOPE_IDENTITY() AS KategorieID; Dies liefert das gewünschte Ergebnis der Aufruf der gespeicherten Prozedur spinsertintokategorienmi- Seite 60

63 SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN ACCESS tid gibt gleich den neuen Autowert zurück (s. Bild 11). Autowert per gespeicherter Prozedur von Access aus Nun wissen wir schon einmal, wie wir den neuen Autowert und somit meist auch den Primärschlüsselwert im SQL Server ermitteln. Der Rest ist eine Kombination aus dem Erstellen eines QueryDef-Objekts und dessen Ausführung mit der OpenRecordset-Methode (s. Listing 4). Die Prozedur erstellt ein neues temporäres QueryDef-Objekt (also mit einer leeren Zeichenkette als Name). Für dieses sind einige Eigenschaften einzustellen: Bild 10: SCOPE_IDENTITY liefert nicht auf Anhieb den gewünschten Wert. Connect: Wird mit der Funktion Standardverbindungszeichenfolge gefüllt. ReturnsRecord: Obwohl die Abfrage eigentlich eine Aktion ausführen soll, möchten wir den Wert des neuen Primärschlüssels erhalten. Also stellen wir ReturnsRecords auf True ein. SQL: Die Anweisung führt die gewünschte gespeicherte Prozedur mit der EXEC-Anweisung aus und gibt den Namen der anzulegenden Kategorie als Parameter an. Anschließend führt die Prozedur die Abfrage aus, indem sie die OpenRecordset-Methode aufruft. Das Ergebnis in Form eines Recordsets wird mit der Objektvariablen rst referenziert. Die folgende Anweisung greift auf das einzige zurückgegebene Feld namens KategorieID zu und liest somit den Autowert des neuen Datensatzes aus. Was aber geschieht, wenn wir die Pass- Through-Abfrage einfach nur ausführen möchten, ohne den neuen Autowert zu beziehen? Fliegt uns diese dann um die Ohren? Nein es Bild 11: Als Teil der gespeicherten Prozedur arbeitet SCOPE_IDENTITY hingegen wie gewünscht. sind nur ein paar kleinere Änderungen nötig. Die folgende Variante stellt ReturnRecords auf False ein, außerdem wird kein Recordset erstellt, sondern die VBA-Prozedur einfach nur mit Execute ausgeführt: Public Function NeuenDatensatzMitID_INSERT_InSP() Dim db As dao.database Dim qdf As dao.querydef Dim rst As dao.recordset Set db = CurrentDb Set qdf = db.createquerydef("") With qdf.connect = Standardverbindungszeichenfolge.ReturnsRecords = True.SQL = "EXEC spinsertintokategorienmitid µ Set rst =.OpenRecordset 'Neue Kategorie 123', 'Beschreibung'" Debug.Print rst.fields("kategorieid") End With Set db = Nothing End Function Listing 4: Prozedur zum Erstellen eines temporären QueryDef-Objekts Seite 61

64 ACCESS SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN Public Function NeuenDatensatzMitID_INSERT_InSP_µ OhneRueckgabewert() Dim db As dao.database Dim qdf As dao.querydef Dim rst As dao.recordset Set db = CurrentDb Set qdf = db.createquerydef("") With qdf.connect = Standardverbindungszeichenfolge.ReturnsRecords = False.SQL = "EXEC dbo.spinsertintokategorienmitid µ 'Neue Kategorie 124', 'Beschreibung'".Execute End With Set db = Nothing End Function Allgemeine Prozeduren für das Ausführen von Aktionsabfragen Zum Ausführen von Aktionsabfragen haben wir zwei verschiedene Routinen vorgesehen eine mit und eine ohne Rückgabewert. Aktionsabfrage ohne Ergebnis Die einfachere ist die ohne Rückgabewert. Sie rufen diese Prozedur beispielsweise wie folgt auf (diese Prozedur finden Sie im Modul mdltoolssqlserver, die Beispiele im Modul mdlrdbmszugriff_datenbearbeiten): SPAktionsabfrageOhneErgebnis µ "dbo.spinsertintokategorienmitid", µ Standardverbindungszeichenfolge, "Neue Kategorie 234", µ "Beschreibung" Autowert-Abfrage per Code an gespeicherte Prozedur anhängen Nicht alle gespeicherten Prozeduren, die Aktionsabfragen wie beispielsweise eine INSERT INTO- oder SELECT INTO-Abfrage ausführen, enthalten gleich eine entsprechende SELECT-Anweisung, welche den zuletzt hinzugefügten Autowert ermittelt. Es kann beispielsweise sein, dass Sie ein Access-Frontend gegen ein SQL Server-Back end programmieren müssen, in dem Sie keine Änderungen durchführen können oder dürfen. Wie hängen Sie dennoch eine Anweisung wie SELECT SCOPE_IDENTITY()... so an die gespeicherte Prozedur an, dass diese im gleichen Gültig keits bereich ausgeführt wird? Die Antwort ist: Es gelingt nicht. Die gespeicherte Prozedur ist ein abgeschlossener Gültigkeitsbereich, also Scope, dessen Änderungen nach der Ausführung nicht über Funktionen wie SCOPE_IDENTITY() abgefragt werden können. Sollten Sie Daten an eine Tabelle hinzufügen wollen und müssen den Autowert ermitteln, können Sie nur direkt die INSERT INTO-Anweisung und die SELECT SCOPE_ IDENTITY()...-Abfrage gleichzeitig über eine Pass- Through-Abfrage absetzen. Dies ruft die gespeicherte Prozedur dbo.spinsertinto- KategorienMitID auf und übergibt die folgenden Parameter: strstoredprocedure: Name der auszuführenden gespeicherten Prozedur, hier dbo.spinsert- INTOKategorienMitID strverbindungszeichenfolge: zu verwendende Verbindungszeichenfolge, hier mit der Funk tion Standardverbindungszeichenfolge ermittelt varparameter: Parameter-Array, das beliebig viele Parameter entgegennehmen kann hier nur der Name der neuen Kategorie und der Beschreibung Die Prozedur erstellt ein neues QueryDef-Objekt ohne Name, da dieses nur temporär innerhalb dieser Prozedur genutzt werden soll. Dann setzt sie mit der Hilfsfunktion Parameterliste die eventuell in varparameter enthaltenen Werte zusammen siehe weiter unten. Nun stattet die Prozedur die Pass-Through-Abfrage mit den notwendigen Informationen aus: Seite 62

65 SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN ACCESS Connect erhält die Verbindungszeichenfolge, ReturnsRecords wird auf False eingestellt, da keine Daten zurückgeliefert werden sollen, SQL wird mit einem Ausdruck gefüllt, der sich aus der EXEC-Anweisung, dem Namen der gespeicherten Prozedur und der Parameterliste zusammensetzt und die Execute-Methode führt die Abfrage aus. Die VBA-Prozedur sieht schließlich wie in Listing 5 aus. Aktionsabfrage mit Ergebnis Diese Routine zur Durchführung einer Aktionsabfrage funktioniert prinzipiell genauso wie die zuvor beschriebene Variante. Allerdings erwartet sie einen einzelnen Rückgabewert. Das Be son dere ist, dass der Rückgabewert entweder in der gespeicherten Prozedur in Form einer entsprechenden SELECT-Abfrage implementiert werden kann (bolrueckgabeinspimplementiert erhält den Wert True) oder dass die Prozedur einfach die Anzahl der von der Aktionsabfrage betroffenen Datensätze ermittelt oder eine mit dem Parameter strrueckgabeausdruck festgelegte SELECT- Anweisung an die EXEC-Methode mit der Angabe der gespeicherten Prozedur anhängt. Achtung: Die gespeicherte Prozedur muss die Anweisung SET NOCOUNT OFF enthalten, die Anzahl der betroffenen Datensätze erfassen kann. Der Rest entspricht dem Aufruf der zuvor beschriebenen Prozedur SPAktionsabfrageOhneErgebnis (s. Listing 6). Ein Beispielaufruf sieht wie folgt aus. Hier hat bolrueckgabeinspimplementiert den Wert True, also wird keine SELECT-Anweisung zur Ermittlung eines Ergebnisses angehängt: Debug.Print SPAktionsabfrageMitErgebnis("dbo.spINSERTINTO- KategorienMitID", Standardverbindungszeichenfolge, True, "", "Neue Kategorie 345", "Beschreibung") Das Ergebnis entspricht dann dem von der Funktion SCOPE_IDENTITY in der gespeicherten Pro ze dur ermittelten Autowert des neuen Datensatzes. Angenommen, es handelt sich um eine reine Aktionsabfrage, dann können Sie mit folgendem Aufruf eine SELECT-Anweisung mit Public Sub SPAktionsabfrageOhneErgebnis(strStoredProcedure As String, strverbindungszeichenfolge As String, _ ParamArray varparameter() As Variant) Dim db As dao.database Dim qdf As dao.querydef Dim strparameter As String Set db = CurrentDb Set qdf = db.createquerydef("") strparameter = Parameterliste(varParameter) With qdf.connect = strverbindungszeichenfolge.returnsrecords = False.SQL = "EXEC " & strstoredprocedure & " " & strparameter.execute End With Set db = Nothing Listing 5: Prozedur zum Ausführen beliebiger gespeicherter Prozeduren mit Parametern, aber ohne Rückgabewert Seite 63

66 ACCESS SQL SERVER UND CO. RDBMS-ZUGRIFF PER VBA: DATEN BEARBEITEN dem Parameter strrueckgabeausdruck übergeben, die dann an die EXEC-Anweisung mit der gespeicherten Prozedur angehängt wird. Hier soll die Kategorie mit dem Primärschlüsselwert 1 gelöscht werden: Debug.Print SPAktionsabfrageMitErgebnis(µ "dbo.spdeletekategorienachid", µ Standardverbindungszeichenfolge, False, µ AS Geloescht", 1) Zu Beginn haben wir grundlegende Herausforderungen und Lösungen beschrieben und die verwendeten VBA- Routinen immer weiter verfeinert, bis wir am Ende die beiden Prozeduren SPAktionsabfrageOhneErgebnis und SPAktionsabfrageMitErgebnis herausgearbeitet haben. Beide haben Parameter, mit denen Sie die auszuführenden gespeicherten Prozeduren, die Verbindungszeichenfolge, Rückgabewerte und Parameter auswerten können. Zusammenfassung und Ausblick Dieser Beitrag erläutert die Grundlagen für das Durchführen von Datensatzbearbeitungen an den Daten einer SQL Server-Datenbank von VBA-Prozeduren aus. Public Function SPAktionsabfrageMitErgebnis(strStoredProcedure As String, strverbindungszeichenfolge As String, _ bolrueckgabeinspimplementiert As Boolean, strrueckgabeausdruck As String, _ ParamArray varparameter() As Variant) As Variant Dim db As dao.database Dim qdf As dao.querydef Dim rst As dao.recordset Dim strparameter As String Set db = CurrentDb Set qdf = db.createquerydef("") strparameter = Parameterliste(varParameter) With qdf.connect = strverbindungszeichenfolge.returnsrecords = True.SQL = "EXEC " & strstoredprocedure & " " & strparameter If Not bolrueckgabeinspimplementiert Then If Len(strRueckgabeausdruck) = 0 Then.SQL =.SQL & vbcrlf & AS RecordsAffected;" Else.SQL =.SQL & vbcrlf & strrueckgabeausdruck End If End If Set rst =.OpenRecordset End With SPAktionsabfrageMitErgebnis = Nz(rst.Fields(0)) Set db = Nothing End Function Listing 6: Prozedur zum Ausführen beliebiger gespeicherter Prozeduren mit Parametern mit Rückgabewert Seite 64

67 LÖSUNGEN TICKETSYSTEM, TEIL III ACCESS Ticketsystem, Teil III In unser Ticketsystem haben wir bereits Funktionen integriert, mit denen Sie die Kundenanfragen per Drag and Drop in der Datenbank speichern können. Außerdem haben wir ein Formular hinzugefügt, das alle offenen s anzeigt und eine einfache Möglichkeit enthält, daraus Tickets zu generieren. Diese wollen wir nun im vorliegenden Teil der Beitragsreihe verarbeiten, und zwar in dafür ausgelegten Formularen. Diese zeigen sowohl eine Übersicht aller Tickets filterbar nach dem Status und anderen Eigenschaften als auch den Verlauf eines einzelnen Tickets. Ticketübersicht Im Formular frmtickets wollen wir eine Übersicht aller Tickets liefern, und zwar nach verschiedenen Kriterien. Die Tickets sollen nach dem Anlagedatum sortiert werden können oder nach dem Kunden. Außerdem benötigen wir natürlich verschiedene Filter, etwa nach dem Kunden oder nach dem Status oder der Priorität. Das Formular soll per Doppelklick auf einen der Einträge die Möglichkeit bieten, das Ticket in der Detailansicht zu öffnen und alle bisher erfolgten Schritte darzustellen. Um keine eigene Programmierung etwa für die Sortierung vornehmen zu müssen, nutzen wir zur Anzeige der Tickets einfach ein Unterformular in der Datenblattansicht. Dieses bietet genügend Möglichkeiten zum Sortieren und Filtern der Einträge. Schnelle Filter wollen wir hinzufügen für den Status von Tickets sowie für die Priorität. Außerdem wollen wir eine Schaltfläche zum schnellen Aufheben der Sortierung und des Filters hinzufügen. Schließlich soll ein Ticket natürlich schnell per Doppelklick geöffnet werden können. Zusätzliche Tabellen Um den Status und die Priorität eines Tickets erfassen zu können, wollen wir der Tabelle tblticktes zwei neue Felder hinzufügen. Diese sollen als Fremdschlüsselfelder ausgelegt werden und zwei weitere neue Tabellen referenzieren. Bild 1: Entwurf der Tabelle tblstatus Die erste Tabelle heißt tblstatus und soll die verschiedenen Statuswerte erfassen (Status ist übrigens tatsächlich der Plural von Status, daher diese Tabellenbezeichnung). Sie finden den Entwurf dieser Tabelle in Bild 1. Neben dem Primärschlüsselfeld StatusID enthält die Tabelle noch das mit einem eindeutigen Index versehene Feld Status. Die zweite neue Tabelle heißt tblprioritaeten und ist fast genauso aufgebaut wie die Tabelle tblstatus. Sie enthält neben dem Primärschlüsselfeld PrioritaetID ebenfalls ein weiteres Feld zum Erfassen der Bezeichnung der Inhalte, in diesem Fall namens Prioritaet. Auch dieses Feld haben wir mit einem eindeutigen Index versehen (s. Bild 2). Die beiden Fremdschlüsselfelder der Tabelle tbltickets richten wir als Nachschlagefelder ein. Dazu wählen Sie Seite 65

68 ACCESS LÖSUNGEN TICKETSYSTEM, TEIL III keine aufsteigende Sortierung für irgendeines der Felder fest und aktivieren Sie die Option Schlüsselspalte ausblenden. Schließlich aktivieren Sie die referenzielle Integrität durch Markieren der Option Datenintegrität aktivieren (s. Bild 3). Bild 2: Entwurf der Tabelle tblprioritaeten Die Tabelle tbltickets sieht danach im Entwurf wie in Bild 4 aus, sodass wir Status und Priorität bequem per Nachschlagefeld auswählen können. Die Einrichtung von Nachschlagefeldern hat außerdem den praktischen Vorteil, dass Sie später, wenn Sie die Felder dieser Tabelle in den Entwurf von Formularen ziehen, direkt Kombinationfelder für solche Felder erhalten. Unterformular sfmtickets einrichten Genau davon profitieren wir gleich im nächsten Schritt. Wir erstellen ein neues Formular und speichern es unter dem Namen sfmtickets ab. Dieses soll das Unterformular eines weiteren Formulars namens frmtickets werden und die Bild 3: Letzter Schritt beim Einrichten eines Nachschlagefeldes zunächst für das Feld StatusID den Datentyp Nachschlage-Assistent aus und starten somit den gleichnamigen Assistenten. Wählen Sie hier die Tabelle tblprioritaeten aus, selektieren Sie die beiden Felder StatusID und Status, legen Sie Bild 4: Entwurf der soeben erweiterten Tabelle tbltickets Seite 66

69 LÖSUNGEN TICKETSYSTEM, TEIL III ACCESS Tickets in der Datenblattansicht übersichtlich anzeigen. Legen Sie als Datenherkunft des Formulars die Tabelle tbltickets fest. Ziehen Sie dann aus der Feldliste alle Felder der Tabelle in den Detailbereich des Formularentwurfs (s. Bild 5). Nun stellen Sie noch die Eigenschaft Standardansicht des Formulars auf Datenblatt ein und schließen das Formular. Nun legen Sie das Formular frmtickets an und ziehen das Unterformular sfmtickets in den Detailbereich des Entwurfs. Stellen Sie die Eigenschaften Horizontaler Anker und Vertikaler Anker jeweils auf den Wert Beide ein. Für die optische Gestaltung des Hauptformulars legen Sie noch die Eigenschaften Navigationsschaltflächen, Datensatzmarkierer, Bildlaufleisten und Trennlinien auf den Wert Nein fest. Bild 6: Entwurf des Formulars frmtickets Bild 5: Entwurf des Unterformulars sfmtickets Filter für die Ticketübersicht steuern Nun nutzen wir die beiden Klassen clsfastfilter und clsfastfiltercontrol, die wir im Beitrag Schneller Filter vorgestellt haben ( de/1072). Importieren Sie diese beiden Klassen in die aktuelle Datenbank. Nun statten Sie das Formular frmtickets mit einigen Steuerelementen aus, mit denen der Benutzer die Daten filtern kann. Sie benötigen zwei Schaltlfächen namens cmdnachfeldfiltern und cmdnachinhaltfiltern, ein Kontrollkästchen namens chkfilterkriterienverknuepfen und eine Optionsgruppe mit dem Namen ogrverknuepfen- Mit (s. Bild 6). Für das Seite 67

70 ACCESS LÖSUNGEN TICKETSYSTEM, TEIL III Kontrollkästchen stellen Sie als Standardwert False ein, für die Optionsgruppe den Wert 1. Außerdem stellen Sie für die Optionsgruppe die Eigenschaft Aktiviert aus Nein ein. Schließlich stellen Sie noch die Eigenschaft Enthält Modul des Unterformulars auf den Wert Ja ein. Nun fügen wir den Code zum Initialisieren der Filterfunktionen hinzu. Das Klassenmodul des Formulars frmtickets sieht danach wie folgt aus: Dim objfastfilter As clsfastfilter Private Sub Form_Load() Set objfastfilter = New clsfastfilter With objfastfilter Set.Subform = Me!sfmTickets.Form Set.FastFilterButton = Me!cmdNachFeldFiltern Set.FastFilterContainsButton = µ Set.CombineFilterCheckbox = µ Me!cmdNachInhaltFiltern Me!chkFilterkriterienVerknuepfen Set.ANDOrOROptiongroup = Me!ogrVerknuepfenMit End With Nach dem ersten Test, bei dem wir einen Teil des Feldes Kunde markieren und die Schaltlfäche cmdnachinhalt- Filtern anklicken, stellen wir allerdings fest, dass dies nicht reibungslos funktioniert. Dies wird durch die Klasse clsfastfiltercontrol nicht abgedeckt. Wir haben probiert, dies so anzupassen, dass auch berechnete Felder aufgelöst werden können, aber dies gelingt nicht, weil es keine Möglichkeit gibt, den berechneten Ausdruck über das Objektmodell zu ermitteln. Dies zeigt im Übrigen auch sehr schön, dass auch eine ausgefeilte Lösung nur selten alle möglichen Konstellationen berücksichtigen kann... Also müssen wir uns einen kleinen Workaround einfallen lassen und die Klasse clsfastfiltercontrol etwas anpassen und zwar so, dass wir in Fällen wie dem hier vorliegenden selbst Hand anlegen können. In diesem Fall ist das Problem, dass wir den Namen des Feldes der Datensatzherkunft des Kombinationsfeldes ermitteln wollen, dessen Inhalt im Kombinationsfeld angezeigt wird. Dieser lautet aber schlicht Kunde und entspricht dem Alias-Namen des berechneten Feldes, das eigentlich den Ausdruck [Nachname] & ", " & [Vorname] enthält. An diesen kommen wir aber programmatisch nicht heran (außer, wir würden den SQL-Ausdruck auseinandernehmen aber dieser kann ja auch beliebig komplex werden, also lassen wir gleich die Finger davon...). Deshalb tragen wir diesen Ausdruck einfach in die Eigenschaft Marke (englisch und unter VBA: Tag) des Kombinationsfeldes ein (s. Bild 7). Der Hintergrund ist, dass das Kombinationsfeld KundeID eine Datensatzherkunft mit einem berechneten Feld verwendet: SELECT KundeID, [Nachname] & ", " & [Vorname] AS Kunde FROM tblkunden; Bild 7: Eintragen des angezeigten Ausdrucks des Kombinationsfeldes in der Eigenschaft Marke Seite 68

71 LÖSUNGEN TICKETSYSTEM, TEIL III ACCESS Nun müssen wir nur noch... den Code der Klasse cls- FastFilterControl anpassen, damit diese, falls sich Else ein Wert in der Eigenschaft Tag befindet, diesen Wert End If statt des Feldnamens selbst verwendet. Genau genommen handelt es End Function sich dabei um die Funktion LookupSearchstring, die wir um die If...Then...Else- Bedingung aus Listing 1 erweitern. Diese prüft schlicht, ob die Tag-Eigenschaft einen Wert enthält, und trägt diesen dann statt des Feldnamens in die Variable strfieldcriteria ein. Wenn wir dies nun ausprobieren, erhalten wir zum Beispiel nach Markierung des Buchstabens M den folgenden SQL-Ausdruck: KundeID IN (SELECT KundeID FROM tblkunden WHERE Nachname & ", " & Vorname LIKE '*M*') Private Function LookupSearchstring(cbo As ComboBox, strvalue As String) As String If Len(cbo.Tag) = 0 Then strfieldcriteria = rst.fields(i).name strfieldcriteria = cbo.tag strsql = strforeignkey & " IN (SELECT " & strboundcolumn & " FROM " _ & strboundtable & " WHERE " & strfieldcriteria & " LIKE '" & strvalue & "')" LookupSearchstring = strsql Listing 1: Anpassung der Funktion LookupSearchstring den Funktionen zum Öffnen oder Löschen von Tickets aus dem Datenblatt unter diesem anlegen. Die beiden Schaltflächen wollen wir optisch etwas ansprechender gestalten und fügen daher zwei neue Icons zur Datenbank hinzu. Dazu markieren Sie eine beliebige leere Stelle im Formularentwurf und wählen dann den Ribbon-Eintrag Entwurf Steuerelemente Bild einfügen Durchsuchen... aus. Steuerelemente zum Bearbeiten der Tickets Nachdem wir die Filterfunktionen oberhalb des Datenblatts angelegt haben, wollen wir die Schaltflächen mit Bild 8: Schaltflächen zum Bearbeiten und Löschen eines Tickets Fügen Sie die Bilddateien hinzu, sofern Sie eine entsprechende Sammlung von Icons haben (anderenfalls lassen Sie die Icons einfach weg und arbeiten mit einer normalen Beschriftung). Wir haben zwei Icons namens edit.png und delete.png hinzugefügt, die dann in der Eigenschaft Bild der Schaltflächen als edit beziehungsweise delete angezeigt werden. Diese wählen wir dort aus. Als Beschriftung fügen Sie die Texte Bearbeiten und Löschen, als Namen die Werte cmdbearbeiten und cmdloeschen hinzu (s. Bild 8). Seite 69

72 ACCESS LÖSUNGEN TICKETSYSTEM, TEIL III Damit die Texte auch neben dem Icons erscheinen, stellen Sie die Eigenschaft Anordnung der Bildbeschriftung auf Allgemein ein. Um den Hintergrund transparent zu gestalten, legen Sie den Wert Transparent für die Eigenschaft Hintergrundart fest. Da wir für das Unterformular-Steuerelement die Eigenschaft Vertikaler Anker auf Beide eingestellt haben, würden die Schaltflächen, die aktuell noch oben verankert sind, beim Vergrößern der Höhe des Formulars hinter dem Unterformular verschwinden. Stellen wir also die Eigenschaft Vertikaler Anker der beiden Schaltflächen auf Unten ein, damit diese entsprechend nach unten verschoben werden. Löschen eines Tickets Die Schaltfläche cmdloeschen soll das aktuell im Unterformular markierte Ticket löschen. Dazu hinterlegen wir für die Ereigniseigenschaft Beim Klicken der Schaltfläche die folgende Prozedur: Private Sub cmdloeschen_click() If Not IsNull(Me!sfmTickets.Form!TicketID) Then If MsgBox("Ticket löschen?", vbyesno) = vbyes Then Me!sfmTickets.SetFocus Else End If End If RunCommand accmddeleterecord MsgBox "Bitte markieren Sie zunächst den zu µ löschenden Eintrag." Die Prozedur prüft zunächst, ob überhaupt ein Datensatz im Unterformular markiert ist (IsNull(Me!sfmTickets. Form!TicketID)). Falls nicht, erscheint eine entsprechende Meldung. Anderenfalls löscht die Prozedur dieses Ticket nach entsprechender Rückfrage (s. Bild 9). Bearbeiten eines Tickets Die zweite Schaltfläche namens cmdbearbeiten soll das Formular frmticket mit den Detailinformationen des Tickets anzeigen. Dazu hinterlegen wir zunächst die folgende Ereignisprozedur: Private Sub cmdbearbeiten_click() DoCmd.OpenForm "frmticket", WhereCondition:=µ "TicketID = " & Me!sfmTickets.Form!TicketID Diese Prozedur öffnet ein Formular namens frmticket und übergibt als Bedingung einen Ausdruck, der etwa so aussieht: TicketID = 123 Bild 9: Ticketübersicht in Aktion, hier beim Löschen eines Tickets Das Detailformular frmticket Bevor wir dieses Formular erstellen, müssen wir uns überlegen, wie dieses aussehen soll und welche Aufgaben damit zu erledigen sind. Da die Aufgaben, Seite 70

73 LÖSUNGEN TICKETSYSTEM, TEIL III ACCESS die wir mit diesem Formular durchführen wollen, jedoch etwas komplexer sind, erstellen wir zunächst ein Basis-Formular mit den grundlegenden Funktionen. In diesem Fall soll das Formular an die Tabelle tbltickets gebunden werden und alle Felder dieser Tabelle anzeigen mit Ausnahme von MailItemID, womit je der Eintrag der zugrunde liegenden aus der Tabelle tblmailitems referenziert wird. Die Felder der Tabelle ordnen wir als Textfelder beziehungsweise Kombinationsfelder zunächst wie in Bild 10 an. Dabei soll das Feld Ticketinhalt beim Vergrößern des Formulars in die vertikale und horizontale Richtung ebenfalls in beiden Richtungen vergrößert werden, weshalb wir die Eigenschaften Horizontaler Anker und Vertikaler Anker beide auf den Wert Beide einstellen. Die beiden darüber liegenden Felder sollen nur in die horizontale Richtung gestreckt werden, also erhalten diese nur für die Eigenschaft Horizontaler Anker den Wert Beide. Die darunter befindlichen Steuerelemente sollen nur nach unten verschoben werden, wenn das Formular nach unten vergrößert wird, also stellen wir die Eigenschaft Vertikaler Anker für diese Steuerelemente auf den Wert Unten ein. Die Schaltfläche cmdok führt die folgende Ereignisprozedur aus: Bild 10: Erster Entwurf des Formulars zur Detailansicht eines Tickets Anzeigen der zugrunde liegenden Die Anzeige der Ticketeigenschaften ist allerdings noch längst nicht alles, was dieses Formulars leisten soll. Da jedes Ticket auf Basis einer eines Kunden erstellt wurde, wollen wir auch die Möglichkeit haben, die entsprechende öffnen zu können. Dazu fügen wir dem Formular eine weitere Schaltfläche hinzu, und zwar links neben dem Steuerelement zur Anzeige des Feldes Ticketinhalt (s. Bild 11). Die Schaltfläche nennen wir cmdanfrageoeffnen. Sie soll die in der Tabelle tblmailitem enthaltene und zum aktuellen Ticket gehörende im entsprechenden Outlook-Fenster anzeigen. Diese Schaltfläche löst die folgende Ereignisprozedur aus: Private Sub cmdanfrageoeffnen_click() Dim strentryid As String Dim objnamespace As Outlook.NameSpace Private Sub cmdok_click() DoCmd.Close acform, Me.Name Bild 11: Neue Schaltfläche zum Öffnen der Kunden- Seite 71

74 ACCESS LÖSUNGEN TICKETSYSTEM, TEIL III Dim objmailitem As Outlook.MailItem strentryid = DLookup("EntryID", "tblmailitems", µ Set objnamespace = GetMAPI Set objmailitem = µ "MailitemID = " & Me!MailitemID) objnamespace.getitemfromid(strentryid) objmailitem.display Die Prozedur deklariert eine Variable zum Aufnehmen der EntryID, also des eindeutigen Identifizierers für die Outlook- , eine Variable für das MAPI-Namespace- Objekt sowie eine Variable für die Mail selbst. Sie ermittelt zunächst die EntryID zu der Ausgangsmail zum angezeigten Ticket. Diese finden wir in der Tabelle tblmailitems, aus der wir den gesuchten Eintrag über den Primärschlüsselwert ermitteln, der in unserem aktuellen Ticket- Datensatz im Fremdschlüsselfeld MailItemID gespeichert ist. Dabei unterstützt uns eine entsprechende DLookup- Funktion. Danach füllen wir die Variable objnamespace über die Funktion GetMAPI und verwenden deren Funktion GetItemFromID, um mit der EntryID eine Referenz auf die gesuchte zu erhalten. Diese speichern wir dann in der Objektvariablen objmailitem, deren Methode Display wir wiederum nutzen können, um diese im Mailfenster von Outlook zu öffnen (s. Bild 12). Dies zeigt die grundlegende Technik für einige der nachfolgend beschriebenen Schritte. Ticket erstellt was nun? Wir haben also nun eine Möglichkeit, Tickets auf Basis von Kunden- s zu erstellen und können diese in einer Übersicht anzeigen. Außerdem haben wir ein Formular erstellt, das die Details zu einem ausgewählten Ticket Bild 12: Anzeige der zum Ticket Seite 72

75 LÖSUNGEN TICKETSYSTEM, TEIL III ACCESS anzeigt und die Möglichkeit bietet, die Ausgangsmail zu öffnen. Kategorisieren von Anfragen Wie aber geht es nun weiter? Was geschieht mit einem Ticket, nachdem es aufgenommen wurde? Dazu gibt es je nach Anwendungsfall verschiedene Möglichkeiten. In meinem Fall kommen Anfragen von Kunden, die ein Heft eines Abonnements nicht erhalten haben, die ein Abonnement kündigen wollen, die auf eine Lieferung eines Buchs warten, die auf eine Rechnung warten, die eine falsche Rechnung erhalten haben, die sich nicht in ihr Kundenkonto einloggen können, die den Zugang zum Online-Archiv nicht finden, die eine Frage zu einem Artikel haben, die einen Fehler in einem Artikel gefunden haben Buchhaltung geschickt werden muss beispielsweise, weil die Rechnungsadresse korrigiert werden muss oder auch, weil die Rechnung falsche Positionen enthält. Kompliziertester Fall: Es ist noch eine weitere Aktion nötig, also etwa das erneute Versenden eines Produkts an den Kunden, die Korrektur eines Artikels oder die Beantwortung einer technischen Frage zu einem Artikel. Wir wollen zunächst den ersten Fall technisch abbilden. Dazu ist eine Antwort an den Kunden erforderlich, die gegebenenfalls automatisch aus einer Vorlage formuliert werden kann, gegebenenfalls aber auch noch eine manuelle Ergänzung erfordert. Speichern der Aktionstypen in der Tabelle tblaktionen Für jeden Schritt, also etwa für einen»einfachen Fall«, wollen wir einen Eintrag in einer Tabelle vornehmen, die mit dem Ticket aus der Tabelle tbltickets verknüpft ist. In dieser Tabelle, die wir tblaktionen nennen werden, wollen wir den Typ der Aktion festlegen. Diese speichern wir wiederum in der Tabelle tblaktionstypen, deren Entwurf wie in Bild 13 aussieht. und vieles mehr. Mögliche Aktionen auf Kundenanfragen Die Anfragen der Kunden laufen dann auf verschiedene durchzuführende Aktionen hinaus: Im einfachsten Fall: schlichte Antwort an den Kunden, zum Beispiel bei einer fehlenden Rechnung, die ohnehin in den nächsten Tagen versendet wird, oder dem Hinweis, wo er die Zugangsdaten für das Online-Archiv findet. Komplizierterer Fall: Wenn nicht nur eine Mail an den Kunden, sondern auch noch eine Mail etwa an die Bild 13: Tabelle zum Speichern der Aktionstypen Seite 73

IM UNTERNEHMEN RIBBON-KLASSEN SCHNELLER FILTER

IM UNTERNEHMEN RIBBON-KLASSEN SCHNELLER FILTER Ausgabe 01/2017 ACCESS RIBBON-KLASSEN Programmieren Sie das Ribbon komplett ohne den Einsatz von XML und Callback-Funktionen (ab S. 8). In diesem Heft: TICKETSYSTEM, TEIL III Weiter geht es mit der Ticketverwaltung

Mehr

ACCESS. Formulare per VBA referenzieren FORMULARE MIT VBA PROGRAMMIEREN FORMULARE PER VBA REFERENZIEREN BASICS

ACCESS. Formulare per VBA referenzieren FORMULARE MIT VBA PROGRAMMIEREN FORMULARE PER VBA REFERENZIEREN BASICS Formulare per VBA referenzieren Wenn Sie Formulare und Steuerelemente programmieren wollen, müssen Sie wissen, wie Sie diese referenzieren. Nicht immer geschieht dies vom Klassenmodul des Formulars selbst

Mehr

ACCESS. Access-Daten nach Excel verknüpfen INTERAKTIV ACCESS-DATEN NACH EXCEL VERKNÜPFEN BASICS

ACCESS. Access-Daten nach Excel verknüpfen INTERAKTIV ACCESS-DATEN NACH EXCEL VERKNÜPFEN BASICS -DATEN NACH EXCEL VERKNÜPFEN Access-Daten nach Excel verknüpfen Wir haben uns bereits in verschiedenen Artikeln angesehen, wie Sie von Access aus auf die Daten einer Excel-Datei zugreifen können ob per

Mehr

Zweitens über eine Abfrage, welche die Tabellen tblartikel und tbllieferanten verknüpft. Auf diese Weise würde das Kombinationsfeld

Zweitens über eine Abfrage, welche die Tabellen tblartikel und tbllieferanten verknüpft. Auf diese Weise würde das Kombinationsfeld Filterkriterien für Formulare, Teil III: Kombinationsfelder In den ersten beiden Teilen dieser Artikelreihe haben Sie erfahren, wie Sie Felder der verschiedenen Datentypen filtern. Nun geht es ans Eingemachte:

Mehr

Access Programmierung. Ricardo Hernández García. 1. Ausgabe, November 2013 ACC2013P

Access Programmierung. Ricardo Hernández García. 1. Ausgabe, November 2013 ACC2013P Access 2013 Ricardo Hernández García 1. Ausgabe, November 2013 Programmierung ACC2013P Die VBA-Entwicklungsumgebung 5 Weitere Eingabehilfen Im Menü Bearbeiten finden Sie noch weitere Hilfen, die Ihnen

Mehr

öffnen den Tabellenverknüpfungs-Manager. Bild 1: Verknüpfte Tabellen in einer Datenbank Bild 2: Fehlgeschlagener Zugriff auf eine verknüpfte Tabelle

öffnen den Tabellenverknüpfungs-Manager. Bild 1: Verknüpfte Tabellen in einer Datenbank Bild 2: Fehlgeschlagener Zugriff auf eine verknüpfte Tabelle Der neue Tabellenverknüpfungs-Manager Still und heimlich wurde der alte Tabellen-Verknüpfungsmanager von Microsoft ausgetauscht. Der Tabellenverknüpfungs-Manager dient dazu, bestehende Verknüpfungen mit

Mehr

Im Original veränderbare Word-Dateien

Im Original veränderbare Word-Dateien Die Benutzeroberfläche von Access Menüband Das am oberen Bildschirmrand befindliche Menüband beinhaltet die meisten Befehle von Access. Im Menüband sind Schnellzugriffsleiste und Titelleiste integriert.

Mehr

RibbonProgram mierung

RibbonProgram mierung ой Andre Minhorst Melanie Breden RibbonProgram mierung für Office 2007 Access, Excel, Word, Outlook, PowerPoint ADDISON-WESLEY An imprint of Pearson Education München Boston San Francisco Harlow, England

Mehr

Ribbon- Programmierung für Office 2007

Ribbon- Programmierung für Office 2007 André Minhorst Melanie Breden Ribbon- Programmierung für Office 2007 Access, Excel, Word, Outlook, PowerPoint An imprint of Pearson Education München Boston San Francisco Harlow, England Don Mills, Ontario

Mehr

ACCESS. Kombinationsfeld um Suche erweitern FORMULARE FÜR DIE DATENEINGABE KOMBINATIONSFELD UM SUCHE ERWEITERN BASICS

ACCESS. Kombinationsfeld um Suche erweitern FORMULARE FÜR DIE DATENEINGABE KOMBINATIONSFELD UM SUCHE ERWEITERN BASICS Kombinationsfeld um Suche erweitern Kombinationsfelder sind schon eine praktische Einrichtung: Sie erlauben nicht nur die Auswahl von Einträgen, die schon nach dem Alphabet voreingestellt sind, sondern

Mehr

Vorwort...10 Einleitung...12 Lernen Üben Anwenden...12 Inhalt und Aufbau des Buches...13 Inhalt...13 Aufbau Access 2007 (fast) alles ist

Vorwort...10 Einleitung...12 Lernen Üben Anwenden...12 Inhalt und Aufbau des Buches...13 Inhalt...13 Aufbau Access 2007 (fast) alles ist Vorwort...10 Einleitung...12 Lernen Üben Anwenden...12 Inhalt und Aufbau des Buches...13 Inhalt...13 Aufbau...14 1 Access 2007 (fast) alles ist neu...16 Sinnvolle Optionseinstellungen...17 Standarddatenbankordner

Mehr

Lorenz Hölscher. Richtig einsteigen: Access 2013 VBA-Programmierung Von den Grundlagen bis zur professionellen Entwicklung

Lorenz Hölscher. Richtig einsteigen: Access 2013 VBA-Programmierung Von den Grundlagen bis zur professionellen Entwicklung Lorenz Hölscher Richtig einsteigen: Access 2013 VBA-Programmierung Von den Grundlagen bis zur professionellen Entwicklung 16 Kapitel 1: Einleitung Teil I»Erste Schritte«enthält diese Einleitung mit der

Mehr

Inhalt. Dokument Beschreibung. Bentley Technical Support ProStructures.Net - Zusatzprogrammierung Visual Basic Express PST_Bearbeitung_Dialog

Inhalt. Dokument Beschreibung. Bentley Technical Support ProStructures.Net - Zusatzprogrammierung Visual Basic Express PST_Bearbeitung_Dialog Bentley Technical Support ProStructures.Net - Zusatzprogrammierung Inhalt Dokument Beschreibung... 1 Windows Form einfügen... 2 Steuerelemente einfügen... 2 Steuerelemente Titel und Name... 3 Dialog Laden

Mehr

Ribbon- Programmierung für Office 2007

Ribbon- Programmierung für Office 2007 André Minhorst Melanie Breden Ribbon- Programmierung für Office 2007 Access, Excel, Word, Outlook, PowerPoint An imprint of Pearson Education München Boston San Francisco Harlow, England Don Mills, Ontario

Mehr

Formulare für die Dateneingabe Mehrere Formularinstanzen anzeigen

Formulare für die Dateneingabe Mehrere Formularinstanzen anzeigen Die Datensätze einer Tabelle zeigen Sie meist in einer Übersicht wie einem Datenblatt oder einem Listenfeld an. Für die Bearbeitung öffnen Sie den gewünschten Datensatz in einem Detailformular, das die

Mehr

Microsoft Access 2010 Bilder

Microsoft Access 2010 Bilder Microsoft Access 2010 Bilder Hyperlinks... arbeiten ähnlich wie ein Link in einer Webseite. sind ein Verweis auf eine Datei (access2010\material\beispiel\tabledevelop\automat.accdb). können ein Verweis

Mehr

Ein erstes "Hello world!" Programm

Ein erstes Hello world! Programm OOP Henrik Horstmann 14. September 2014 Inhaltsverzeichnis Inhaltsverzeichnis 1 Bedeutung der Symbole...1 2 Die Benutzer Oberfläche von HOOPLU...2 2.1 Projekte öffnen und speichern...2 2.2 Die Klasse Program

Mehr

Ribbon-Elemente, Attribute und Ereignisse im Überblick

Ribbon-Elemente, Attribute und Ereignisse im Überblick -Elemente, Attribute und Ereignisse im Überblick In den folgenden Tabellen finden Sie die für die Programmierung von s notwendigen Elemente und deren Eigenschaften und Ereignisse. Elemente, Attribute und

Mehr

ACCESS. 1:1-Beziehungen TABELLEN ENTWERFEN 1:1-BEZIEHUNGEN BASICS

ACCESS. 1:1-Beziehungen TABELLEN ENTWERFEN 1:1-BEZIEHUNGEN BASICS 1:1-Beziehungen 1:1-Beziehungen können für eine ganze Reihe von Anwendungzwecken sinnvoll sein. Sie können damit beispielsweise die Liefer- und/oder die Rechnungsanschrift für einen Kundendatensatz in

Mehr

Verknüpfte Daten kopieren

Verknüpfte Daten kopieren Das Kopieren einfacher Datensätze ist schnell erledigt. Markieren, kopieren, einfügen schon liegt der neue Datensatz vor. Was aber geschieht, wenn an dem zu kopierenden Datensatz noch weitere Daten hängen

Mehr

Rückgabewerte von Methoden

Rückgabewerte von Methoden OOP Rückgabewerte von Methoden Henrik Horstmann 14. September 2014 Inhaltsverzeichnis Inhaltsverzeichnis 1 Bedeutung der Symbole...1 2 Rückgabewerte von Methoden...2 3 Der freundliche Computer...2 3.1

Mehr

Das Kapitel im Überblick

Das Kapitel im Überblick Das Kapitel im Überblick Ihr persönliches Menü Persönliche Registerkarten Damit der Schnellzugriff auch schnell ist So haben Sie wichtige Befehle im Griff Weitere Befehle Befehle auswählen Alle Befehle

Mehr

ACCESS. Aufgabenplaner LÖSUNGEN AUFGABENPLANER BASICS

ACCESS. Aufgabenplaner LÖSUNGEN AUFGABENPLANER BASICS Aufgabenplaner Im Artikel Berichtsansicht haben Sie eine neue Ansicht für Berichte kennen gelernt, die ganz neue Möglichkeiten eröffnet. Sie können damit hierarchische Daten anzeigen, ohne das Tree- View-Steuerelement

Mehr

Sie haben mehrere Möglichkeiten neue Formulare zu erstellen. Achten Sie darauf, dass das Objekt Formulare aktiviert ist: Klicken Sie auf.

Sie haben mehrere Möglichkeiten neue Formulare zu erstellen. Achten Sie darauf, dass das Objekt Formulare aktiviert ist: Klicken Sie auf. 9. FORMULARE Mit Formularen können Sie sehr komfortabel Daten in eine Tabelle eingeben und auch anzeigen lassen, da Sie viele Eingabemöglichkeiten zur Verfügung haben. EIN EINFACHES FORMULAR ERSTELLEN

Mehr

Skriptum Bauinformatik SS 2013 (Vorlesung IV)

Skriptum Bauinformatik SS 2013 (Vorlesung IV) Skriptum Bauinformatik SS 2013 (Vorlesung IV) Stand: 23.04.2013 Dr. Johannes Lange 2 Inhalt Objektorientierte Programmierung Großes Beispiel... 2 Klasse erstellen... 2 Erzeugen eines Objekts der Klasse...

Mehr

Empfänger. Alle Empfänger, die sich für Ihre(n) Newsletter angemeldet haben, werden in der Empfängerverwaltung erfasst.

Empfänger. Alle Empfänger, die sich für Ihre(n) Newsletter angemeldet haben, werden in der Empfängerverwaltung erfasst. Empfänger Alle Empfänger, die sich für Ihre(n) Newsletter angemeldet haben, werden in der Empfängerverwaltung erfasst. Für eine größere Flexibilität in der Handhabung der Empfänger erfolgt der Versand

Mehr

Zurückkonvertieren von Access 2007-Datenbanken in das Access 2002/3-Format

Zurückkonvertieren von Access 2007-Datenbanken in das Access 2002/3-Format Zurückkonvertieren von Access 2007-Datenbanken in das Access 2002/3-Format Sie mögen hin und wieder vor der Aufgabe stehen, eine Datenbank, die sie unter Access 2007 in dessen nativem Format (accdb) erstellt

Mehr

Tools4Tools Basisversion Bedienungsanleitung

Tools4Tools Basisversion Bedienungsanleitung Tools4Tools Basisversion Bedienungsanleitung Version: 1.0.0.4 1 Inhaltsverzeichnis 1.0 Tools4Tools die Oberfläche...3 1.1 Tools4Tools Reiter Ansicht...3 1.2 Tools4Tools Reiter Export/Import...4 1.3 Tools4Tools

Mehr

Formulare. Datenbankanwendung 113

Formulare. Datenbankanwendung 113 Formulare Wenn Sie mit sehr umfangreichen Tabellen arbeiten, werden Sie an der Datenblattansicht von Access nicht lange Ihre Freude haben, sind dort doch immer zu wenig Felder gleichzeitig sichtbar. Um

Mehr

ACCESS. Berechnete Felder in Tabellen TABELLEN ENTWERFEN BERECHNETE FELDER IN TABELLEN BASICS

ACCESS. Berechnete Felder in Tabellen TABELLEN ENTWERFEN BERECHNETE FELDER IN TABELLEN BASICS Berechnete Felder in Tabellen Berechnete Felder in Tabellen sind ein Feature, das mit der Version 2010 von Access hinzugekommen ist. Dabei handelt es sich um die Möglichkeit, die Inhalte der übrigen Felder

Mehr

Word 2010 Formulare erstellen mit Inhaltssteuerelementen

Word 2010 Formulare erstellen mit Inhaltssteuerelementen WO.020, Version 1.0 23.09.2013 Kurzanleitung Word 2010 Formulare erstellen mit en Bei der Erstellung von Word-Formularen werden in den meisten Fällen sogenannte Formularfelder eingesetzt, also Platzhalter

Mehr

Tutorial: Verwendung von Visual Studio 2005 als XML-Werkzeug

Tutorial: Verwendung von Visual Studio 2005 als XML-Werkzeug Dr. Thomas Meinike // Hochschule Merseburg (FH) // FB IKS // XML-Kurs // 10/2006 1/6 Tutorial: Verwendung von Visual Studio 2005 als XML-Werkzeug Neue Datei erstellen [Menü Datei Neu Datei... oder Strg+N]:

Mehr

16 Ribbons. 16.1 Menüführung per Ribbon

16 Ribbons. 16.1 Menüführung per Ribbon 16 Ribbons Wenn Sie mal eben eine Anwendung für den Privatgebrauch oder für Kollegen erstellen, die sich mit Access auskennen, werden Sie ein paar Tabellen, Abfragen, Formulare und Berichte lieblos dahinprogrammieren

Mehr

Handbuch für Redakteure

Handbuch für Redakteure Handbuch für Redakteure Erste Schritte... 1 Artikel erstellen... 2 Artikelinhalt bearbeiten... 3 Artikel bearbeiten... 3 Trennen der Druck- und der Online-Version.. 4 Grunddaten ändern... 5 Weitere Artikel-eigenschaften...

Mehr

Tau-Cloud. Integrationsmanagement - Administratorenhandbuch. * Der griechische Buchstabe T (sprich Tau ) steht für Perfektion.

Tau-Cloud. Integrationsmanagement - Administratorenhandbuch. * Der griechische Buchstabe T (sprich Tau ) steht für Perfektion. Tau-Cloud Integrationsmanagement - Administratorenhandbuch * Der griechische Buchstabe T (sprich Tau ) steht für Perfektion. Idee und Copyright: rocom GmbH Eichenstraße 8a, 83083 Riedering Zentrale: 08036/94

Mehr

OLConnector Programmierung

OLConnector Programmierung Das Vorgehen, um Outlook zu automatisieren, unterscheidet sich mit dem nur geringfügig vom üblicherweise dafür eingesetzten. Um irgendwelche Aktionen ausführen zu können, benötigt man die laufende Instanz

Mehr

ACCESS. Laufende Summen in Tabellen TABELLEN ENTWERFEN LAUFENDE SUMMEN IN TABELLEN BASICS

ACCESS. Laufende Summen in Tabellen TABELLEN ENTWERFEN LAUFENDE SUMMEN IN TABELLEN BASICS Laufende Summen in Tabellen Immer wieder taucht die Frage auf, wie man in Access die Inhalte von Zahlenfelder mehrerer Datensätze aufsummiert. Unter Excel ist das einfach dort trägt man einfach die Summe

Mehr

Effektiver Umstieg auf Office Thomas Alker, Konrad Stulle UM-O2010

Effektiver Umstieg auf Office Thomas Alker, Konrad Stulle UM-O2010 Effektiver Umstieg auf Office 2010 Thomas Alker, Konrad Stulle UM-O2010 1. Ausgabe, 4. Aktualisierung, Februar 2012 Die neue Oberfläche Keine Angst vor dem neuen Office Word 2010 und Excel 2010 basieren

Mehr

Access-Benutzeroberfläche

Access-Benutzeroberfläche Mit Access 2007 hat Microsoft das Ribbon eingeführt und Access seiner Werkzeuge beraubt, Menüleisten und über die Benutzeroberfläche zu erstellen. Nun gut: Menüleisten gibt es nicht mehr, aber können in

Mehr

Formularerstellung In Word 2010

Formularerstellung In Word 2010 Formularerstellung In Word 2010 Grundlagen eines Formulars in Word Die Elemente Text und Feld, (Beschreibung und Ausfüllfeld) Die drei Formular-Feldtypen, Text, Dropdown und Kreuzchen Der globale Schutz

Mehr

Erstellung von abhängigen Kombinationsfeldern mit dazugehörigen Unterformular...1

Erstellung von abhängigen Kombinationsfeldern mit dazugehörigen Unterformular...1 Erstellung von abhängigen Kombinationsfeldern mit dazugehörigen Inhaltsverzeichnis Erstellung von abhängigen Kombinationsfeldern mit dazugehörigen...1 Inhaltsverzeichnis...1 Einleitung...1 Vorgaben...1

Mehr

MiniPPS - Systembeschreibung

MiniPPS - Systembeschreibung MiniPPS - Systembeschreibung Hans-Christian Walter Beuth Hochschule für Technik Einführungsbeispiel für Access Version 04.11.2012 Inhalt 1. Access einrichten 2 2. Tabellen 5 3. Abfrage 9 4. Formulare 10

Mehr

Hierfür sind mit dem Content Management System (CMS) Joomla in Verbindung mit SIGE Pro Inhalte angelegt worden, die genau diesen Zweck erfüllen.

Hierfür sind mit dem Content Management System (CMS) Joomla in Verbindung mit SIGE Pro Inhalte angelegt worden, die genau diesen Zweck erfüllen. Tutorial / Anleitung Fotogalerie SIGE Pro v3.1.0 mit Joomla CMS 3.5.1 Klaus Große-Erwig Stand: 05/2016 Mit der Fotogalerie SIGE Pro ist ein wahlfreier Zugriff auf große Bestände an Bildmaterial möglich,

Mehr

Schulungsunterlagen Suchen,

Schulungsunterlagen Suchen, Schulungsunterlagen Suchen, Finden, Drucken, Exportieren Inhalt I Suchen & Finden... 2 1. Eine Lehrveranstaltung anhand ihrer Bezeichnung finden... 2 2. Erweiterte Suche und Filter... 3 3. Sucheingaben

Mehr

Typo3 Dokumentation. Erklärungen und Anmerkungen zum Umgang mit dem Content Management System Typo3. Version und älter

Typo3 Dokumentation. Erklärungen und Anmerkungen zum Umgang mit dem Content Management System Typo3. Version und älter Typo3 Dokumentation Erklärungen und Anmerkungen zum Umgang mit dem Content Management System Typo3 Version 9.5.4 und älter Inhaltsverzeichnis 1. Online einwählen in Typo3 3 2. Seiten anlegen 3-6 3. Texte

Mehr

Schnellübersichten. ECDL Datenbanken mit Windows 10 und Access 2016

Schnellübersichten. ECDL Datenbanken mit Windows 10 und Access 2016 Schnellübersichten ECDL Datenbanken mit Windows 10 und Access 2016 1 Access kennenlernen 2 2 Access verwenden 3 3 Tabellen 4 4 Informationen abfragen 5 5 Formulare 6 6 Outputs 7 1 Access kennenlernen Datenbank

Mehr

Formulare. Textverarbeitung Professionell 71

Formulare. Textverarbeitung Professionell 71 Formulare Im folgenden Kapitel lernen Sie Formulare zu erstellen und bearbeiten. Sie werden Formulare speichern, schützen und ausfüllen. Weiterhin werden Sie Formulare ausdrucken und die Feldfunktionen

Mehr

Xpert - Europäischer ComputerPass. Konrad Stulle, Andrea Weikert, Tanja Bossert. Datenbankanwendung (mit Access 2010)

Xpert - Europäischer ComputerPass. Konrad Stulle, Andrea Weikert, Tanja Bossert. Datenbankanwendung (mit Access 2010) Xpert - Europäischer ComputerPass Konrad Stulle, Andrea Weikert, Tanja Bossert 1. Ausgabe, 1. Aktualisierung, Juli 2012 Datenbankanwendung (mit Access 2010) XP-ACC2010 3 Xpert - Europäischer ComputerPass

Mehr

Swissmem ebooks ebook Funktionen Software Version 4.x (PC)

Swissmem ebooks ebook Funktionen Software Version 4.x (PC) Swissmem ebooks ebook Funktionen Software Version 4.x (PC) 25.08.2017 Inhalt 6.0.0 ebook Funktionen 2 6.1.0 Übersicht...2 6.2.0 Notizen...3 6.2.1 Einfaches Notizfeld...3 6.2.2 Handschriftliches Notizfeld...6

Mehr

Tiscover CMS 7. Neuerungen im Vergleich zu Tiscover CMS 6

Tiscover CMS 7. Neuerungen im Vergleich zu Tiscover CMS 6 Tiscover CMS 7 Neuerungen im Vergleich zu Tiscover CMS 6 Inhaltsverzeichnis An- und Abmeldung im Tiscover CMS 7... 3 1. Anmeldung... 3 2. Abmeldung... 3 Bereiche der Arbeitsoberfläche von Tiscover CMS

Mehr

Handbuch ECDL 2003 Modul 5: Datenbank Formulare anpassen

Handbuch ECDL 2003 Modul 5: Datenbank Formulare anpassen Handbuch ECDL 2003 Modul 5: Datenbank Formulare anpassen Dateiname: ecdl5_04_02_documentation Speicherdatum: 24.11.2004 ECDL 2003 Modul 5 Datenbank - Formulare anpassen Inhaltsverzeichnis 1 EINLEITUNG...

Mehr

Access 2010. für Windows. Andrea Weikert 1. Ausgabe, 4. Aktualisierung, Juni 2012. Grundlagen für Anwender

Access 2010. für Windows. Andrea Weikert 1. Ausgabe, 4. Aktualisierung, Juni 2012. Grundlagen für Anwender Andrea Weikert 1. Ausgabe, 4. Aktualisierung, Juni 2012 Access 2010 für Windows Grundlagen für Anwender ACC2010 2 Access 2010 - Grundlagen für Anwender 2 Mit Datenbanken arbeiten In diesem Kapitel erfahren

Mehr

Kurzanleitung creator 2.0

Kurzanleitung creator 2.0 Kurzanleitung creator 2.0 Mit dieser Software können Sie an Ihrem Computer Namenskarten für alle im creator enthaltenen Namensschilder-Formate erstellen. Die Vorlagen setzen sich hierfür aus 3 Komponenten

Mehr

Inhaltsverzeichnisse. 1. Überschriften zuweisen. 2. Seitenzahlen einfügen. 3. Einen Seitenwechsel einfügen

Inhaltsverzeichnisse. 1. Überschriften zuweisen. 2. Seitenzahlen einfügen. 3. Einen Seitenwechsel einfügen Inhaltsverzeichnisse 1. Überschriften zuweisen Formatieren Sie die Überschriften mit Hilfe der integrierten Formatvorlagen als Überschrift. Klicken Sie dazu in die Überschrift und dann auf den Drop- Down-Pfeil

Mehr

Datenbank konfigurieren

Datenbank konfigurieren Sie haben eine Datenbank angelegt, jetzt müssen Sie diese noch konfigurieren. Klicken Sie auf den Titel Ihrer neu erstellten Datenbank. Die Spalten Ihrer Datenbank werden als Felder bezeichnet. Sie haben

Mehr

13 Unterprogramme erstellen

13 Unterprogramme erstellen 13 Unterprogramme erstellen»non prendere il lavoro come un nemico, e non farne nemmeno l'unica ragione della tua vita. Betrachte die Arbeit nicht als Feind und mache sie auch nicht zum einzigen Grund deines

Mehr

Anleitung OCAD 12 Multi-Repräsentation

Anleitung OCAD 12 Multi-Repräsentation Anleitung OCAD 12 Multi-Repräsentation Was ist Multi-Repräsentation in OCAD? Multi-Repräsentation bietet die Möglichkeit innerhalb einer OCAD-Karte mehrere Kartenblätter (Repräsentationen) zu verwalten.

Mehr

Kurzanleitung creator 2.0

Kurzanleitung creator 2.0 Kurzanleitung creator 2.0 Mit dem creator 2.0 können Sie an Ihrem Computer Namenskarten für alle bei badgepoint erhältlichen Namensschilder selbst erstellen. Die Vorlagen setzen sich hierfür aus 3 Komponenten

Mehr

Beschreibung: Erforderliches Programm: Excel (97)2000 bis 2007

Beschreibung: Erforderliches Programm: Excel (97)2000 bis 2007 Beschreibung: Erforderliches Programm: Excel (97)2000 bis 2007 Diese Anleitung bezieht sich auf Microsoft Excel Versionen von (97)2000 bis 2003 und mit Spezialverweisen auch auf die Version 2007. Durch

Mehr

IMS-Audit Pro. Kurzanleitung 2 / 14

IMS-Audit Pro. Kurzanleitung 2 / 14 Schneller Einstieg Version 11.2018 2 / 14 Inhaltsverzeichnis Inhaltsverzeichnis 1 Einleitung... 4 1.1 Installation... 4 1.2 Bildschirm Übersichten... 4 2 Stammdaten eintragen... 5 2.1 Mandanten anlegen...

Mehr

Microsoft Office PowerPoint für Windows POW2007F. Autorin: Tina Wegener. Inhaltliches Lektorat: Sabine Spieß

Microsoft Office PowerPoint für Windows POW2007F. Autorin: Tina Wegener. Inhaltliches Lektorat: Sabine Spieß POW2007F Autorin: Tina Wegener Inhaltliches Lektorat: Sabine Spieß 1. Ausgabe, 2. Aktualisierung, September 2011 HERDT-Verlag für Bildungsmedien GmbH, Bodenheim Microsoft Office PowerPoint 2007 für Windows

Mehr

DAS EINSTEIGERSEMINAR

DAS EINSTEIGERSEMINAR DAS EINSTEIGERSEMINAR Microsoft Office Excel 2010 Gudrun Rehn-Göstenmeier LERNEN ÜBEN ANWENDEN Teil I: Lernen L1 Dateiorganisation Bevor wir uns mit den Excel-spezifischen Befehlen und Funktionen befassen

Mehr

Das Anpassen der Stammdatenansichten

Das Anpassen der Stammdatenansichten Das Softwarehaus für Schulen Das Anpassen der Stammdatenansichten (Stand: 07/2010) PEDAV : Das Softwarehaus für Schulen ort : 45359 Essen-Schönebeck str : Schönebecker Straße 1 tel : (0201) 61 64 810 http

Mehr

Novell. GroupWise 2014 effizient einsetzen. Peter Wies. 1. Ausgabe, Juni 2014

Novell. GroupWise 2014 effizient einsetzen. Peter Wies. 1. Ausgabe, Juni 2014 Peter Wies 1. Ausgabe, Juni 2014 Novell GroupWise 2014 effizient einsetzen GW2014 1 Novell GroupWise 2014 effizient einsetzen Menüleiste Navigationsleiste Symbolleisten Kopfleiste Favoritenliste Fensterbereich

Mehr

Powermail Formularbaukasten

Powermail Formularbaukasten Powermail Formularbaukasten Ein Powermail-Formular erstellen Modul "Seite" > gewünschte Seite 1. Klicken Sie auf der Seite das 'Inhalt +'-Symbol an, um ein neues Inhaltselement zu erstellen. 2. Wählen

Mehr

Axel Tüting Version 1.1 zeit für das wesentliche TUTORIAL: SCHNELBAUSTEINE

Axel Tüting Version 1.1 zeit für das wesentliche TUTORIAL: SCHNELBAUSTEINE 2014 www.time4mambo.de Axel Tüting Version 1.1 zeit für das wesentliche TUTORIAL: SCHNELBAUSTEINE Inhalt Schnellbausteine... 3 Eigene Schnellbausteine... 4 Die verschiedenen Steuerelemente... 8 www.time4mambo.de

Mehr

White Paper Wählen-Buttons in Excel

White Paper Wählen-Buttons in Excel White Paper Wählen-Buttons in Excel Seite 2 White Paper... 1 Wählen-Buttons in Excel... 1 Wählen-Button in einer Tabelle... 3 Schritt 1: Excel-Datei öffnen... 3 Schritt 2: Button einbauen... 3 Schritt

Mehr

Handbuch für Redakteure

Handbuch für Redakteure Handbuch für Redakteure Erste Schritte... 1 Artikel erstellen... 2 Artikelinhalt bearbeiten... 3 Artikel bearbeiten... 3 Trennen der Druck- und der Online-Version.. 4 Grunddaten ändern... 5 Weitere Artikeleigenschaften...

Mehr

Neuerungen im Überblick 5 wichtige Punkte. Tipps zur Arbeitserleichterung: Die neue Word- Statuszeile

Neuerungen im Überblick 5 wichtige Punkte. Tipps zur Arbeitserleichterung: Die neue Word- Statuszeile Inhaltsübersicht: Neuerungen im Überblick 5 wichtige Punkte Seite 2 Multifunktionsleiste: Weg mit den Menüs Seite 5 Tipps zur Arbeitserleichterung: Die neue Word- Statuszeile Seite 8 Tipps zur Arbeitserleichterung:

Mehr

Kennen, können, beherrschen lernen was gebraucht wird

Kennen, können, beherrschen lernen was gebraucht wird Mit Tastenkombinationen im Text bewegen So gelangen Sie zum Textanfang / Textende Absatzweise nach oben / unten Bildschirmseite nach oben / unten zum Anfang der vorherigen / nächsten Seite S p S Y / S

Mehr

Dokumentation. Content-Manager

Dokumentation. Content-Manager Dokumentation Content-Manager 1 Funktionsweise... 2 2 Dokumentstruktur... 3 3 Aktivieren und Deaktivieren von Artikeln... 4 4 Artikel Editieren... 5 4.1 Textbearbeitung... 5 4.2 Link einfügen... 4-6 4.3

Mehr

Visual Basic Editor CATIA V5

Visual Basic Editor CATIA V5 Visual Basic Editor CATIA V5 Daniel Frauenrath Allgemein Der Visual Basic Editor ist fester Bestandteil von CATIA V5. Im Gegensatz zum internen Editor für CATScript und CATVbs hat der Visual Basic Editor

Mehr

Inhalt: Brainex Ihre persönliche Wissensdatenbank. Brainex Update Tool. Datenbanken aktualisieren Datenbank hinzufügen/anlegen

Inhalt: Brainex Ihre persönliche Wissensdatenbank. Brainex Update Tool. Datenbanken aktualisieren Datenbank hinzufügen/anlegen Inhalt: Brainex Ihre persönliche Wissensdatenbank Arbeitsbereich Benutzer anlegen Ordner/Eintrag anlegen Einträge bearbeiten Einträge suchen Dateien hinzufügen Änderungsprotokoll Vorlagen Benutzergruppen

Mehr

Inhaltsverzeichnisse

Inhaltsverzeichnisse Inhaltsverzeichnisse Überschriften zuweisen Formatieren Sie die Überschriften mit Hilfe der integrierten Formatvorlagen als Überschrift. Klicken Sie dazu in die jeweilige Überschrift und dann auf der Registerkarte

Mehr

GS-Verein 2014 Buchungslauf rückgängig machen

GS-Verein 2014 Buchungslauf rückgängig machen GS-Verein 2014 Buchungslauf rückgängig machen Impressum Sage GmbH Emil-von-Behring-Straße 8-14 60439 Frankfurt am Main Copyright 2014 Sage GmbH Die Inhalte und Themen in dieser Unterlage wurden mit sehr

Mehr

Funktionen in JavaScript

Funktionen in JavaScript Funktionen in JavaScript Eine Funktion enthält gebündelten Code, der sich in dieser Form wiederverwenden lässt. Mithilfe von Funktionen kann man denselben Code von mehreren Stellen des Programms aus aufrufen.

Mehr

1 Einführung zur Windowsprogrammierung für das Programm Hallo mit Visual Studio.Net

1 Einführung zur Windowsprogrammierung für das Programm Hallo mit Visual Studio.Net Windowsprogrammierung mit dem MFC-AnwendungsAssistenten/ C++ unter Visual Studio.Net Dr. Elfi Thiem 04.01.2006 Visual C++ 1 Einführung zur Windowsprogrammierung für das Programm Hallo mit Visual Studio.Net

Mehr

4 Makros ausprobieren

4 Makros ausprobieren 4 Makros ausprobieren Getreu dem Motto:»Val più la pratica che la grammatica Die Praxis ist mehr wert als die Grammatik«haben Sie jetzt zuerst einmal die Gelegenheit, die Funktionsweise von Makros auszuprobieren.

Mehr

Microsoft Access Arbeiten mit Tabellen. Anja Aue

Microsoft Access Arbeiten mit Tabellen. Anja Aue Microsoft Access Arbeiten mit Tabellen Anja Aue 10.11.16 Tabellen in der Datenblattansicht Ansicht des Anwenders. Eingabe von neuen Daten. Bearbeiten von vorhandenen Informationen. Microsoft Access Einführung

Mehr

bibliothek 1 5 InDesign CS6 Verwenden von Objektbibliotheken

bibliothek 1 5 InDesign CS6 Verwenden von Objektbibliotheken 1 5 Verwenden von Objekten Mit Objekten können Sie häufig verwendete Grafiken, Text und Seiten systematisch ordnen. Außerdem können Sie einer Bibliothek Hilfslinien, Raster, gezeichnete Formen und gruppierte

Mehr

ECDL Information und Kommunikation Kapitel 4

ECDL Information und Kommunikation Kapitel 4 Kapitel 4 Internet Explorer anpassen In diesem Kapitel lernen Sie, wie Sie sich den Internet Explorer an Ihre Bedürfnisse anpassen und die Arbeit mit ihm erleichtern. Der Satz Zeit ist Geld passt hier

Mehr

Handbuch zum VivaWeb-Serienbrief-Programm

Handbuch zum VivaWeb-Serienbrief-Programm Handbuch zum VivaWeb-Serienbrief-Programm In 10 Schritten zum Serienbrief Das folgende Handbuch erläutert Ihnen die Nutzungsmöglichkeiten des ARV Serienbrief-Programms in all seinen Einzelheiten. Dieses

Mehr

Schnell und sicher im Internet arbeiten. mit dem Internet Explorer 8 INT-IE8. Autor: Peter Wies. Inhaltliches Lektorat: Charlotte von Braunschweig

Schnell und sicher im Internet arbeiten. mit dem Internet Explorer 8 INT-IE8. Autor: Peter Wies. Inhaltliches Lektorat: Charlotte von Braunschweig INT-IE8 Autor: Peter Wies Inhaltliches Lektorat: Charlotte von Braunschweig 1. Ausgabe, 2. Aktualisierung, September 2011 HERDT-Verlag für Bildungsmedien GmbH, Bodenheim Internet: www.herdt.com Alle Rechte

Mehr

ACCESS. Steuerelemente per VBA referenzieren FORMULARE MIT VBA PROGRAMMIEREN STEUERELEMENTE PER VBA REFERENZIEREN BASICS

ACCESS. Steuerelemente per VBA referenzieren FORMULARE MIT VBA PROGRAMMIEREN STEUERELEMENTE PER VBA REFERENZIEREN BASICS Steuerelemente per VBA referenzieren Wenn Sie Formulare und Steuerelemente programmieren wollen, müssen Sie wissen, wie Sie diese referenzieren. Nicht immer geschieht dies vom Klassenmodul des Formulars

Mehr

RIBBON-ADMIN RIBBONS ENTWICKELN MIT ACCESS ANDRÉ MINHORST

RIBBON-ADMIN RIBBONS ENTWICKELN MIT ACCESS ANDRÉ MINHORST RIBBON-ADMIN RIBBONS ENTWICKELN MIT ACCESS 2007-2016 ANDRÉ MINHORST André Minhorst Ribbon-Admin Ribbons entwickeln mit Access 2007 bis 2016 André Minhorst Ribbon-Admin ISBN 978-3-944216-05-8 2015 André

Mehr

GS-Verein Buchungslauf rückgängig machen

GS-Verein Buchungslauf rückgängig machen GS-Verein Buchungslauf rückgängig machen Copyright 2015 Sage GmbH Die Inhalte und Themen in dieser Unterlage wurden mit sehr großer Sorgfalt ausgewählt, erstellt und getestet. Fehlerfreiheit können wir

Mehr

Michael Kolberg. einfach klipp & klar. Microsofft* Press

Michael Kolberg. einfach klipp & klar. Microsofft* Press Michael Kolberg einfach klipp & klar Microsofft* Press III Vorwort 11 Die CD-ROM zum Buch 21 Die AutoPlay-Funktion der CD-ROM 22 Installation der Testfragen und des Ebooks 23 Installation des Computer-Lexikons

Mehr

Swissmem ebooks ebook Funktionen Software Version 4.x (PC)

Swissmem ebooks ebook Funktionen Software Version 4.x (PC) Swissmem ebooks ebook Funktionen Software Version 4.x (PC) 29.05.2017 Inhalt 6.0.0 ebook Funktionen 2 6.1.0 Übersicht...2 6.2.0 Notizen...3 6.2.1 Einfaches Notizfeld...3 6.2.2 Handschriftliches Notizfeld...6

Mehr

Kurzanleitung. Zitiertil-Creator. Dokumentvorlagen Dokumente Formatvorlagen Format Zeichen Format Absatz

Kurzanleitung. Zitiertil-Creator. Dokumentvorlagen Dokumente Formatvorlagen Format Zeichen Format Absatz Dokumentvorlagen Dokumente Formatvorlagen Format Zeichen Format Absatz Datei Neu... Datei öffnen Datei schließen Beenden Suchen Suchen & Ersetzen Verknüpfungen Optionen Einfügen Inhalte einfügen Format

Mehr

Daten verknüpfen und einbetten

Daten verknüpfen und einbetten Daten verknüpfen und einbetten Office 2010 - Aufbau ZID/Dagmar Serb V.02/Okt 2018 VERKNÜPFEN & EINBETTEN VON DATEN... 2 VERKNÜPFEN... 2 Bearbeiten verknüpfter Informationen... 3 EINBETTEN... 3 Bearbeiten

Mehr

Seiten und Navigationspunkte

Seiten und Navigationspunkte Seiten und Navigationspunkte Legen Sie neue Seiten und Navigationspunkte an. Um Sie mit dem Anlegen von Seiten und Navigationspunkten vertraut zu machen, legen wir zunächst einen neuen Navigationspunkt

Mehr

Kopf-/Fußzeilen und Seitenzahlen

Kopf-/Fußzeilen und Seitenzahlen Dokumentvorlagen Dokumente Formatvorlagen Format Zeichen Format Absatz Datei Neu... Datei öffnen Datei schließen Beenden Suchen Suchen & Ersetzen Verknüpfungen Optionen Einfügen Inhalte einfügen Format

Mehr

Wichtige Standardaktionen durchführen

Wichtige Standardaktionen durchführen Anhang A Wichtige Standardaktionen durchführen Die Themen dieses Anhangs So geht es mit Access 2013 So geht es mit Access 2010 So geht es mit Access 2007 So geht es mit Access 2003 In diesem Anhang wird

Mehr

VBA (Visual Basic for Application) Einführung

VBA (Visual Basic for Application) Einführung VBA (Visual Basic for Application) Einführung Bücher Doberenz & Kowalski: Microsoft Office Access 2003 Programmierung; Microsoft Press André Minhorst: Das Access 2003-Entwicklerbuch; Addision-Wesley Albrecht

Mehr