App-Entwicklung für Windows Phone 8–Part 4-1

Von maxs Autor Feed 16. February 2013 07:00

Dies ist der 4. und vorerst letzte Teil unserer Serie, in der wir das GeoFotoalbum App entwickeln. Diesmal erweitern wir unsere Anwendung um die von Windows Phone und auch Windows 8 bekannten Live-Tiles (Kacheln). Dabei werden wir ein Live-Tile erstellen, welches uns jeweils die geografisch nahesten 3 Bilder in einer Slide-Show anzeigt.

 

Inhalt des Artikels

  • Erstellen eines BackgroundTasks (Scheduling Agent)
  • Erstellen einer Klassenbibliothek
  • Notwendige Erweiterungen in der Foto-Klasse (Sortierung)
  • Ansprechen und Verwalten von Live-Tiles

image

 

Erstellen eines BackgroundTasks

Zuallererst müssen wir unserer Projektmappe ein neues Projekt hinzufügen, damit wir einen gesonderten Backgroundtask realisieren können. Dabei wählen wir „Windows Phone Scheduled Task Agent“ als Zielprojekt. Der Name ist natürlich frei wählbar, wir entschieden uns in diesem Fall für „GeoFotoalbumLiveTileService“.

Nachdem wir das Projekt hinzugefügt haben, sollte unsere Projektmappe in etwa so aussehen:

image

Visual Studio erstellt uns angenehmerweise direkt eine Datei „ScheduledAgent.cs“, welche unseren Service beinhalten wird. Dort sind auch bereits alle wichtigen Einsprungspunkte enthalten und mit Kommentaren markiert. Bevor wir jedoch die OnInvoke-Methode implementieren, müssen wir uns erst darum kümmern, dass wir auch Zugriff auf alle wichtigen Funktionen aus unserem Hauptprojekt haben.

Da dieses jedoch nicht durch den Service referenziert werden kann, müssen wir einige Klassen in eine Klassenbibliothek auslagern.

 

Erstellen einer Klassenbibliothek

Hierfür erstellen wir ein neues Projekt und wählen diesmal „Windows Phone Class Library“ als Ziel. Der Name, für den wir uns in diesem Blogeintrag entschieden haben, lautet diesmal „GeoFotoalbumClassLibrary“. Die automatisch erstellte Datei „Class1.cs“ löschen wir direkt wieder.

Nun verschieben wir die 4 Klassen, die wir zusätzlich in unserem GeoFotoalbumLiveTileService benötigen, in die unsere Klassenbibliothek:

  • DistanceBetweenLocations.cs
  • ExifReader.cs
  • ExifTags.cs
  • Foto.cs

Nun erweitern wir die Referenzen der beiden Projekte, damit diese auch Zugriff auf die Inhalte der Klassenbibliothek erhalten (Verweise –> Verweis hinzufügen –> Projektverweise und dann unsere GeoFotoalbumClassLibrary auswählen).

Unsere Projektmappe sollte nun wie folgt aussehen:

image

 

Notwendige Erweiterungen in der Foto-Klasse (Sortierung)

Wir möchten etwas später im Live-Tile die drei geografisch nahesten Bilder anzeigen. Damit wir das tun können, müssen wir zuerst eine Möglichkeit implementieren, wie wir diese finden. Am einfachsten erreichen wir dies mit der Sort-Methode der bereits implementierten Listen von C# bzw. des .NET Frameworks.

Das Problem hierbei ist jedoch, dass C# noch nicht weiß, wie es unsere Fotos in der Liste sortieren soll. Damit dies klar wird, müssen wir unsere Foto-Klasse ein neues Interface implementieren lassen: IComparable. Die Klassendefinition sollte nun also etwa so aussehen: public class Foto : INotifyPropertyChanged, IComparable.

Das Hinzufügen dieses neuen Interfaces bietet uns nun die Möglichkeit die Methode CompareTo zu implementieren:

