Bing Map Apps

Schreiben einer Echtzeit-Transportanwendung mit dem Bing Map App-SDK

Luan Nguyen

Downloaden des Codebeispiels

Microsoft kündigte während der 2010 Tech • Ed-Konferenz im Juni, die Verfügbarkeit von das kostenlose Bing Map App SDK ermöglicht Entwicklern das Schreiben von Map-zentrierte Anwendungen über die Karten Bing Erforschen Website unter bing.com/maps/explore/.

Dies stellt eine Vielzahl von Möglichkeiten für Organisationen, Unternehmen oder Hobbyprogrammierer, um Ihre eigenen Erfahrungen Zuordnung innerhalb Bing Karten zu erstellen. Unternehmen können Anwendungen, die Ihre Produkte angekündigt oder zur Ergänzung des Ihre online-Dienste schreiben. Bestparking.com-entwickelt beispielsweise eine “ Parkplätze Finder ”-Anwendung finden alle Parkplätze Einrichtungen in Ihrer Stadt mit Ihrer Datenbank arbeiten kann.

Zum Anzeigen eine Karte Anwendung aussieht gehen Sie zur Bing Maps Site durchsuchen, und klicken Sie auf MAP-APPS in der Nähe der unteren linken Ecke der Seite.Katalog Anwendungen zuordnen wird geöffnet und zeigt die verfügbaren Anwendungen.Viele Anwendungen, die in der Sammlung bereits vorhanden sind, und er weiter wächst.

In diesem Artikel wird beschrieben, was benötigt wird, um eine Zuordnung-Anwendung mit dem Bing Map App-SDK schreiben.Ich werde Sie durch die Entwicklung einer Beispielanwendung durchlaufen, die in Echtzeit Busdaten Ankunft um King County, Washington Zustand anzeigt.Diese Anwendung wird von beliebten Anwendung ein Bus Artikel inspiriert.

Das endgültige Produkt

Bevor ich in Code zu genau let’s sehen Sie sich die endgültigen Zuordnung Anwendung aussehen. Wenn aktiviert, wenn die Zuordnung in King County Bereich vergrößert wird, meine Anwendung Zuordnung wird im linken Bereich Liste der anzeigen alle Bus Stopps innerhalb der Zuordnung Viewport (siehe Abbildung 1 ).

image: The OBA App Shows Bus Information on the Left Panel and Pushpins on the Map

Abbildung 1 die OBA-Anwendung zeigt Bus Informationen zur links Systemsteuerung und Pins auf der Karte

Jeder Bus Stop enthält Informationen über alle Busse, die durch den Informationsfluss für den wieder. Klicken Sie auf den Hyperlink “ eingetroffen times ” für jede beenden wird der zweite Bereich geöffnet dem bevorstehenden Ankunft Zeiten angezeigt (siehe Abbildung 2 ).

image: Bus Arrival Times for a Particular Bus Stop

Abbildung 2 Bus eingetroffen-Zeiten für einen bestimmten Bus Stop

Die Anwendung platziert auch einen ansprechenden PIN (markiert mit dem Buchstaben B) an jeden Bus Stop auf der Karte. Sie können interagieren mit den Pins, ein Popupfenster zu aktivieren, in dem häufig verwendete Befehle für einen bestimmten Bus wieder wie abrufen gesteuert oder schrittweise Anweisungen zum oder (siehe Abbildung 3 ) aus aufgerufen werden können.

image: Clicking on a Pushpin Shows a Pop-Up UI with Extra Information

Abbildung 3 Klicken Sie auf einen PIN zeigt eine Popup-Benutzeroberfläche mit zusätzlicher Informationen

Bing Map App SDK downloaden

Bevor Sie Ihre erste Map-Anwendung schreiben, müssen Sie im Bing Map-App-SDK unter connect.microsoft.com/bingmapapps-installieren. Das SDK stellt die Verweisassemblys, ein Beispielprojekt und offline-Dokumentation. Darüber hinaus Da Silverlight 4 im SDK basiert, müssen Sie auch den Silverlight-4-Tools für Visual Studio 2010 unter silverlight.net/getstarted/-installieren.

Zuordnen von App-Grundlagen

Entdecken Sie die Karten Bing Erweiterbarkeit als eine der obersten Prioritäten Website erstellt wurde.Bing Maps-Team wollte, damit Entwickler auf der ganzen Welt problemlos Hinzufügen von Funktionalität, die nützlich finden würden und möglicherweise für andere Benutzer nützlich sein würde.In diesem Sinne ist eine Zuordnung Anwendung eine konkrete-Funktion, die von der Baustein Dienste von den Kern der Bing Karten verwenden.Bei näherer Betrachtung der Standard-Bing Maps-Website die Wegbeschreibungen werden Unternehmen suchen und Speicherort Suchfeatures alle aus derselben Bausteine erstellt.

