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

31 Tage Mango | Tag #8: Kontakte API

Die­ser Arti­kel ist Tag #8 der Serie 31 Tage Man­go von Jeff Blan­ken­burg.

Der Ori­gi­nal­ar­ti­kel befin­det sich hier: Day #8: Con­tac­ts API.

Wenn Sie die Anwen­dung zu die­sem Arti­kel auf Ihr Gerät her­un­ter­la­den möch­ten, fin­den Sie die­se auch im Win­dows Pho­ne Mar­ket­place.

Heu­te befas­sen wir uns mit einem neu ver­füg­ba­ren Name­space: Microsoft.Phone.UserData. In der Serie 31 Days of Win­dows Pho­ne habe ich die­ses The­ma in Day #8 cal­led Choo­sers behan­delt. Mit die­sen Tasks waren wir in der Lage, den Anwen­der zur Wahl eines Ein­trags aus sei­nen Kon­tak­ten auf­zu­for­dern. Damit konn­ten wir eine bestimm­te Eigen­schaft des Kon­takts abfra­gen: Eine Email Adres­se oder eine Tele­fon­num­mer. Bei­des gleich­zei­tig ging nicht. Außer­dem konn­ten wir immer nur genau einen Ein­trag bekom­men.

Mit dem neu­en Name­space in Win­dows Pho­ne 7.5 kön­nen wir die Kon­tak­te des Anwen­ders wie eine loka­le Daten­bank ver­wen­den. Wir kön­nen dar­in suchen, meh­re­re Kon­tak­te abfra­gen und auf alle Infor­ma­tio­nen zu den Kon­tak­ten zugrei­fen. Die­se Schnitt­stel­le ist mäch­ti­ger, erfor­dert von uns aber mehr manu­el­len Auf­wand.

Wie schon Peter Par­kers Onkel Ben gesagt hat: „Aus gro­ßer Kraft folgt gro­ße Ver­ant­wor­tung“. Nur weil Sie die Mög­lich­keit haben, auf die Kon­tak­te des Anwen­ders zuzu­grei­fen, bedeu­tet das nicht, dass Sie die­se Mög­lich­keit aus­nut­zen soll­ten. Damit ein Anwen­der Ihre Anwen­dung auf sei­nem Gerät instal­liert, muss er Ihnen ver­trau­en kön­nen. Wenn Sie die­ses Ver­trau­en ent­täu­schen indem Sie alle sei­ne Freun­de und Kon­tak­te mit Email und ande­rem Müll über­flu­ten, wird Ihre Anwen­dung ers­tens schnell deinstal­liert und zwei­tens kön­nen Sie sich nega­ti­ver Beur­tei­lun­gen im Mar­ket­place sicher sein.

Der Zugriff auf die Daten ist nur LESBAR. Wir kön­nen kei­ne Kon­tak­te ver­än­dern, löschen und nicht mal etwas hin­zu­fü­gen. Die hier vor­ge­stell­te API dient nur dazu, Kon­tak­te zu fin­den und die­se Daten in unse­rer eige­nen Anwen­dung zu ver­wen­den.

Die Benutzeroberfläche

Wie in allen Arti­keln unse­rer Serie brau­chen wir zunächst eine Benut­zer­ober­flä­che. In die­sem Bei­spiel bin ich von der Stan­dard­vor­la­ge für Win­dows Pho­ne Anwen­dun­gen aus­ge­gan­gen. Ich habe eine List­Box hin­zu­ge­fügt und bereits ein Bin­ding ange­ge­ben, wel­ches wir spä­ter ver­wen­den wer­den. Wenn Sie mehr über Data Bin­ding in XAML erfah­ren möch­ten, kön­nen Sie Day #13 of the 31 Days of Sil­ver­light lesen. Wei­ter­hin habe ich eine Text­Box hin­zu­ge­fügt, die wir zur Suche in den Kon­tak­ten ver­wen­den wer­den.

Hier ist das XAML unse­rer Anwen­dung:

<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 wesent­li­chen Tei­le im XAML sind ers­tens unse­re List­Box mit dem Namen „Con­tact­List“ und der Text­Block inner­halb des DataTem­pla­te. Wir haben den Wert der Text Eigen­schaft des Text­Blocks an Dis­play­Na­me gebun­den. Dies wird eine der Eigen­schaf­ten sein, die wir zu jedem Kon­takt bekom­men. Zwei­tens haben wir eine Text­Box mit den Namen „Search­Term“ hin­zu­ge­fügt. Wir wer­den die Ein­ga­be in der Text­Box als Such­kri­te­ri­um ver­wen­den, indem wir den Text­Ch­an­ged Event imple­men­tie­ren. Unse­re Ober­flä­che soll­te am Ende etwa so aus­se­hen:

Kontakte suchen

Als ers­tes müs­sen wir in unse­rer code-behind Datei ein using State­ment für den Name­space Microsoft.Phone.UserData hin­zu­fü­gen.

Wir begin­nen mit dem Abfan­gen der Text­än­de­run­gen unse­rer Text­Box. Den Event Hand­ler im XAML haben wir schon defi­niert — jetzt brau­chen wir noch die Event Hand­ler Metho­de. Hier ist unser anfäng­li­cher C# Code mit dem Event Hand­ler:

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 Metho­de SearchTerm_TextChanged wird jedes Mal auf­ge­ru­fen, wenn sich der Inhalt unse­rer Text­Box ändert. Die eigent­li­che Suche nach Kon­tak­ten habe ich in eine sepa­ra­te Metho­de aus­ge­la­gert. Search­Con­tac­ts nimmt einen String als Argu­ment und ver­wen­det die­sen Wert für die eigent­li­che Suche.

Um die eigent­li­che Suche in den Daten durch­zu­füh­ren, müs­sen wir eine neue Instanz der Con­tac­ts Klas­se anle­gen (hier­zu brau­chen wir den Microsoft.Phone.UserData Name­space). Die Suche in den Kon­takt­da­ten gschieht asyn­chron. Die Con­tac­ts Klas­se hat einen Search­Com­ple­ted Event Hand­ler, den wir ver­wen­den, um auf die Ergeb­nis­se der Suche zuzu­grei­fen wenn die­se abge­schlos­sen ist. Um die Suche mit unse­ren Kri­te­ri­en zu star­ten ver­wen­den wir die Sear­chA­sync(). Wer­fen wir einen Blick auf den resul­tie­ren­den C# Code. Danach gehe ich auf die Such­pa­ra­me­ter ein, die wir ver­wen­den kön­nen.

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 Metho­de Search­Con­tac­ts() im Kon­struk­tor mit dem Para­me­ter string.Empty auf. Durch Über­ga­be des string.Empty sagen wir der Metho­de Sear­chA­sync(), dass wir ALLE Kon­tak­te haben wol­len. Im Emu­la­tor macht die­ser Auf­ruf kei­ne Pro­ble­me (er hat nur 7 Kon­tak­te). Auf einem tat­säch­li­chen Tele­fon eines Anwen­ders mit Tau­sen­den von Kon­tak­ten kann das Laden aber eini­ge Sekun­den dau­ern. Sie soll­ten für die­sen Fall die Ver­wen­dung einer Pro­gress­Bar in Erwä­gung zie­hen oder dem Anwen­der ander­wei­tig anzei­gen, dass die Anwen­dung gera­de Daten lädt.

Wie Sie an der Signa­tur der Metho­de Contacts.SearchAsync() sehen, erwar­tet die­se drei Para­me­ter:

  • string — das ist das Such­kri­te­ri­um. In unse­rem Bei­spiel wird es mit dem Inhalt der Text­Box aus unse­rer XAML Datei belegt.
  • Fil­ter­Kind — hier­mit kön­nen Sie ange­ben, was Sie suchen möch­ten. Mög­li­che Wer­te sind Pho­neN­um­ber, Dis­play­Na­me oder Email­Ad­dress. Wenn Sie den Wert None ange­ben, wird der String des Such­kri­te­ri­ums igno­riert.
  • object — mit die­sem Para­me­ter kön­nen Sie dem Such­auf­ruf ein belie­bi­ges Objekt mit­ge­ben, so dass Sie, wenn die Ergeb­nis­se vor­lie­gen, noch wis­sen, wo die­se her kamen. Das kann bei­spiels­wei­se ein ein­fa­cher String sein bis hin zu einem kom­ple­xen Objekt, wel­ches Sie bei der Ver­ar­bei­tung der Ergeb­nis­se benö­ti­gen.

Wenn Sie Ihre Anwen­dung jetzt aus­füh­ren, soll­ten Sie sehen, dass die Ergeb­nis­se abhän­gig von der Ein­ga­be in der Text­Box gefil­tert wer­den. Die gefil­ter­te Lis­te im Win­dows Pho­ne Emu­la­tor soll­te etwa so aus­se­hen:

Die Kontaktdaten

Bis­her haben wir die Kon­tak­te auf dem Gerät erfolg­reich abge­fragt und in einer List­Box ange­zeigt. Was noch fehlt, ist, wel­che Daten wir über­haupt bekom­men. Da wir die Daten in Form eines IEnu­me­ra­ble bekom­men, kön­nen wir übri­gens Linq für Abfra­gen auf den Ergeb­nis­sen ver­wen­den.

Ich habe die Metho­de contacts_SearchCompleted unse­res obi­gen Bei­spiels zur Demons­tra­ti­on so über­ar­bei­tet, dass eini­ge Eigen­schaf­ten des ers­ten gefun­de­nen Kon­takts in loka­len Varia­blen gespei­chert wer­den. In Ihrer Anwen­dung wer­den Sie die­se Daten wahr­schein­lich anzei­gen, spei­chern oder sonst wei­ter­ver­ar­bei­ten. Die neue Metho­de, mit eini­gen der Eigen­schaf­ten eines Kon­takts, 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 obi­ge Bei­spiel ist nur ein klei­ner Aus­schnitt der Daten, die Sie über einen Kon­takt­ein­trag abfra­gen kön­nen. Je nach Bedürf­nis soll­ten Sie eigent­lich alles fin­den. Hier ist eine Lis­te der ver­füg­ba­ren Infor­ma­tio­nen (vor­aus­ge­setzt, der Kon­takt­ein­trag hat auch einen Wert für das ent­spre­chen­de Feld).

  • Accounts — Die Lis­te der Accounts mit denen der Kon­takt ver­bun­den ist. Die Accounts sind bei­spiels­wei­se Face­book, Win­dows Live, Out­look, usw.
  • Addres­ses — Die Lis­te der Adres­sen für den Kon­takt. Jede Adres­se hat einen „Account“ (von oben), aus dem sie stammt, was für eine Art von Adres­se es ist (Arbeit, Zuhau­se, usw.) und schluss­end­lich ein Feld Phy­si­calAd­dress, in dem die tat­säch­li­che Adres­se, also Address1, City, State­Pro­vin­ce, Coun­try­Re­gi­on, usw. steht.
  • Bir­th­days — Ich weiß nicht, war­um ein Kon­takt meh­re­re Geburts­ta­ge haben soll­te, aber jeder „Account“ hat mög­li­cher­wei­se ein unter­schied­li­ches Datum — also bekommt man eine Lis­te. Falls das Jahr nicht ange­ge­ben ist, bekommt man hier den Wert „1“ zurück.
  • Child­ren — Eine ein­fa­che Lis­te von Strings, wel­che die Namen der Kin­der aus dem Kon­takt beinhal­tet.
  • Com­pa­nies — wie­der ange­rei­chert um die Account Infor­ma­tio­nen beinhal­tet die­ses Feld Wer­te wie Job­Tit­le, Com­pany­Na­me und Office­Lo­ca­ti­on.
  • Com­ple­te­Na­me — die­sen Wert haben wir im obi­gen Code­bei­spiel ver­wen­det. Er ent­hält alle Eigen­schaf­ten des Kon­takt­na­mens, wie z.B. Tit­le, Suf­fix, Midd­le­Na­me, etc.
  • Dis­play­Na­me — ein ein­fa­cher String für den Anzei­gena­men des Kon­takts.
  • Email­Ad­dres­ses — Eine Lis­te von Email Adres­sen, ange­rei­chert um „Account“ und „Kind“.
  • IsPin­ned­ToStart — dies ist ein inter­es­san­ter Bool­scher Wert, da er eine Aus­sa­ge dar­über ent­hält, wel­che Kon­tak­te dem Anwen­der am wich­tigs­ten sind (wenn man davon aus­geht, dass der Anwen­der sei­ne wich­tigs­ten Kon­tak­te auf die Start­sei­te gepinnt hat).
  • Notes — Eine Lis­te von Strings, die der Anwen­der als Noti­zen zu dem Kon­takt ange­ge­ben hat.
  • Pho­neN­um­bers — eine Lis­te der Tele­fon­num­mern des Kon­takts, wie­der ange­rei­chert um „Account“ und „Kind“.
  • Signi­fi­can­tO­thers — man soll­te mei­nen, dass es auch hier nur einen Wert gibt, aber Sie wis­sen ja wie das ist… Wie auch immer, die­ses Feld ent­hält, ähn­lich wie die Child­ren Eigen­schaft, eine Lis­te von Strings.
  • Web­sites — Eine Lis­te von Web­sei­ten für den Kon­takt in Form einer Lis­te von Strings.

Zu guter Letzt möche ich sicher­ge­hen, dass Sie einen opti­schen Ein­druck der Anwen­dung haben. Hier ist mal wie­der ein klei­nes Video der Anwen­dung die wir gera­de gebaut haben:

Zusammenfassung

Ich den­ke, mit die­sem Arti­kel haben Sie einen ganz guten Ein­stieg für die Ver­wen­dung der Con­tac­ts API auf einem Win­dows Pho­ne. Der gesam­te Name­space ist nur auf Win­dows Pho­ne 7.5 ver­füg­bar. Wenn Sie also eine Anwen­dung für Win­dows Pho­ne 7.0 ent­wi­ckeln, wer­den Sie die­se API nicht ver­wen­den kön­nen (aber wer ent­wi­ckelt schon noch 7.0 Apps).

Um eine lauf­fä­hi­ge Win­dows Pho­ne Solu­ti­on mit dem gesam­ten Bei­spiel­code die­ses Arti­kels her­un­ter­zu­la­den, kli­cken Sie auf den Down­load Code But­ton:

Mor­gen beschäf­ti­gen wir uns mit der zwei­ten Hälf­te des User­Da­ta Name­spaces, näm­lich mit der Calen­dar API. Auch die­se ist nur les­bar, erlaubt Ihnen aber ein paar Ein­bli­cke in die Ver­füg­bar­keit des Anwen­ders zu bestimm­ten Zei­ten.

Bis dahin!

Schreibe einen Kommentar

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