// Mit der Implementierung dieses Interfaces, können wir die Sortierverfahren auf den
// Distanzwert aufmerksam machen, wodurch dieser als Sortierschlüssel verwendet wird
int IComparable.CompareTo(object obj)
{
  if (obj is Foto)
  {
    Foto f1 = (Foto)this;
    Foto f2 = (Foto)obj;

    // Der Returnwert bestimmt die Reihenfolge der beiden Elemente in der Liste
    if (f1.entfernung > f2.entfernung)
    {
      return 1;
    }
    if (f1.entfernung < f2.entfernung)
    {
      return -1;
    }
    return 0;
  }
  else
  {
    // Standardverfahren, falls das andere Objekt kein Foto ist
    return String.Compare(this.ToString(), obj.ToString());
  }
}

Der Code sollte hierbei zum größten Teil selbsterklärend sein. Wir prüfen zuerst, ob das empfangene Objekt (mit dem das Aktuelle verglichen werden soll) auch ein Foto ist. Falls dem so ist, benutzen wir die Entfernungswerte der beiden Bilder als Sortierschlüssel und returnieren Werte zwischen 1 und -1. Dabei steht 0 für Gleichheit des gewählten Sortierschlüssels; 1 und -1 beschreiben, welches Element zuerst in der Liste dargestellt wird.

Falls das Objekt jedoch kein Foto ist, greifen wir auf das Standardverfahren zurück und vergleichen die ToString-Werte der beiden Instanzen.

Wenn wir nun eine Liste von Fotos haben und dort „Sort“ aufrufen, wird unsere CompareTo Methode dazu benutzt die Elemente so zu sortieren, wie wir das vorgesehen haben.

 

Ansprechen und Verwalten von Live-Tiles

Damit wir nun das Live-Tile unserer Anwendung aktualisieren können, legen wir eine neue Klasse in unserer Klassenbibliothek an und nennen diese „TileUpdater.cs“. Diese statten wir mit zwei Methoden aus, die wir UpdateTile und StartService nennen. UpdateTile nimmt hierbei eine Liste von fotos entgegen. Wenn alles funktioniert hat, sollte die Klasse nun etwa so aussehen:

public class TileUpdater
{
  public static void UpdateTile(List<Foto> fotos)
  {
      
  }

  public static void StartService()
  {

  }
}

Soweit, so gut. Bevor wir jedoch diese beiden Methoden (die das Herzstück unserer Funktionalität beinhalten werden) implementieren, kümmern wir uns um die Aufrufe. Dazu wechseln wir zunächst in das Code-Behind-File der Klasse MainPage. Aus der laufenden App heraus, wollen wir das Live-Tile bei jedem Positionswechsel aktualisieren. Wir erweitern dafür das PositionChanged-Event des Watchers um die folgenden drei Zeilen:

// Wir aktualisieren am Ende noch das Live Tile
List<Foto> list = fotos.ToList();
list.Sort();
TileUpdater.UpdateTile(list);

Dabei wandeln wir die ObservableCollection in eine herkömmliche Liste um, sortieren diese und verwenden dann unsere gerade eben angelegte UpdateTile-Methode.

Dies war der erste Teil, doch was passiert nun, wenn die Anwendung nicht mehr läuft? Schön wäre es, wenn unser Backgroundservice dann genau dasselbe tut – eine Liste von Fotos erstellen, diese sortieren und UpdateTile aufrufen.

Damit er dies jedoch tun kann, müssen wir den Service zunächst einmal starten. Da wir uns sowieso gerade in der richtigen Datei (MainPage.xaml.cs) befinden, wechseln wir in den Konstruktor der Klasse und erweitern diesen um einen Aufruf von StartService der TileUpdater-Klasse. Der neue Konstruktor sollte dann in etwa so aussehen:

public MainPage()
{
  InitializeComponent();
  LoadImages();
  InitializeGeoWatcher();

  // Geeignetes Zoomlevel wählen und positionLayer registrieren
  Map.Layers.Add(positionLayer);
  Map.ZoomLevel = 12;

  // Service starten
  TileUpdater.StartService();
}