Bing Maps-Team beschlossen, mit diesem Ziel Denken Sie daran ein erweiterbares Framework unterstützen das Konzept der Zuordnung von Anwendungen zu erstellen.Wenn über das Framework geschrieben, nutzen Zuordnung Anwendungen einen Dependency Injection-Stil Ansatz für den Zugriff auf die Funktionalität, die in die verwaltete Erweiterbarkeit Framework (MEF) ähnelt.

Den Kern des Frameworks ist Sie das Konzept des Plug-Ins.Ein plug-in ist eine Einheit der Erweiterbarkeit, die Entwicklern ermöglicht, verwaltbar und entkoppelte Features hinzufügen.Er kann als das Äquivalent eines Teils zusammenstellbar MEF-Terminologie betrachtet werden.Deklariert ein plug-in über Attribute die Importe sowie Exporte erforderlich, die freigegeben werden sollen.Zur Laufzeit wird das Framework Beheben von Abhängigkeiten zwischen-Plug-ins und die Importe mit die Exporte, die den gleichen Vertrag überein.

Um eine Map-Anwendung schreiben, erstellen Sie eine Silverlight-4-Klassenbibliothek, die genau eine Unterklasse der Basisklasse-Plug-In enthält.Um sinnvoll zu tun, wird Ihr plug-in wahrscheinlich eine Reihe von integrierten Verträge importieren, mit denen Sie verschiedene Kerndienste Zugriff auf.Die SDK-Dokumentation enthält einen Abschnitt mit allen integrierten Verträge, die vom Framework exportiert.

Plug-in-Klasse und alle integrierten Vertrag Typen verweisen, müssen Ihr Projekt aus dem SDK bereitgestellten Assemblys verweisen.Abbildung 4 beschreibt kurz die vier Assemblys.

Abbildung 4 Verweisassemblys für ein MAP-Anwendung-Projekt

Assemblyname Beschreibung
Microsoft.Maps.Plugins.dll Enthält die Basisklasse-Plug-in und verwandten Klassen des Import/Export-Attribut.
Microsoft.Maps.Core.dll Enthält alle Karten Bing bietet Vertrag-Typen.
Microsoft.Maps.MapControl.Types.dll Enthält die Typen, die für die Arbeit mit dem Map-Steuerelement erforderlich.
Microsoft.Maps.Extended.dll Enthält die Typen, die für die Interaktion mit dem StreetSide Map-Modus.

Zugriff auf die einem Bus Abwesend API

Ankunft in Echtzeit Busdaten erhalten, werde ich vorgenommen der öffentlich verfügbaren ein Bus Abwesend REST API (einfach genannt Oba API für den Rest dieses Artikels) verwenden. ode.google.com/p/onebusaway/wiki/OneBusAwayRestApi-finden Sie Details über die API Oba. (Beachten Sie, dass die API freien noncommercial verwendet, aber Sie sind erforderlich, um eine Anwendungstaste zu registrieren, bevor Sie darauf zugreifen können. Im herunterladbaren Code für diesen Artikel wurden mir, zugeordneten Schlüssel entfernt, wenn Sie kompilieren und führen die Anwendung selbst ausführen möchten, Sie ihn durch einen eigenen Schlüssel ersetzen müssen.)

Wenn eine Silverlight-Anwendung versucht, einen Webdienst in einer anderen Domäne zuzugreifen, erfordert die Silverlight-Laufzeit, dass der Dienst explizit in entscheiden muss, um domänenübergreifenden Zugriff zu ermöglichen. Ein Dienst gibt seine Zustimmung durch Platzieren eines clientaccesspolicy.xml oder eine crossdomain.xml im Stamm der Domäne, in der Dienst gehostet wird. Weitere Details zu den Schemas dieser zwei Dateien msdn.microsoft.com/library/cc197955%28VS.95%29-dokumentiert. Glücklicherweise bietet die Oba-API die Datei crossdomain.xml, die meine Anwendung Zuordnung in Silverlight-Code aufrufen kann.

In der Lösung zum Download sehen Sie zwei Bibliotheksprojekte ObaApp und ObaLib. ObaApp ist das Hauptprojekt, das die Zuordnung app-Plug-In enthält, und Sie ObaLib verweist. ObaLib ist ein weiteres Klassenbibliotheks-Projekt, das alle Hilfsklassen zur Kommunikation mit der API Oba kapselt. Ich war es eine separate Bibliothek so, dass ich problemlos es in anderen Projekten wiederverwendet werden kann ich gegebenenfalls. Ich auf die Details jeder Klasse in dieser Bibliothek wechseln wird nicht, aber Sie sind aufgefordert, Weitere Informationen zu den Klassen, die es den Quellcode untersuchen.

Die wichtigste Klasse kennen, ist die ObaSearchHelper-Klasse, die bietet praktische Methoden und Ereignisse Abfragen an die API Oba vornehmen:

