Sie möchten den Anwendern Ihrer Datenbank die Möglichkeit anbieten, neben den eigentlichen Adressdaten auch gleich dazugehörige Geoinformationen einzusehen Dazu eignen sich sicher die Dienste Google-Maps oder Bing-Maps. Ein Webbrowser-Steuerelement im Formular könnte als Host für jene dienen. Doch der Weg ist steiniger, als erwartet! Wie Sie die auftretenden Probleme lösen können, beschreibt dieser Beitrag.
Google Maps
Wenn Sie nach einem Ort auf der Startseite von Google Maps (https://www.google.de/maps) recherchieren, so geben Sie in das Suchfeld links oben die gewünschte Adresse ein. Google verarbeitet die Anfrage mit seinen Geo Location Services und zeigt das Resultat im Browser an. Dabei ändert sich die Adresszeile entsprechend. Aus der Anfrage Duisburg Borkhofer Str. wird dann eine URL, die sich aus mehreren Teilen unterschiedlicher Bedeutung zusammensetzt:
https://www.google.de/maps/place /Borkhofer+Str.,+47137+Duisburg /@51.4634026,6.7777953,17z /data=!3m1!4b1!4m5!3m4!1 s0x47b8bfbe53cca829:0xb28d5de5549581d!8m2!3d51.4634026!4d6.7799893
Der Service hat zunächst ermittelt, dass es sich offenbar um den Ort Duisburg in Deutschland handeln soll, der die Postleitzahl 47137 besitzt. Die Angabe des Landes fehlt. Google geht davon aus, dass der Ort sich mit großer Wahrscheinlichkeit in dem Land befindet, von dem aus die Anfrage gesendet wurde. Dies geht etwa aus der übermittelten IP hervor. Der Straßenname vervollständigt diesen Teil, in dem natürlich keine Leerzeichen vorkommen dürfen. Sie sind durch Pluszeichen ersetzt.
Doch damit nicht genug! Hinter einem Slash befinden sich, durch ein @ symbolisiert, auch gleich die Geokoordinaten des gefundenen Orts. Längen- und Breitengrad kommen als Fließkommazahlen daher, wobei als Dezimaltrennzeichen der Punkt dient. Kommas trennen hingegen die einzelnen Werte. Was jedoch hat es mit dem dritten Parameter dieses Teils auf sich, dem 17z
Versuch und Irrtum machen schlau, denn Google hat den Aufbau dieser Parameter nicht wirklich dokumentiert. Etliche Leute, den Autor eingeschlossen, haben teilweise herausgefunden, was sie bedeuten. Am weitesten kam wohl M. Sticklesville mit der Aufstellung in seinem Blog (https://mstickles.wordpress.com/2015/06/12/gmaps-urls-options). Der dritte Parameter gibt nämlich den Zoomgrad der Kartenansicht wieder.
Sie können den Wert in der Adresszeile des Browsers einfach von Hand ändern, etwa in 11z. Der Ort wird nun von weiter oben betrachtet. Bei 4z ist allerdings Schluss. Damit wird fast die Erdhalbkugel angezeigt. Der höchste Zoomfaktor beträgt 21z. Bei allen anderen Werten außerhalb dieses Bereichs kommt es nicht etwa zu einer Fehlermeldung, sondern Google korrigiert den Faktor automatisch durch Auf- oder Abrunden auf den nächsten Wert.
Im dritten Teil der URL findet sich ein ominöser data-Parameter. Zahlen, Buchstaben und Ausrufezeichen wechseln sich scheinbar willkürlich ab. Tatsächlich haben sie eine Bedeutung, der Sie auf die Schliche kommen, wenn Sie etwa die Ansicht der Karte auf Satellit einstellen, oder den Layer für das Verkehrsaufkommen einschalten. Bis auf diesen data-Parameter bleiben alle anderen Teile der URL dabei konstant. Der data-Parameter bestimmt das Layout der Karte. Wenn Sie mehr über seinen Aufbau erfahren möchten, so kundschaften Sie den angegebenen Blog von Sticklesville aus. Wir kommen auf den data-String aber auch später noch zu sprechen.
Am Ende der URL schließt sich ein länglicher String an, der wohl die ID der Suchanfrage enthält. Google cachet diesen Wert auch in einem Cookie, um Rechenzeit einzusparen. Er ist für die Suchanfrage bedeutungslos.
Sie können die URL nun dergestalt modifizieren, dass nur noch die wichtigsten Elemente enthalten sind. Das sähe dann so aus:
https://www.google.de/maps/place /@51.4634026,6.7777953,17z /data=!3m1!4b1!4m5!3m4!1
Falls die Koordinaten dieselben sind, wie zuvor, so macht Google sofort wieder die ursprüngliche Version daraus, weil es sie aus dem Cookie ausliest. Bei geänderten Koordinaten hingegen bleibt der String meist erhalten. Allerdings ändert sich nun auch die Ansicht komplett: Das Control Panel links verschwindet und der Orts-Marker ist ebenfalls nicht mehr da.
Man kann an dieser Stelle zusammenfassen, dass sich die Google-Maps-Anfragen in einer URL unterbringen und sich dabei sowohl Zoomfaktor, wie auch Gestalt des Kartengebnisses, beeinflussen lassen. Was will man mehr Damit ist der erste Teil der Aufgabe in Angriff genommen, eine Google-Maps-Karte in ein Formular zu bringen.
Das Webbrowser-Steuerelement
Als Host für die Maps-Seiten soll ein Webbrowser-Steuerelement dienen, das Microsoft seit der Access-Version 2007 in den Fundus der Steuerelemente integriert hat. Es unterscheidet sich in nichts vom gleichnamigen ActiveX-Steuerelement, das Windows immer mit sich führt. Lediglich die Möglichkeit, es direkt an ein Datenfeld einer Tabelle oder Abfrage zu binden, welches die zu navigierende URL enthält, ist ein Bonus-Feature.
Zum Test setzen Sie das Webbrowser-Steuerelement in ein neues Formular ein, wie dies im Formular frmMaps der Beispieldatenbank GoogleMaps.accdb geschah (siehe Bild 1). Außer dem Webbrowser Control enthält es nur ein Textfeld rechts oben, welches den Inhalt des URL-Felds des aktuellen Datensatzes anzeigt. Die Tabelle tblURLs dient als Datenherkunft des Formulars. Sie weist neben der Autowert-ID nur das Feld URL aus und ein optionales Feld Beschreibung, welches hier nicht zum Einsatz kommt. An URL sind sowohl das Textfeld, wie auch das Webbrowser-Steuerelement gebunden.
Bild 1: Im Testformular zur Tabelle tblURLs ist zentral ein Webbrowser-Steuerelement untergebracht
Nach dem ersten Start präsentiert das Formular eine leere Fläche, da die erste URL den Inhalt about:blank hat. Würde die URL nämlich aus einem Leer-String bestehen, so käme es zu einer Fehleranzeige im Browser wegen ungültiger Adresse. Der zweite Datensatz hat den folgenden Inhalt:
https://www.google.de/maps/
Sie erwarten, dass nun Google-Maps im Browser angezeigt wird Stattdessen konfrontiert uns Google mit der Meldung aus Bild 2. Aus irgendeinem Grund ist es mit dem Webbrowser-Steuerelement nicht zufrieden und verwehrt die Navigation zu Maps. Doch dieses verwendet intern ja lediglich den im System installierten Internet Explorer, der möglicherweise in einer aktuellen Version 11 vorhanden ist (Edge bleibt außen vor). Warum dann diese Meldung
Bild 2: Statt Maps zeigt Google trotz korrekter URL zunächst eine Inkompatibilitätsmeldung an
Webbrowser Control: Limitierungen und deren Aufhebung
Aus Sicherheitsgründen grenzt Microsoft die Möglichkeiten im Webbrowser Controls stark ein. Das gilt übrigens für das Access-Steuerelement gleichermaßen, wie für das ActiveX-Steuerelement. Die wichtigste änderung besteht darin, dass das Control grundsätzlich die Version IE7 nach außen meldet.
Genau damit ist Google nicht einverstanden, da die aktuelle Version von Maps einen neueren Browser für korrekte Funktion erwartet. Zudem erlaubt das Control nur eine eingeschränkte Ausführung von JavaScript.
Das Ende der Fahnenstange ist damit zum Glück noch nicht erreicht. Tatsächlich können Sie das Verhalten des Webbrowser Controls durch Einstellungen in der Registry ändern! Und dafür sind noch nicht einmal administrative Rechte erforderlich, da sich der entsprechende Zweig im Benutzerteil HKEY_CURRENT_USER befindet:
HKEY_CURRENT_USER\Software\Microsoft \Internet Explorer\Main\FeatureControl
Unter diesem Zweig befinden sich noch weitere Schlüssel, die verschiedene Funktionen des Controls steuern. Der wichtigste ist dabei der Folgende:
FEATURE_BROWSER_EMULATION
Unter diesem Schlüssel tragen Sie einen DWORD-Wert ein, der den Namen msaccess.exe trägt. Als Wert verwenden Sie 11001. Dieser nämlich führt dazu, dass Windows beim Start einer Access-Instanz und dem öffnen eines Webbrowser-Steuerelements hier nachschaut, ob ein Eintrag zu finden ist. Falls Prozessname und Eintrag zueinander passen, so spendiert Windows dem zugrundeliegenden Internet Explorer die angegebenen Version. Beim Wert 11001 wäre das die Version 11. Funktionieren würde Google Maps auch noch mit der Version 9, die dem Registry-Wert 9999 entspräche. Doch warum zu einer älteren Version greifen, wenn es eine neuere gibt Voraussetzung ist natürlich, dass auch tatsächlich der Internet Explorer 11 installiert ist. Passen Sie den Wert an die installierte Version an. Im MSDN gibt es eine Seite, die erschöpfender darüber Auskunft gibt (https://msdn.microsoft.com/en-us/library/ee330730(v=vs.85).aspx). Edge kann das Webbrowser Control übrigens nicht hosten. Es verwendet auch unter Windows 10 den Kompatibilitätsmodus des IE11.
Bedenken brauchen Sie bei der Modifikation der Registry hier nicht zu haben. Zum einen sind Einträge unter dem Benutzerzweig recht unverdächtig, zum anderen betrifft die änderung nur ausschließlich Access-Prozesse. übrigens bedienen sich auch andere Entwickler dieser Möglichkeit. Wir fanden in diesem Zweig auch Einträge prominenter Anwendungen, wie Visual Studio, RapidPHP oder Starmoney.
Da die änderungen an der Registry keine Administratorrechte erfordern, können Sie auch von Access aus über VBA getätigt werden. Dies geschieht auch in der Beispieldatenbank. Das Intro-Formular ruft beim Klick auf den grünen Button die Funktion SetWebControlAsIE9 des Moduls mdlWebcontrol auf. Diese liest zunächst aus der Registry aus, ob der Wert für die Browser Emulation bereits gesetzt ist. Falls nicht, so holt sie das umgehend nach. Allerdings wirkt sich die änderung noch nicht unmittelbar aus. Erst nach einem Neustart von Access gibt das Webbrowser Control die Version IE11 aus. Deshalb empfiehlt die Routine in diesem Fall auch gleich den Neustart (Bild 3) und bewerkstelligt diesen auch noch. Schauen wir uns diese Routine (ersterer Teil in Listing 1) einmal genauer an.
Bild 3: Automatische Aufforderung zum Neustart von Access über die Prozedur SetWebControlAsIE9 und eine Messagebox
Da Access und VBA keine Methoden zum Auslesen oder Schreiben der Registry haben, benötigen wir den Verweis auf eine externe Komponente. Für unseren einfachen Zweck gelingt das am effizientesten mit der Bibliothek Windows Script Host Object Model, die im Objektkatalog nachher den Namen IWshRuntimeLibrary trägt und auf der Datei wshom.ocx beruht. Diese Bibliothek ist Bestandteil von Windows und kann deshalb bedenkenlos zum Einsatz kommen. Ihre Klasse WshShell enthält Methoden zur Modifikation der Registry.
Die Objektvariable oShell nimmt eine Instanz dieser Klasse per New auf. Der Zweig der Registry ist in der String-Variablen sKey gespeichert, und zwar inklusive des Wertnamens. Die Funktion RegRead auf den Schlüssel gibt dann den Wert als Variant in der Variablen vKey zurück. Da es zum Fehler kommt, wenn der Registry-Schlüssel nicht existiert, ist der Aufruf der Funktion in eine Fehlerbehandlung über On Error Resume Next eingebettet. Der Fehlerwert beträgt in diesem Fall -2147024894, der in der folgenden Bedingung dazu führt, dass der Schlüsselwert über die Methode RegWrite neu geschrieben wird. Die Klasse WshShell erlaubt also einen komfortablen Zugriff auf die Registry mit sehr wenig Aufwand.
Existiert der Schlüsselwert zwar, so ist zu prüfen, ob er dem gewünschten entspricht. Das übernehmen die folgenden Zeilen. Weicht der Inhalt von vKey von 11001 ab, so erfolgt abermals das Schreiben in den Schlüssel. Ansonsten passiert außer der informativen Ausgabe im VBA-Direktfenster weiter nichts. Wichtig aber ist noch eine Kleinigkeit: Wurde in die Registry geschrieben, so erhält die Bool-Variable bNewStart den Wert True. Dieser Wert wird am Ende der Routine ausgewertet:
If bNewStart Then RestartAccess End If
RestartAccess ist eine weitere Prozedur im Modul, die den Neustart von Access veranlasst (siehe Listing 2). Wird die Nachfrage der MsgBox bestätigt, so wird wieder das Shell-Objekt bemüht. Seine Methode Exec entspricht ungefähr der Shell-Methode von VBA, ist aber etwas einfacher zu handhaben. In sExec wird ein String zusammengebastelt, der eine neue Instanz von Access startet, wobei über SysCmd erfahren wird, wo sich die msaccess.exe befindet. Als Parameter für den Prozess wird der Name der aktuellen Datenbank (CurrentDb.Name) übergeben. In der Folge startet eine zusätzliche Instanz der Datenbank. Die aktuelle Instanz wird aber über Quit geschlossen. Das passiert so schnell, dass es kaum wahrnehmbar ist. Im Ergebnis befinden wir uns in der Datenbank mit dem neu aktiviertem Webbrowser Control.
Sub RestartAccess() If MsgBox("Die Datenbank und Access müssen neu gestartet werden," & vbCrLf & _ "weil die Einstellungen für das WebControl geändert wurden." & vbCrLf & _ "Jetzt neu starten", vbQuestion Or vbYesNo, "Bestätigen:") = vbYes Then Dim sExec As String Dim oShell As New WshShell sExec = Chr$(34) & SysCmd(acSysCmdAccessDir) & "msaccess.exe" & Chr$(34) & _ " " & Chr$(34) & CurrentDb.Name & Chr$(34) oShell.Exec sExec Application.Quit acQuitSaveNone End If End Sub
Listing 2: Die Routine RestartAccess übernimmt gegebenenfalls den Neustart von Access
Die Routine SetWebControlAsIE9 enthält noch einen zweiten Teil, der drei weitere Einstellungen für das Control vornimmt. Das passiert analog zum ersten Teil. Der Unterschied besteht nur darin, dass der Code-Block nicht dreimal wiederholt wird, sondern eine Schleife auf das Array vRegElement die einzelnen Registry-Schlüssel anspricht:
vRegElement = Array("FEATURE_BLOCK_LMZ_SCRIPT",
"FEATURE_BLOCK_LMZ_OBJECT",
"FEATURE_BLOCK_LMZ_IMG")
For i= 0 To 2
sKey = "HKEY_CURRENT_USER\Software\Microsoft\" & _
"Internet Explorer\Main\FeatureControl\" & _
vRegElement(i) & "\msaccess.exe"
...
oShell.RegWrite sKey, 1, "REG_DWORD"
...
Next i
Falls es noch nicht aufgefallen sein sollte: RegWrite übernimmt in einem Rutsch die Anlage des Registry-Schlüssels und des Werts! Einfacher geht“s nicht! Bei allen alternativen Methoden muss das separat erfolgen.
Neben den Einstellungen, die so über die Registry vorgenommen werden können, gibt es noch einige weitere, die die weitgehende übereinstimmung des Webbrowser-Steuerelements mit dem dem Internet Explorer herstellen können. Dazu jedoch muss eine Windows-API-Funktion bemüht werden, die sich CoInternetSetFeatureEnabled nennt. Das Setzen dreier neuer Eigenschaften ist in die Prozedur ChangeWebControlFeature ausgelagert (Listing 3).
Public Declare Function CoInternetSetFeatureEnabled Lib "urlmon.dll" _ (ByVal FeatureEntry As eINTERNETFEATURELIST, ByVal Flags As eFEATURESetting, ByVal bEnable As Long) As Long Public Declare Function CoInternetIsFeatureEnabled Lib "urlmon.dll" _ (ByVal FeatureEntry As eINTERNETFEATURELIST, ByVal Flags As eFEATURESetting) As Long Sub ChangeWebControlFeature() Dim ret As Long Dim lret As Long If CoInternetIsFeatureEnabled(FEATURE_BEHAVIORS, SET_FEATURE_ON_PROCESS) <> 0 Then ret = CoInternetSetFeatureEnabled(FEATURE_BEHAVIORS, SET_FEATURE_ON_PROCESS, 1) lret = ret End If If CoInternetIsFeatureEnabled(FEATURE_ZONE_ELEVATION, SET_FEATURE_ON_PROCESS) <> 0 Then ret = CoInternetSetFeatureEnabled(FEATURE_ZONE_ELEVATION, SET_FEATURE_ON_PROCESS, 1) lret = lret + ret End If If CoInternetIsFeatureEnabled(FEATURE_RESTRICT_ACTIVEXINSTALL, SET_FEATURE_ON_PROCESS) <> 0 Then ret = CoInternetSetFeatureEnabled(FEATURE_RESTRICT_ACTIVEXINSTALL, SET_FEATURE_ON_PROCESS, 0) lret = lret + ret End If Debug.Print IIf(lret = 0, "Features adjusted", "Feature adjustment failed") End Sub
Listing 3: ChangeWebcontrolFeature setzt neue Eigenschaften für das Webbrowser Control per API
Die Konstanten für Steueranweisungen, wie FEATURE_BEHAVIOURS, stehen im Kopf des Moduls. Sie bestimmen das anzusprechende Feature des Webbrowser Controls. Für alle drei Aufrufe ist die Anweisung SET_FEATURE_ON_PROCESS angegeben. Das besagt, dass die Eigenschaft nur auf den laufenden Prozess, also die Access-Instanz, wirken soll. Dabei fragt die zweite API-Funktion CoInternetIsFeatureEnabled jeweils zuerst ab, ob die Eigenschaft schon gesetzt ist. Die Prozedur ChangeWebControlFeature muss, da sie nur den Prozess betrifft, immer beim Start der Datenbank ausgeführt werden. Auf die Bedeutung der einzelnen Features möchten wir an dieser Stelle nicht weiter eingehen. In der MSDN (https://msdn.microsoft.com/en-us/library/ee330720(v=vs.85).aspx) sind die Features näher erläutert.
Zurück zu unserem Testformular. Nach dem Durchlaufen der Anpassungsroutinen und Neustart von Access rufen Sie frmMaps erneut auf. Und nun präsentiert sich beim zweiten Datensatz tatsächlich die Startseite von Google Maps (s. Bild 4). Alles ok! Der dritte Datensatz verweist direkt auf eine zuvor in Maps im Browser ermittelte Ortsadresse:
Bild 4: Google Maps nach der Anpassung des Webbrowser-Steuerlements über Registry und API
https://www.google.com/maps/place/Berlin,+Deutschland /@52.5075419,13.4251364,11z/data=!4m2!3m1!
Auch diese wird nun korrekt angesteuert (s. Bild 5). Da es sich um keine genaue Adresse handelt, sondern eine Stadt, wird diese in der Karte als Gebiet umrahmt. Störend ist bei dieser Formulargröße allerdings, dass das Control Panel von Google Maps mehr Platz einnimmt, als der Kartenausschnitt. Leider lässt sich dieses Panel nach allem bisherigen Wissen nicht über Steuerparameter ausblenden. Da bleibt nur der Klick auf den Schließen-Button des Panels, der dann allerdings auch die Ortsmarkierung in der Karte entfernt…
Bild 5: Google Maps zeigt nach Ansteuerung einer Such-URL das richtige Ergebnis mit Steuer-Panel
Bei genauerem Hinsehen finden Sie in der Karte einen kleinen Blitz links neben den +/-Elementen zu Zoomen der Karte eingebaut. Der fehlt beim Aufruf der Seite in einem Browser. Ein Klick auf ihn fördert ein Popup zutage, welches die Meldung in Bild 6 enthält. Offenbar hat Google nun doch ermittelt, dass hier nicht mit einem vollwertigen Browser gearbeitet wird. Das liegt daran, dass bestimmte externe Objekte des Web-Dokuments hier nicht über JavaScript ansprechbar sind. Wir konnten im Lite-Modus jedoch keine wesentlichen Einschränkungen feststellen. Alle Ansichten, inklusive Street View, konnten anstandslos eingeschaltet werden und verhielten sich erwartungsgemäß.
Bild 6: Trotz Webbrowser-Control-Anpassungen schaltet Google-Maps dennoch in den sogenannten Lite-Modus
Adressen im Webbrowser-Steuerelement automatisch über Formular ansteuern
Mit den bisherigen Erkenntnissen im Gepäck kann es an die Realisierung einer Beispiellösung gehen. Ziel ist es, ein einfaches Adressformular zu Kunden der Datenbank um eine Kartenansicht zu bereichern. Hierfür kommt das Datenmodell in Bild 7 zum Einsatz. In der Tabelle tblKunden sind Adressen gespeichert.
Bild 7: Beziehungs-Layout der Beispieldatenbank (Kundenadressen)
tblAnreden und tblOrte sind verknüpfte Detailtabellen. Sie sind auch im Entwurf der Kundentabelle (s. Bild 8) in den Feldern IDAnrede und IDOrt als Nachschlagetabellen für die hier verwendeten Kombinationsfelder eingetragen. tblKunden zeigt im Entwurf neben den üblichen Adressfeldern noch die zwei zusätzlichen Textfelder MapsURL und BingURL. In sie sollen später die über Google- oder Bing-Maps im Webbrowser-Steuerelement ermittelten Such-URLs abgelegt werden. Bild 9 demonstriert einen Ausschnitt der Daten in der Datenblattansicht. Beide URL-Felder sind mit dem Vorgabewert about:blank belegt, der dann, wie im unteren Bereich zu sehen, durch die korrekten Maps-Adressen ersetzt wird. übrigens handelt es sich hier zwar um gefakte Adressen, bei denen jedoch die Kombination Straße, PLZ und Ort korrekt sind, damit die Geo Services etwas Sinnvolles finden können.
Bild 8: Der Entwurf der Tabelle tblKunden zeigt die URL-Felder
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo