Georg fährt extra nach Berlin um Steve Ballmer zu treffen

31 Tage Mango | Tag #8: Kontakte API

Dieser Artikel ist Tag #8 der Serie 31 Tage Mango von Jeff Blankenburg.

Der Originalartikel befindet sich hier: Day #8: Contacts API.

Wenn Sie die Anwendung zu diesem Artikel auf Ihr Gerät herunterladen möchten, finden Sie diese auch im Windows Phone Marketplace.

Heute befassen wir uns mit einem neu verfügbaren Namespace: Microsoft.Phone.UserData. In der Serie 31 Days of Windows Phone habe ich dieses Thema in Day #8 called Choosers behandelt. Mit diesen Tasks waren wir in der Lage, den Anwender zur Wahl eines Eintrags aus seinen Kontakten aufzufordern. Damit konnten wir eine bestimmte Eigenschaft des Kontakts abfragen: Eine Email Adresse oder eine Telefonnummer. Beides gleichzeitig ging nicht. Außerdem konnten wir immer nur genau einen Eintrag bekommen.

Mit dem neuen Namespace in Windows Phone 7.5 können wir die Kontakte des Anwenders wie eine lokale Datenbank verwenden. Wir können darin suchen, mehrere Kontakte abfragen und auf alle Informationen zu den Kontakten zugreifen. Diese Schnittstelle ist mächtiger, erfordert von uns aber mehr manuellen Aufwand.

Wie schon Peter Parkers Onkel Ben gesagt hat: „Aus großer Kraft folgt große Verantwortung“. Nur weil Sie die Möglichkeit haben, auf die Kontakte des Anwenders zuzugreifen, bedeutet das nicht, dass Sie diese Möglichkeit ausnutzen sollten. Damit ein Anwender Ihre Anwendung auf seinem Gerät installiert, muss er Ihnen vertrauen können. Wenn Sie dieses Vertrauen enttäuschen indem Sie alle seine Freunde und Kontakte mit Email und anderem Müll überfluten, wird Ihre Anwendung erstens schnell deinstalliert und zweitens können Sie sich negativer Beurteilungen im Marketplace sicher sein.

Der Zugriff auf die Daten ist nur LESBAR. Wir können keine Kontakte verändern, löschen und nicht mal etwas hinzufügen. Die hier vorgestellte API dient nur dazu, Kontakte zu finden und diese Daten in unserer eigenen Anwendung zu verwenden.

Die Benutzeroberfläche

Wie in allen Artikeln unserer Serie brauchen wir zunächst eine Benutzeroberfläche. In diesem Beispiel bin ich von der Standardvorlage für Windows Phone Anwendungen ausgegangen. Ich habe eine ListBox hinzugefügt und bereits ein Binding angegeben, welches wir später verwenden werden. Wenn Sie mehr über Data Binding in XAML erfahren möchten, können Sie Day #13 of the 31 Days of Silverlight lesen. Weiterhin habe ich eine TextBox hinzugefügt, die wir zur Suche in den Kontakten verwenden werden.

Hier ist das XAML unserer Anwendung:

<phone:PhoneApplicationPage
   x:Class="Day8_ContactsAPI.MainPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
   xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
   FontFamily="{StaticResource PhoneFontFamilyNormal}"
   FontSize="{StaticResource PhoneFontSizeNormal}"
   Foreground="{StaticResource PhoneForegroundBrush}"
   SupportedOrientations="Portrait" Orientation="Portrait"
   shell:SystemTray.IsVisible="True">

    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="31 DAYS OF MANGO - DAY #8" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="contacts api" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <TextBox x:Name="SearchTerm" Height="80" Margin="0,-12,0,539" TextChanged="SearchTerm_TextChanged" />
            <ListBox x:Name="ContactList" Margin="0,70,0,0">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=DisplayName, Mode=OneWay}" />
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Grid>
</phone:PhoneApplicationPage> 

Die wesentlichen Teile im XAML sind erstens unsere ListBox mit dem Namen „ContactList“ und der TextBlock innerhalb des DataTemplate. Wir haben den Wert der Text Eigenschaft des TextBlocks an DisplayName gebunden. Dies wird eine der Eigenschaften sein, die wir zu jedem Kontakt bekommen. Zweitens haben wir eine TextBox mit den Namen „SearchTerm“ hinzugefügt. Wir werden die Eingabe in der TextBox als Suchkriterium verwenden, indem wir den TextChanged Event implementieren. Unsere Oberfläche sollte am Ende etwa so aussehen:

Kontakte suchen

Als erstes müssen wir in unserer code-behind Datei ein using Statement für den Namespace Microsoft.Phone.UserData hinzufügen.

Wir beginnen mit dem Abfangen der Textänderungen unserer TextBox. Den Event Handler im XAML haben wir schon definiert – jetzt brauchen wir noch die Event Handler Methode. Hier ist unser anfänglicher C# Code mit dem Event Handler:

using System;
using System.Linq;
using System.Windows.Controls;
using Microsoft.Phone.Controls;
using Microsoft.Phone.UserData;

namespace Day8_ContactsAPI
{
   public partial class MainPage : PhoneApplicationPage
   {
      public MainPage()
      {
         InitializeComponent();
      }

      private void SearchTerm_TextChanged(object sender, TextChangedEventArgs e)
      {
         SearchContacts(SearchTerm.Text);
      }

      private void SearchContacts(string searchterm)
      {
         //PERFORM THE SEARCH
      }
   }
} 

Die Methode SearchTerm_TextChanged wird jedes Mal aufgerufen, wenn sich der Inhalt unserer TextBox ändert. Die eigentliche Suche nach Kontakten habe ich in eine separate Methode ausgelagert. SearchContacts nimmt einen String als Argument und verwendet diesen Wert für die eigentliche Suche.

Um die eigentliche Suche in den Daten durchzuführen, müssen wir eine neue Instanz der Contacts Klasse anlegen (hierzu brauchen wir den Microsoft.Phone.UserData Namespace). Die Suche in den Kontaktdaten gschieht asynchron. Die Contacts Klasse hat einen SearchCompleted Event Handler, den wir verwenden, um auf die Ergebnisse der Suche zuzugreifen wenn diese abgeschlossen ist. Um die Suche mit unseren Kriterien zu starten verwenden wir die SearchAsync(). Werfen wir einen Blick auf den resultierenden C# Code. Danach gehe ich auf die Suchparameter ein, die wir verwenden können.

using System;
using System.Linq;
using System.Windows.Controls;
using Microsoft.Phone.Controls;
using Microsoft.Phone.UserData;

namespace Day8_ContactsAPI
{
   public partial class MainPage : PhoneApplicationPage
   {
      Contacts contacts = new Contacts();

      public MainPage()
      {
         InitializeComponent();
         contacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(contacts_SearchCompleted);
         SearchContacts(String.Empty);
      }

      private void SearchTerm_TextChanged(object sender, TextChangedEventArgs e)
      {
         SearchContacts(SearchTerm.Text);
      }

      void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
      {
         ContactList.ItemsSource = e.Results;
      }

      private void SearchContacts(string searchterm)
      {
         contacts.SearchAsync(searchterm, FilterKind.DisplayName, null);
      }
   }
} 

Wie Sie sehen, rufen wir die Methode SearchContacts() im Konstruktor mit dem Parameter string.Empty auf. Durch Übergabe des string.Empty sagen wir der Methode SearchAsync(), dass wir ALLE Kontakte haben wollen. Im Emulator macht dieser Aufruf keine Probleme (er hat nur 7 Kontakte). Auf einem tatsächlichen Telefon eines Anwenders mit Tausenden von Kontakten kann das Laden aber einige Sekunden dauern. Sie sollten für diesen Fall die Verwendung einer ProgressBar in Erwägung ziehen oder dem Anwender anderweitig anzeigen, dass die Anwendung gerade Daten lädt.

Wie Sie an der Signatur der Methode Contacts.SearchAsync() sehen, erwartet diese drei Parameter:

  • string – das ist das Suchkriterium. In unserem Beispiel wird es mit dem Inhalt der TextBox aus unserer XAML Datei belegt.
  • FilterKind – hiermit können Sie angeben, was Sie suchen möchten. Mögliche Werte sind PhoneNumber, DisplayName oder EmailAddress. Wenn Sie den Wert None angeben, wird der String des Suchkriteriums ignoriert.
  • object – mit diesem Parameter können Sie dem Suchaufruf ein beliebiges Objekt mitgeben, so dass Sie, wenn die Ergebnisse vorliegen, noch wissen, wo diese her kamen. Das kann beispielsweise ein einfacher String sein bis hin zu einem komplexen Objekt, welches Sie bei der Verarbeitung der Ergebnisse benötigen.

Wenn Sie Ihre Anwendung jetzt ausführen, sollten Sie sehen, dass die Ergebnisse abhängig von der Eingabe in der TextBox gefiltert werden. Die gefilterte Liste im Windows Phone Emulator sollte etwa so aussehen:

Die Kontaktdaten

Bisher haben wir die Kontakte auf dem Gerät erfolgreich abgefragt und in einer ListBox angezeigt. Was noch fehlt, ist, welche Daten wir überhaupt bekommen. Da wir die Daten in Form eines IEnumerable bekommen, können wir übrigens Linq für Abfragen auf den Ergebnissen verwenden.