public sealed class ObaSearchHelper
{
  public void SearchArrivalTimesForStop(string stopId);
  public event EventHandler<BusTripsEventArgs>
    SearchArrivalTimesForStopCompleted;


  public void SearchStopsByLocation(double latitude, double longitude, 
    double radius, int maxResultCount);
  public event EventHandler<BusStopsEventArgs> 
    SearchStopsByLocationCompleted;

  // more method/event pairs
  // ...
}

Es ist leicht zu erkennen, das in dieser Klasse verwendete Muster.Für jeden REST-Endpunkt der Oba-API ist eine Methode, um die Suche und ein entsprechendes Ereignis, dass die Suche den Abschluss signalisieren auslösen.Abonnenten der Ereignisse, um die Ergebnisse von Ereignisobjekten Argument zu erhalten.

Mit dieser praktischen Klasse bereit let’s untersuchen genau ansehen die wichtigsten Klassen im Projekt ObaApp.

Definieren Sie die Entität zuordnen

Als Erstes müssen Sie beim Schreiben einer Anwendung Zuordnung ist die Zuordnung Entität definieren.Hier ist die Definition der Entität, von der SDK-Dokumentation: “ Eine Entität ist ein Geo zugeordnete Element auf der Karte.Eine Zuordnung Entität kann eine Position, eine Polylinie, ein Vieleck oder eine Überlagerung Bild sein. ” Einfache Faustregel ist, wenn Sie etwas auf der Karte platzieren möchten, eine Instanz der Entität, um es darzustellen.Sie möchten in der Regel eine Unterklasse der Entität zum Hinzufügen von zusätzlichen Eigenschaften und benutzerdefinierte Logik für Ihre Entitäten zu erstellen.Da die Speicherorte der Bus angehalten wird, angezeigt werden soll, habe ich in meiner Anwendung eine BusStopEntity Klasse ein Bus-Stop darstellen.Abbildung 5 zeigt die BusStopEntity-Klasse.

Abbildung 5 Die BusStopEntity-Klasse

public class BusStopEntity : Entity
{
  private readonly DependencyProperty NamePropertyKey =
    Entity.RetrieveProperty(“Name”, typeof(string));

  private BusStop _busStop;

  // Short name of this bus stop
  public string Name
  {
    get { return _busStop.Name; }
  }

  // Unique id of this bus stop
  public string StopId
  {
    get { return _busStop.Id; }
  }

  public BusStopEntity(BusStop busStop, 
    PushpinFactoryContract pushpinFactory)
  {
    _busStop = busStop;

    // Set the location of this Entity
    Location location = 
      new Location(_busStop.Latitude, _busStop.Longitude);
    this.Primitive = pushpinFactory.CreateStandardPushpin(location, “B”);

    // Set the name of this Entity
    this.SetValue(NamePropertyKey, _busStop.Name);
  }
}

Meine BusStopEntity-Klasse stellt zwei Eigenschaften Name und StopId, die ich später zum Binden-an Steuerelemente der Benutzeroberfläche verwenden.Diese Werte stammen aus der zugrunde liegenden BusStop-Instanz.Im Projekt ObaLib definiert, kapselt die BusStop-Klasse Daten von einem Bus-Stop, die von der API Oba abgerufen.

Im Konstruktor legte ich auch die Primitive und Name-Eigenschaften.Die primitive-Eigenschaft gibt den Typ (z. B. Punkt, Polygon oder Polylinie) und den Speicherort der eigene Entität.Um die Längen- und Breitengrad Werte der Instanz BusStop festgelegt Speicherort.Darüber hinaus ermöglicht das Festlegen der primitive-Eigenschaft auf den zurückgegebenen Wert der PushpinFactoryContract.CreateStandardPushpin-eigene Entität das Aussehen und Verhalten des standardmäßigen Bing Maps-Pins aus.Die PushpinFactoryContract bietet praktischer Methoden zum Erstellen von allgemeinen PIN UI-Elemente.Sie brauchen ihn verwenden, wenn Sie eigene benutzerdefinierte PIN-Shapes erstellen möchten.Ich hier ein Buchstabe B (für Bus) innerhalb der PIN genauso einfach ausgedrückt, jedoch können Sie ein Bild oder eine beliebige "UIElement".

Obwohl ich in meiner Unterklasse die Name-Eigenschaft definieren, es wird nicht erkannt von anderen Typen im SDK – diese Typen haben einfach keine Kenntnis von der Eigenschaft.Aus diesem Grund legte ich auch den gleichen Wert für die Name-Abhängigkeitseigenschaft, die ich über den Aufruf Entity.RetrieveProperty (“ Name ”, typeof(string)). AbrufenAuf diese Weise aktivieren ich andere Features beim Abrufen des Namens einer Entität Bus beenden.