Perfekt! :) Bevor wir nun die Funktion des TileUpdaters ausimplementieren, kümmern wir uns zuerst noch um die Aufrufe durch den ScheduledAgent. Wir wechseln also in die dazugehörige Klasse (ScheduledAgent im GeoFotoalbumLiveTileService-Projekt) und statten diese zuerst einmal mit folgenden neuen Membervariablen aus:

private List<Foto> fotos;
private GeoCoordinateWatcher watcher;

Beide Variablen sollten dabei selbsterklärend sein, da sie in ganz ähnlicher Form bereits im Code-Behind-File der MainPage vorkommen.

„Fotos“ ist hierbei eine ganz normale Liste, da wir die Funktionalität der ObservableCollection, welche normalerweise das User Interface benachrichtigen würde, aufgrund dessen Fehlens, nicht benötigen.

Wir übernehmen als nächstes die Initialize- und LoadImages Methoden aus der MainPage in leicht veränderter Form:

public void LoadImages()
{
  MediaLibrary medialibrary = new MediaLibrary();

  fotos = new List<Foto>();
  foreach (Picture picture in medialibrary.Pictures)
  {
    Foto foto = new Foto(picture);
    fotos.Add(foto);
  }
}

public void Initialize()
{
  // Initialisiert wird, wie bereits auch im Hauptprogramm die Bildliste und der Watcher
  watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High)
  {
    MovementThreshold = 20
  };

  watcher.PositionChanged += this.watcher_PositionChanged;
  LoadImages();
}

Auch hier findet sich im Kern dieselbe Funktionalität, wie auch schon in der MainPage. Initialize initialisiert den GeoCoordinateWatcher und ruft danach LoadImages auf, welches wiederum eine Liste mit allen Bildern aus der Medienbibliothek erstellt.

Auch hier benötigt der Watcher wieder eine Callback-Methode, die er bei veränderter Position aufrufen kann:

private void watcher_PositionChanged(object sender,
    GeoPositionChangedEventArgs<GeoCoordinate> e)
{

}

clip_image001 Exkurs: Callbacks. Beim Aufruf von Methoden, die besonders viel Zeit benötigen (wie etwa Remote-Anfragen) und deshalb asynchron aufgerufen werden, ist es oftmals gewünscht, irgendwie benachrichtigt zu werden, sobald die Methode erfolgreich beendet wurde. Callbacks sind eine Möglichkeit genau dies zu erreichen. Kurz gesagt, passiert hierbei nichts anderes, als dass wir der aufgerufenen Methode unsere eigene Methode mitgeben und diese dann die mitgegebene Methode an ihrem Ende (oder einem anderen wichtigen Punkt) aufruft.

Sehr gut! Nun wenden wir uns der OnInvoke-Methode des Agents zu, welche immer dann vom System aufgerufen wird, wenn der Service einmal ausgeführt werden soll (dies ist im Normalbetrieb etwa alle 30 Minuten der Fall).

Wir möchten jedoch nun aufgrund der enormen Länge dieses Blogeintrags an dieser Stelle unterbrechen und führen den Blogeintrag am Freitag der kommenden Woche hier fort.

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

Datenschutzhinweis: Sie stimmen durch "Kommentar speichern" der Speicherung Ihrer Angaben durch Microsoft Österreich für die Beantwortung der Anfrage zu. Sie erhalten dadurch keine unerwünschten Werbezusendungen. Ihre Emailadresse wird auf Ihren Wunsch dazu verwendet Sie über neue Kommentare zu informieren.

Microsoft respektiert den Datenschutz. Datenschutz & Cookies

Aktuelle Downloads

Azure Free Trial
 
Visual Studio Downloads
 
Windows Azure Free Trial

 
    Developer Events
Instagram
CodeFest.at on Facebook

Datenschutz & Cookies · Nutzungsbedingungen · Impressum · Markenzeichen
© 2013 Microsoft. Alle Rechte vorbehalten · BlogEngine.NET 2.7.0.0 · Diese Website wird für Microsoft von atwork gehostet.
powered by atwork