Ich habe die Methode contacts_SearchCompleted unseres obigen Beispiels zur Demonstration so überarbeitet, dass einige Eigenschaften des ersten gefundenen Kontakts in lokalen Variablen gespeichert werden. In Ihrer Anwendung werden Sie diese Daten wahrscheinlich anzeigen, speichern oder sonst weiterverarbeiten. Die neue Methode, mit einigen der Eigenschaften eines Kontakts, sieht damit so aus:

void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
{
   ContactList.ItemsSource = e.Results;

   //Grabbing the first record of our result.
   Contact patientZero = e.Results.FirstOrDefault();

   string firstname = patientZero.CompleteName.FirstName;
   string lastname = patientZero.CompleteName.LastName;

   string emailaddress = patientZero.EmailAddresses.FirstOrDefault().EmailAddress;
   string phonenumber = patientZero.PhoneNumbers.FirstOrDefault().PhoneNumber;

   bool isPinnedToStart = patientZero.IsPinnedToStart;
} 

Das obige Beispiel ist nur ein kleiner Ausschnitt der Daten, die Sie über einen Kontakteintrag abfragen können. Je nach Bedürfnis sollten Sie eigentlich alles finden. Hier ist eine Liste der verfügbaren Informationen (vorausgesetzt, der Kontakteintrag hat auch einen Wert für das entsprechende Feld).

  • Accounts – Die Liste der Accounts mit denen der Kontakt verbunden ist. Die Accounts sind beispielsweise Facebook, Windows Live, Outlook, usw.
  • Addresses – Die Liste der Adressen für den Kontakt. Jede Adresse hat einen „Account“ (von oben), aus dem sie stammt, was für eine Art von Adresse es ist (Arbeit, Zuhause, usw.) und schlussendlich ein Feld PhysicalAddress, in dem die tatsächliche Adresse, also Address1, City, StateProvince, CountryRegion, usw. steht.
  • Birthdays – Ich weiß nicht, warum ein Kontakt mehrere Geburtstage haben sollte, aber jeder „Account“ hat möglicherweise ein unterschiedliches Datum – also bekommt man eine Liste. Falls das Jahr nicht angegeben ist, bekommt man hier den Wert „1“ zurück.
  • Children – Eine einfache Liste von Strings, welche die Namen der Kinder aus dem Kontakt beinhaltet.
  • Companies – wieder angereichert um die Account Informationen beinhaltet dieses Feld Werte wie JobTitle, CompanyName und OfficeLocation.
  • CompleteName – diesen Wert haben wir im obigen Codebeispiel verwendet. Er enthält alle Eigenschaften des Kontaktnamens, wie z.B. Title, Suffix, MiddleName, etc.
  • DisplayName – ein einfacher String für den Anzeigenamen des Kontakts.
  • EmailAddresses – Eine Liste von Email Adressen, angereichert um „Account“ und „Kind“.
  • IsPinnedToStart – dies ist ein interessanter Boolscher Wert, da er eine Aussage darüber enthält, welche Kontakte dem Anwender am wichtigsten sind (wenn man davon ausgeht, dass der Anwender seine wichtigsten Kontakte auf die Startseite gepinnt hat).
  • Notes – Eine Liste von Strings, die der Anwender als Notizen zu dem Kontakt angegeben hat.
  • PhoneNumbers – eine Liste der Telefonnummern des Kontakts, wieder angereichert um „Account“ und „Kind“.
  • SignificantOthers – man sollte meinen, dass es auch hier nur einen Wert gibt, aber Sie wissen ja wie das ist… Wie auch immer, dieses Feld enthält, ähnlich wie die Children Eigenschaft, eine Liste von Strings.
  • Websites – Eine Liste von Webseiten für den Kontakt in Form einer Liste von Strings.

Zu guter Letzt möche ich sichergehen, dass Sie einen optischen Eindruck der Anwendung haben. Hier ist mal wieder ein kleines Video der Anwendung die wir gerade gebaut haben:

Zusammenfassung

Ich denke, mit diesem Artikel haben Sie einen ganz guten Einstieg für die Verwendung der Contacts API auf einem Windows Phone. Der gesamte Namespace ist nur auf Windows Phone 7.5 verfügbar. Wenn Sie also eine Anwendung für Windows Phone 7.0 entwickeln, werden Sie diese API nicht verwenden können (aber wer entwickelt schon noch 7.0 Apps).

Um eine lauffähige Windows Phone Solution mit dem gesamten Beispielcode dieses Artikels herunterzuladen, klicken Sie auf den Download Code Button:

Morgen beschäftigen wir uns mit der zweiten Hälfte des UserData Namespaces, nämlich mit der Calendar API. Auch diese ist nur lesbar, erlaubt Ihnen aber ein paar Einblicke in die Verfügbarkeit des Anwenders zu bestimmten Zeiten.

Bis dahin!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.