Schreiben Sie die Main-Plug-in

Wie bereits erwähnt, benötigen Sie eine Plug-in-abgeleitete Klasse zur Darstellung der Anwendung zuordnen. Abbildung 6 zeigt meine, der treffend ObaPlugin genannt wird.Das plug-in wird insgesamt sechs Verträge importiert.Beachten Sie, dass alle Verträge von Karten Bing bereitgestellten Microsoft-Namenskonvention folgen / *.Die SDK-Dokumentation finden Sie ausführliche Beschreibungen aller Verträge, die importierten, siehe Abbildung 7 werden können.

Abbildung 6 Imports deklarierte Klasse ObaPlugin

public class ObaPlugin : Plugin
{

  #region Imports

  [ImportSingle(“Microsoft/LayerManagerContract”, ImportLoadPolicy.Synchronous)]
  public LayerManagerContract LayerManager { get; set; }

  [ImportSingle(“Microsoft/PopupContract”, ImportLoadPolicy.Synchronous)]
  public PopupContract PopupContract { get; set; }

  [ImportSingle(“Microsoft/ConfigurationContract”, 
    ImportLoadPolicy.Synchronous)]
  public ConfigurationContract ConfigurationContract { get; set; }

  [ImportSingle(“Microsoft/MapContract”, ImportLoadPolicy.Synchronous)]
  public MapContract Map { get; set; }

  [ImportSingle(“Microsoft/PushpinFactoryContract”, 
    ImportLoadPolicy.Synchronous)]
  public PushpinFactoryContract PushpinFactory { get; set; }

  [ImportSingle(“Microsoft/ContributorHelperContract”, 
    ImportLoadPolicy.Synchronous)]
  public ContributorHelperContract ContributorHelper { get; set; }

  #endregion

Abbildung 7 Verträge importiert, indem die ObaPlugin

Vertragstyp Beschreibung
LayerManagerContract Ermöglicht ein plug-in hinzufügen oder Entfernen von Ebenen zuordnen.
PopupContract Ermöglicht ein plug-in für die Popup-Benutzeroberfläche anzuzeigen, wenn Benutzer bewegt wird oder auf die Entitäten/Pusphins klickt.
ConfigurationContract Ermöglicht ein plug-in für die Konfigurationswerte zur Laufzeit dynamisch aus der Konfigurationsdatei geladen.
MapContract Ermöglicht ein plug-in für die Zuordnung zu steuern.
PushpinFactoryContract Ermöglicht ein plug-in für die standard-Pins für seine Entitäten zu rendern.
ContributorHelperContract Ermöglicht das Erstellen asynchroner oder Demand-Load Mitwirkende plug-in.(Ich werde dieses Hilfsprogramm verwenden, um meine Popup “ Driving Richtungen ” und “ StreetSide ” Mitwirkenden Verknüpfungen später hinzufügen.)

Nach dem deklarieren die Importe, überschreiben ich virtuelle Initialize-Methode:

public override void Initialize()
{
  // Obtain the application key from configuration file
  IDictionary<string, object> configs = 
    ConfigurationContract.GetDictionary(this.Token);
  string appKey = (string)configs[“ApplicationKey”];
  _searchHelper = new ObaSearchHelper(appKey);
  _searchHelper.SearchStopsByLocationCompleted += 
    OnSearchBusStopsCompleted;
  _searchHelper.SearchArrivalTimesForStopCompleted += 
    OnSearchArrivalTimesComplete;

  // Update the bus stop every time map view changes
  Map.TargetViewChangedThrottled += (s, e) => RefreshBusStops();
}

Diese Methode wird genau einmal aufgerufen werden, nachdem die Plug-In-Instanz erstellt wird, und alle deklarierten Importe zufrieden sind.Dies ist die perfekte Stelle keine Initialisierung Arbeit ausführen, die die importierten Verträge abhängt.Wenn Ihr plug-in Exporte verfügt, müssen Sie sicherstellen, dass die exportierten Eigenschaften vollständig durch die Zeit instanziiert werden Initialize zurückgibt.

Ich zuerst den Anwendungsschlüssel aus der Konfigurationsdatei über die importierten ConfigurationContract-Instanz zu erhalten und an den ObaSearchHelper-Konstruktor übergeben.Den Anwendungsschlüssel in der Konfigurationsdatei, einfügen, kann ich einfach jederzeit ändern, ohne Neukompilierung des Projekts verwenden.

Das zweite, was, das ich tun, wird das Ereignis TargetViewChangedThrottled aus der Instanz MapContract einzubinden.Dieses Ereignis wird ausgelöst, bei jedem Anzeigen der Karte entweder programmgesteuert oder durch Benutzerinteraktion geändert wird.Wie der Name schon sagt, ist das Ereignis intern gedrosselt, so, dass es zu oft in kurze Zeit ausgelöst wird nicht.Daher ist es eine perfekte Ereignis zu berücksichtigen, wenn Sie Ihre Entitäten mit Kartenansicht synchron zu halten möchten.In meinem Fall rufe ich die RefreshBusStops-Methode zum Aktualisieren der Liste der Bus beendet.Hier ist die Definition der RefreshBusStops:

internal void RefreshBusStops()
{
  if (Map.ZoomLevel >= 14)
  {
    // Search within the radius of 1km, maximum 150 results
    _searchHelper.SearchStopsByLocation(
      Map.Center.Latitude, Map.Center.Longitude, radius: 1000, 150);
  }
  else
  {
    // Clear all bus stops
    ClearBusStops();
  }
}

Hier überprüfen, dass die aktuelle Zuordnung Vergrößerungsstufe mindestens 14 ist und anschließend Ausstellen von einen Aufruf an die SearchStopsByLocation-Methode, die Liste der alle Bus Stopps innerhalb eines angegebenen Radius um den Mittelpunkt der Zuordnung zurück.Andernfalls ist die Zoomstufe kleiner 14, löschen Bedeutung die Zuordnung auf der Ebene der Ort nicht in nahe genug gezoomt wird ich alle Bus beendet.

SearchStopsByLocation-Methode (asynchron) abgeschlossen ist, löst das Abbildung 8 , die ich zuvor in die Initialize-Methode abonniert Siehe SearchStopsByLocationCompleted-Ereignis aus.

Abbildung 8 Handler für das Ereignis SearchBusStopsCompleted

private int _searchCount;
private Dictionary<BusStop, int> _busStopCache = 
  new Dictionary<BusStop, int>();
private ObaLayer _layer;

private void OnSearchBusStopsCompleted(object sender, BusStopsEventArgs e)
{
  _searchCount++;

  // Contains new bus stops not present in the current view 
  Collection<BusStopEntity> addedEntities = 
    new Collection<BusStopEntity>();
  foreach (BusStop stop in e.BusStops)
  {
    // If this bus stop is not in the cache, it is a new one
    if (!_busStopCache.ContainsKey(stop))
    {
      addedEntities.Add(new BusStopEntity(stop, PushpinFactory));
    }

    // Marks this bus stop as being in the current search
    _busStopCache[stop] = _searchCount;
  }

  // Contains the old bus stops 
  // that should be removed from the current view
  Collection<BusStopEntity> removedEntities = 
    new Collection<BusStopEntity>();
  foreach (BusStopEntity bse in _layer.Entities)
  {
    // This bus stop belongs to the previous search, 
    // add it to removed list
    if (_busStopCache[bse.BusStop] < _searchCount)
    {
      removedEntities.Add(bse);
      _busStopCache.Remove(bse.BusStop);
    }
  }

// Tells the layer to add new in-view entities 
// and remove out-of-view ones 
      _layer.RefreshEntities(addedEntities, removedEntities);
}

Beachten Sie das Objekt des Typs ObaLayer, die ich im nächsten Abschnitt erläutert.Im Moment wird reicht es zu sagen, dass dieses Objekt zuständig für die Verwaltung des linken Bereichs Inhalt und Entitäten auf der Karte.

Beachten Sie, dass ich ein Dictionary < BusStop, Int > verwendeObjekt, das mir die aktuelle Liste der angezeigten Bus hält der helfen.Mit Hilfe der in diesem Wörterbuch jedes Mal, wenn ich eine brandneue Bus beendet aus der Webdienstaufruf erhalten kann ich schnell ermitteln der neuen beendet, die noch nicht angezeigt werden nicht als auch die alten unterbrochen, die jetzt aus der Ansicht aufgrund von der Karte anzeigen ändern.

Sie können sich Fragen, warum ich nicht nur deaktivieren, alle aktuellen Bus beendet und den neuen Satz von Bus vollständig anzeigen.Der Grund dafür ist Leistung.Wenn ich dies haben, würde ich die PIN-Steuerelemente neu erstellt werden, auch wenn viele davon an derselben Position in der alten und neuen Kartenansichten erzwingen.Schlimmer noch, würde Benutzern einen kurzen Blitz angezeigt, wenn die Pins wurden entfernt und schnell wieder hinzugefügt.Mein Ansatz entfällt beide dieser Einschränkungen.

Anzeigen von Inhalt und Pins mit einem Layer

Die-Plug-in-Klasse stellt Ihre Anwendung zuordnen, aber wenn UI-Darstellungen der es angezeigt werden soll, müssen Sie die Ebenen zu erstellen.Eine Ebene ist eine abstrakte Konzept, das Sie Entitäten auf der Karte als Pins (oder Polylinien oder Vielecke) platzieren und benutzerdefinierte Benutzeroberfläche anzeigen auf den linken Bereich Inhalt ermöglicht.Optional, es kann auch eine beliebige Benutzeroberfläche Überlagerung über die Zuordnung platzieren, aber, für meine Anwendung muss nicht.Die meisten Anwendungen müssen nur um eine Ebene.

Wenn Sie Photoshop verwendet haben, werden Sie das Konzept Photoshop-Ebenen sehr ähnlich finden.Die Schaltfläche "Verlauf" (befindet sich in der unteren linken Ecke der Seite) zeigt die Liste aller derzeit geladenen Schichten.Können Sie zum ein- oder Ausblenden von keinem Layer, oder Sie können festlegen, dass eine Schicht aktiv wird.Sobald ein Layer aktiv ist, wird das Systemsteuerung UI-Element im linken Bereich angezeigt, Bereich und alle seine Entitäten in der Z-Index-Stapel vorwärts geschaltet werden.

Im Code wird eine Schicht eine Unterklasse der abstrakten Klasse Layer.Wie in Abbildung 9 , ObaPlugin erstellt und verwaltet eine Instanz der Klasse ObaLayer Layer abgeleitet.

Abbildung 9 ObaLayer Klasse

internal class ObaLayer : Layer
{
  private ObaPlugin _parent;
  private BusStopsPanel _resultPanel;
  private ObservableCollection<BusStopEntity> _busStopEntities;

  public ObaLayer(ObaPlug-in parent) : base(parent.Token)
  {
    _parent = parent;

    // Contains the set of active bus stop entities 
    // for data binding purpose
    _busStopEntities = new ObservableCollection<BusStopEntity>();
    _resultPanel = new BusStopsPanel(parent, this);
    _resultPanel.DataContext = _busStopEntities;

    this.Title = “Bus stops”;
    this.Panel = _resultPanel;
  }
  ...
}

Im Konstruktor der ObaLayer legte ich den Titel der Ebene, die sich am oberen Rand der linke Bereich angezeigt wird.Ich auch die Panel-Eigenschaft auf eine Instanz des Benutzersteuerelements BusStopsPanel, dem eingenommenen Bereich gesamte linke Bereich Wenn mein Layer aktiv wird festgelegt.Das Benutzersteuerelement DataContext-Eigenschaft wird auf eine Instanz der ObservableCollection <Entity> festgelegt.

Wie angezeigt der Layer erhalten?Erfolgt durch ObaPlugin wie in Abbildung 10 .

Abbildung 10 ShowResultLayer-Methode zeigt die ObaLayer-Instanz für den Benutzer

public override void Activate(IDictionary<string, string> activationParameters)
{
  ShowResultLayer();
  RefreshBusStops();
}

private void ShowResultLayer()
{
  if (_layer == null)
  {
    _layer = new ObaLayer(this);
  }

  if (LayerManager.ContainsLayer(_layer))
  {
    LayerManager.BringToFront(_layer);
  }
  else
  {
    LayerManager.AddLayer(_layer);
  }
}

Die Überschreibung Activate-Methode wird aufgerufen, jedes Mal, wenn meine Anwendung Zuordnung über den Map-Anwendungen-Katalog aktiviert ist.Meine Ebene anzeigen möchten, finden Sie ich zuvor importierten LayerManagerContract-Typ.Die LayerManagerContract-Klasse definiert Methoden zum Arbeiten mit Ebenen.Meine Schicht bereits hinzugefügt wird, legte ich es zum aktiven durch Aufrufen der BringToFront-Methode.Versuch, fügen Sie dem layer-zweimal führt zu einer Ausnahme.

Abbildung 8 als ich die ObaLayer.RefreshEntities-Methode zum Aktualisieren des Bus beendet bezeichnet. Abbildung 11 zeigt seine Definition.

Abbildung 11 die Definition ObaLayer.RefreshEntities Methode

public void RefreshEntities(
  ICollection<BusStopEntity> addedEntities,
  ICollection<BusStopEntity> removedEntities)
{
  foreach (BusStopEntity entity in removedEntities)
  {
    // Remove this pushpin from the map
    this.Entities.Remove(entity);
    // Remove this bus stop entry from the panel
    _busStopEntities.Remove(entity);
  }

  foreach (BusStopEntity entity in addedEntities)
  {
    // Add this pushpin to the map
    this.Entities.Add(entity);
    // Add this bus stop entry to the panel
    _busStopEntities.Add(entity);

    // Register this entity to have popup behavior
    _parent.PopupContract.Register(entity, OnPopupStateChangeHandler);
  }
}

Hinzufügen oder Entfernen einer Entität auf der Karte, verwende ich die Auflistungseigenschaft Layer.Entities.Und für jedes neue BusStopEntity, ich die PopupContract.Register-Methode der Popup-Benutzeroberfläche auf meinem Bus Stop Pusphin zu registrieren.Die Register-Methode akzeptiert die Entität und eine Callback-Methode, die aufgerufen hat, wenn das Popup-Steuerelement auf die Entität geändert (siehe Abbildung 12 ).

Abbildung 12 Ändern des Status eines Popup-Steuerelements

private void OnPopupStateChangeHandler(PopupStateChangeContext context)
{
  if (context.State == PopupState.Closed)
  {
    return;
  }

  BusStopEntity entity = (BusStopEntity)context.Entity;
  context.Title = entity.Name;
  context.Content = “Bus numbers: “ + entity.BusRoutesAsString;

  // Only shows contributors link in the normal state of popup
  if (context.State == Pop-upState.Normal)
  {
    // Add Arrival times contributor
    context.Contributors.Add(new BusStopContributor(_parent, entity));

    Dictionary<string, object> parameter = new Dictionary<string, object>
    {
      {“Entity”, entity}
    };
    var contributorHelper = _parent.ContributorHelper;

    // Add Directions contributor
    context.Contributors.Add(contributorHelper.
      CreateDemandLoadContributor(
      “Microsoft/DirectionsContributorFactoryContract”, 
      parameter, “Directions”));

    // Add Streetside contributor
    context.Contributors.Add(contributorHelper.CreateAsyncContributor(
      “Microsoft/StreetsideContributorFactoryContract”, parameter));
  }
}

Innerhalb der Popup-Rückruf gibt das Methodenargument PopupStateChangeContext Typs mir Zugriff auf den aktuellen Zustand des Popup und die Entität derzeit unter Popup.Basierend auf diesen, legte ich das Popupmenü Titel mit dem Namen Bus Stop und dem Popupmenü Inhalt mit der BusStopEntity.BusRoutesAsString-Eigenschaft gibt eine durch Kommas getrennte Liste der Bus-Routen, die für diesen bestimmten Bus Stop.

Wenn das Popup im normalen Zustand befindet, kann ebenfalls hinzugefügt drei Mitwirkenden Links das Popupfenster über die Auflistungseigenschaft beteiligte Personen – der Mitwirkende “ eingetroffen times ” zeigt den Ankunft Zeiten für diese Stop Bus, der Mitwirkende Routen Ruft die treibenden Richtungen-Funktion und der Mitwirkende Streetside Straße Ebene Map-Modus wechselt.

Ein Teilnehmer ist ein Hyperlink am unteren Rand das Popup-Steuerelement dargestellt.Zuordnung Anwendungen eine bestimmte zentrales Feature von Bing Zuordnungen aufrufen kann.Mitwirkende generieren möchten, rufen Sie eine der beiden Methoden des Typs ContributorHelperContract: CreateAsyncContributor oder CreateDemandLoadContributor.Beide Methoden ein Mitwirkender Proxy synchron zurückzugeben und das Laden der echten Mitwirkenden Instanz zurückgestellt.Der einzige Unterschied ist, dass die CreateAsyncContributor echten Mitwirkenden lädt, sobald Sie zurückgibt, Gegensatz CreateDemandLoadContributor nur, wenn die Proxy Mitwirkenden Verknüpfung zum ersten Mal aufgerufen wird.

BusStopsPanel UserControl

BusStopsPanel ist ein Benutzersteuerelement, die für die Anzeige der Liste der Bus-beenden-Ansicht im linken Bereich (wie dargestellt in Abbildung 1 ) verantwortlich ist.Es enthält eine ItemsControl-Instanz mit der ItemTemplate-Eigenschaft auf eine benutzerdefinierte DataTemplate festgelegt (siehe Abbildung 13 ).Beachten Sie, dass ich Virtualisierung Benutzeroberflächenmodus für meine ItemsControl aktivieren, indem die ItemsPanel-Eigenschaft, um ein VirtualizingStackPanel verwenden.Im Allgemeinen ist es ratsam, UI-Virtualisierung auf ItemsControl anwenden, wenn Ihre Anwendung Hunderte von Elementen in diese laden kann.

Abbildung 13 Die BusStopsPanel UserControl

<UserControl.Resources>
  <DataTemplate x:Key=”BusStopTemplate”>
    <Border Margin=”2,0,2,12”
      <StackPanel>
        <TextBlock Text=”{Binding Name}” FontSize=”14” FontWeight=”Bold”  
          TextWrapping=”Wrap” />
        <TextBlock Text=”{Binding BusRoutesAsString, StringFormat=’Bus numbers:
          {0}’}” FontSize=”12” TextWrapping=”Wrap” />
          <HyperlinkButton Content=”Arrival times” Click=”OnBusStopClick” 
            Style=”{StaticResource App.P2.Hyperlink}”         
              HorizontalAlignment=”Left” />
      </StackPanel>
    </Border>
  </DataTemplate>

  <ControlTemplate x:Key=”ScrollableItemsControl”    
    TargetType=”ItemsControl”>
    <ScrollViewer Style=”{StaticResource App.ScrollViewer}”  
      VerticalScrollBarVisibility=”Auto”>
      <ItemsPresenter />
    </ScrollViewer>
  </ControlTemplate>
</UserControl.Resources>
    
<ItemsControl 
  ItemsSource=”{Binding}”
  VirtualizingStackPanel.VirtualizationMode=”Recycling”
  ItemTemplate=”{StaticResource BusStopTemplate}” 
  Template=”{StaticResource ScrollableItemsControl}”>
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <VirtualizingStackPanel />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ItemsControl>

Innerhalb der Bus-Stop DataTemplate wurde ein HyperlinkButton-Steuerelement hinzugefügt, die beim Klicken auf die Suche für die Ankunft Zeiten für den entsprechenden Bus-Trigger beendet:

private void OnBusStopClick(object sender, RoutedEventArgs e)
{
  FrameworkElement element = (FrameworkElement)sender;
  BusStopEntity entity = (BusStopEntity)element.DataContext;
  _plugin.SearchArrivalTimes(entity);
}

Beachten Sie, dass die Style-Eigenschaft für ein Objekt aus der Auflistung StaticResource mit dem Schlüssel als “ App.P2.Hyperlink. ” festgelegt ist Bing Maps bietet eine Reihe von UI-Ressourcen wie Stile und ControlTemplates von allgemeinen Steuerelementen sowie standardmäßige Farben und vom verwendeten Bing Karten selbst Pinsel.Zuordnung app Autoren werden empfohlen, diese Ressourcen auf Ihre UI-Elemente angewendet werden, so, dass Sie das dasselbe Aussehen und Verhalten wie systemeigene UI-Elemente verfügen.Finden Sie in der Dokumentation zu den Ressourcen, die von Karten Bing bereitgestellt.

SearchArrivalTimes ist eine interne Methode der ObaPlugin-Klasse.Es ruft die ObaSearchHelper.SearchArrivalTimesForStop-Methode zum Abrufen von Ankunft Zeiten für das angegebene Bus anhalten:

internal void SearchArrivalTimes(BusStopEntity _entity)
{
  _searchHelper.SearchArrivalTimesForStop(_entity.StopId, _entity);
}

Wenn die Suche abgeschlossen ist, wird das plug-in ObaLayer im linken Bereich die Ankunft Zeiten anzeigen angewiesen.ObaLayer wird dessen Titel und Panel-Eigenschaften dynamisch ändern.

Testen und Debuggen der Anwendung zuordnen

Wenn fertig codieren, müssen Sie Ihre Anwendung testen.Die Bing Maps-Website enthält eine Entwicklermodus, die durch Anhängen aktiviert ist ein “ Entwickler = 1 ” Abfrageparameter an den URL wie folgt: https://www.bing.com/maps/explore/?developer=1.When in developer mode, you test your map app with the “Map app test tool,” which can be activated via the same Map apps gallery.Das Dienstprogramm Map app Test können Sie Plug-in-Assemblys aus der lokalen Festplatte auswählen.Er lädt dann das plug-in in die Website ebenso wie mit allen systemeigenen-Plug-ins.Fügen Sie zum Debuggen des Codes VS.NET an Ihrem Browser, während Ihrer Anwendung geladen wird.Stellen Sie sicher, dass Sie Silverlight Debug-Codetyp festlegen.

Senden Sie Ihre Karte App

Wenn Sie Ihren Code haben, können Sie schließlich Ihre Anwendung auf der Site Maps Bing offiziell veröffentlicht werden senden. Senden eine neue Applikation, und zeigen Sie den Status der vorherige Übermittlungen Bing Maps Konto Center (bingmapsportal.com ). Die SDK-Dokumentation enthält detaillierte Anweisungen zum Senden Ihrer Anwendung, und listet die Anforderungen, die Ihren Anwendungen erfüllen müssen, um genehmigt werden.

Rufen Sie in Aktion

Daher gibt es haben Sie es: eine ausgereifte, Echtzeit-Übertragung-Anwendung, die eine Menge Code erforderlich ist, nicht. Das SDK enthält die Bausteine, die beim Schreiben von Map-zentrierten Anwendungen eine angenehmer und lohnende Erfahrung. Ich empfehle Ihnen, laden Sie noch heute das SDK, erfahren es und Schreiben Ihrer eigenen Anwendungen zuordnen.

Luan Nguyen war als Entwickler (früher Microsoft Virtual Earth) Bing Maps-Team für fast vier Jahren. Er war Mitglied der Shell-Funktion-Team die war verantwortlich für die Framework-Shell der Site Maps Bing und Bing Map-App-SDK. Er hat kürzlich an das ASP.NET-Team gewechselt.

Dank an die folgenden technischen Experten für die Überprüfung der in diesem Artikel: Alan Paulin, Chris PendletonDan Polivy -und Greg Schechter