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

31 Tage Mango | Tag #9: Kalender API

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

Der Originalartikel befindet sich hier: Day #9: Calendar API.

Heute beschäftigen wir uns noch mal mit dem Microsoft.Phone.UserData Namespace. Im heutigen Artikel schauen wir uns an, wie wir den Kalender des Anwenders verwenden können und wofür.

Wichtig bei diesen Daten ist, dass sie, wie die Kontakte, nur lesbar sind. Wir sind nicht in der Lage, neue Termine zum Kalender des Anwenders hinzuzufügen. Wir können aber sehen, wann der Anwender verfügbar ist.

Den Kalender verwenden

Einer der offensichtlichen Anwendungsfälle für die Kalenderdaten ist in einer Terminplanungsanwendung. Stellen Sie sich vor, Sie bauen eine Anwendung für eine Arztpraxis und eine der Funktionen ist: „Vereinbare einen Termin beim Arzt“. Indem Sie den Kalender des Benutzers mit dem Kalender des Arztes abgleichen, sollten sie einfach in der Lage sein, einen Termin vorzuschlagen, der für beide passt.

Ein weiteres Anwendungsbeispiel ist Kalendersynchronisierung. Meine Frau und ich haben zum Beispiel beide ein Windows Phone und wir nutzen beide den Windows Live Kalender für unsere Termine. Mit zwei Kindern können Sie sich vorstellen, dass wir ziemlich rumjonglieren müssen, damit jeder zur rechten Zeit am rechten Fleck ist. Die meisten Termine unserer Kinder werden von meiner Frau organisiert, also stehen diese in ihrem Kalender. Mein Kalender dreht sich hauptsächlich um meine Arbeit, meine Reisen, User Group Meetings und meine sportlichen Aktivitäten. Unsere Kalender sind nicht verbunden. Sie sind nicht synchronisiert. Momentan haben wir keine andere Möglichkeit, als beide Kalender bei beiden Telefonen hinzuzufügen. Damit ist es immerhin möglich, dass ich keine Reise während eines wichtigen Familienereignisses plane und sie keine Termine plant, wenn ich nicht da bin. Eine Anwendung die beide Kalender vereint, wäre für mich eine sehr willkommene Erweiterung meines Windows Phones.

Die Benutzeroberfläche

Jetzt, wo wir einige mögliche Anwendungsfälle diskutiert haben, wollen wir uns ansehen wie man das eigentlich macht. Ich fange gerne mit der Benutzeroberfläche an, da ich dadurch besser verstehe, welche Daten ich eigentlich auf dem Bildschirm brauche oder möchte. Für die heutige Anwendung verwenden wir das DatePicker Control aus dem Silverlight Toolkit for Windows Phone. Wir verwenden es, um eine Liste von Terminen für einen bestimmten Tag anzuzeigen.

Falls Sie das Silverlight Toolkit for Windows Phone noch nicht kennen, sollten Sie sich dieses unbedingt anschauen. Es handelt sich um eine kostenlose Sammlung von Controls für Windows Phone Anwendungen auf Codeplex. Ich habe das Silverlight Toolkit in Day #21 der 31 Days of Windows Phone näher behandelt. Sie sollten mit diesen Inhalten vertraut sein, bevor wir weitermachen. Es gibt einige Feinheiten zum DatePicker in diesem Artikel, die Sie wissen sollten.

Unsere Oberfläche wird dieses Mal ziemlich einfach sein. Wir werden einen DatePicker haben, mit dem der Anwender ein Datum auswählen kann und eine ListBox, welche die Kalendereinträge für dieses Datum anzeigt. Wie schon erwähnt, können wir mit dieser API keine Termine hinzufügen oder verändern, wir werden die Termine dem Benutzer also nur anzeigen.

Im unten stehenden XAML habe ich wieder die Standardvorlage für Windows Phone Anwendungen verwendet. Die einzigen Änderungen gegenüber der Vorlage sind der DatePicker, die ListBox und einige TextBlocks, welche ich nach dem Codebeispiel beschreiben werde. Bitte beachten Sie auch die Zeile xmlns:toolkit für den Silverlight Toolkit for Windows Phone Namespace oben in der Seite. Wir brauchen diesen Namespace um den DatePicker verwenden zu können.

<phone:PhoneApplicationPage 
   x:Class="Day9_CalendarAPI.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" 
    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" 
   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 #9" Style="{StaticResource PhoneTextNormalStyle}"/> 
            <TextBlock x:Name="PageTitle" Text="calendar api" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> 
        </StackPanel> 

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> 
            <toolkit:DatePicker x:Name="DateBox" Height="100" Margin="0,37,0,470" ValueChanged="DateBox_ValueChanged" /> 
            <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,6,0,0" Name="textBlock1" Text="select a date" VerticalAlignment="Top" Width="126" /> 
            <ListBox x:Name="DateList" Margin="0,143,0,0"> 
                <ListBox.ItemTemplate> 
                    <DataTemplate> 
                        <StackPanel> 
                            <TextBlock Text="{Binding StartTime}" FontSize="16" /> 
                            <TextBlock Text="{Binding Subject}" TextWrapping="NoWrap" FontWeight="Bold" FontSize="24" /> 
                            <TextBlock Text="{Binding Location}" TextWrapping="NoWrap" Margin="0,0,0,15" FontSize="20" Foreground="Gray"/> 
                        </StackPanel> 
                    </DataTemplate> 
                </ListBox.ItemTemplate> 
            </ListBox> 
            <TextBlock Height="30" HorizontalAlignment="Left" Margin="70,107,0,0" Name="MessageText" VerticalAlignment="Top" Width="380" TextAlignment="Right" /> 
        </Grid> 
    </Grid> 
</phone:PhoneApplicationPage>

Der DatePicker ist ein ziemlich einfach zu benutzen. Er verwendet automatisch das heutige Datum wenn Sie keins angeben (was für unsere Zwecke völlig aureicht). Weiterhin hat er einen Event ValueChanged, den wir nutzen um bei Änderung des gewählten Datums eine neue Kalendersuche anzustoßen.

Die beiden TextBlocks sind eigentlich nur Verzierung. Im zweiten TextBlock zeigen wir an, wieviele Einträge wir gefunden haben. Wir werden dazu kommen, wenn wir in die code-behind Datei schauen.

Schließlich haben wir noch die ListBox. Das heutige Beispiel ist etwas ausführlicher als das gestrige, da wir einen etwas aufwändigerern DataTemplate in der Liste verwenden. Wir binden diesen wieder an Eigenschaften unserer Appointment Objekte, welche wir in unserem Code bekommen. Hier ist eine kleine Vorschau auf die Benutzeroberfläche.

Wieder einmal habe ich auch ein kleines Video der laufenden Anwendung auf meinem Gerät vorbereitet. Schauen Sie sich das Video an, wenn Sie das Endergebnis des heutigen Artikels sehen möchten!

Die Calendar API verwenden

Auch wenn unser Beispiel ziemlich einfach ist, werden wir am Ende eine ziemlich robuste Anwendung für unseren Anwender erstellt haben. Als erstes machen wir den Namespace Microsoft.Phone.UserData per using Statement bekannt. Wenn wir das haben, erstellen wir ein neues Appointment Objekt zum Zugriff auf die Kalenderdaten unseres Anwenders.

Hier ist unser erstes Codebeispiel:

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

namespace Day9_CalendarAPI 
{ 
   public partial class MainPage : PhoneApplicationPage 
   { 
      Appointments appointments = new Appointments(); 

      public MainPage() 
      { 
         InitializeComponent(); 
         appointments.SearchCompleted += new EventHandler<AppointmentsSearchEventArgs>(appointments_SearchCompleted); 
         appointments.SearchAsync(DateBox.Value.Value, DateBox.Value.Value.AddDays(1), null); 
      } 

      void appointments_SearchCompleted(object sender, AppointmentsSearchEventArgs e) 
      { 
         //SEARCH THE CALENDAR! 
      } 
   } 
} 

Wie Sie im obigen Beispiel sehen, erstellen wir einen SearchCompleted Event Handler und rufen dann SearchAsync() auf, um unsere Suche durchzuführen. Die Methode SearchAsync() erwartet drei Parameter:

  • StartDate – Ein DateTime Wert, welcher den Anfang unserer Suche repräsentiert. Dieser Wert ist einschließlich, d.h. wenn Sie hier DateTime.Now angeben, werden Sie alle Termine ab inklusive heute bekommen.
  • EndDate – Wieder ein einschließlicher Wert mit dem wir das Endedatum der Suche angeben.
  • State – Ein benutzerdefiniertes Objekt, welches wir der Suche übergeben können. Es wird nach Abschluss der Suche dem Event Handler mitgegeben. Sie können dieses Objekt nutzen um eine einzelne Suchanfrage später zu identifizieren. Einfache Anwendungen wie unsere heutige übergeben hier einfach null als Wert.

In unserem XAML oben haben wir einen DatePicker der Oberfläche hinzugefügt. Sie werden im obigen Codebeispiel wahrscheinlich den Code vermisst haben, der die Auswahl des Anwenders berücksichtigt und die Suchergebnisse an die ListBox bindet. Diese zwei Schritte müssen wir noch erledigen, damit die Anwendung so funktioniert wie in dem Video oben.

Für den DatePicker estellen wir eine Event Handler Methode für den ValueChanged Event, den wir im XAML schon registriert haben. Da wir die Methode SearchAsync() möglicherweise von mehreren Stellen in unserem Code aufrufen werden, lassen wir unsere ValueChanged Event Handler Methode eine neue Methode SearchCalendar() aufrufen, in welcher wir den tatsächlichen Aufruf SearchAsync() durchführen (ich habe deshalb den Aufruf von SearchAsync() aus dem MainPage Konstruktur entfernt).

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

namespace Day9_CalendarAPI 
{ 
   public partial class MainPage : PhoneApplicationPage 
   { 
      Appointments appointments = new Appointments(); 

      public MainPage() 
      { 
         InitializeComponent(); 
         appointments.SearchCompleted += new EventHandler<AppointmentsSearchEventArgs>(appointments_SearchCompleted); 
         SearchCalendar(); 
      } 

      private void SearchCalendar() 
      { 
         appointments.SearchAsync(DateBox.Value.Value, DateBox.Value.Value.AddDays(1), null); 
      } 

      private void DateBox_ValueChanged(object sender, DateTimeValueChangedEventArgs e) 
      { 
         SearchCalendar(); 
      } 

      void appointments_SearchCompleted(object sender, AppointmentsSearchEventArgs e) 
      { 
         if (e.Results.Count() == 0) 
         { 
            MessageText.Text = "no events for the selected day"; 
         } 
         else 
         { 
            MessageText.Text = e.Results.Count() + " events found"; 
            DateList.ItemsSource = e.Results; 
         } 
      } 
   } 
} 

Zum Schluss sollten Sie sich die Methode appointments_SearchCompleted() ansehen. Diese wird aufgerufen, wenn die Ergebnisse unseres SearchAsync() Aufrufs vorliegen. Die Ergebnisse bekommen wir als Parameter mitgeliefert. In der Beispielanwendung schaue ich nach, wieviele Einträge gefunden wurden und gebe das Ergebnis im MessageText TextBlock aus. Zuletzt binde ich die Ergebnisse der Suche an unsere DateList ListBox – vorausgesetzt, es wurden Einträge gefunden.

Die Geheimnisse des Databindings

Für diejenigen unter Ihnen, die gerade erst mit der XAML Entwicklung beginnen, mag die folgende Zeile etwas geheimnisvoll wirken:

DateList.ItemsSource = e.Results;

Es mag so wirken, als ob hier Zauberei geschieht und in gewisser Weise stimmt das sogar. Wenn Sie sich noch an unser XAML erinnern, sehen Sie ein relativ ausführliches ListBox Control:

<ListBox x:Name="DateList" Margin="0,143,0,0"> 
    <ListBox.ItemTemplate> 
        <DataTemplate> 
            <StackPanel> 
                <TextBlock Text="{Binding StartTime}" FontSize="16" /> 
                <TextBlock Text="{Binding Subject}" TextWrapping="NoWrap" FontWeight="Bold" FontSize="24" /> 
                <TextBlock Text="{Binding Location}" TextWrapping="NoWrap" Margin="0,0,0,15" FontSize="20" Foreground="Gray"/> 
            </StackPanel> 
        </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

Im DataTemplate für die ListBox habe ich drei TextBlock Controls definiert. Jeder dieser TextBlocks hat ein {Binding} für den Wert seiner Text Eigenschaft. Für uns als Entwickler bedeutet das, dass wir einzelne Eigenschaften unserer Datenobjekte nehmen und deren Werte an unsere XAML Objekte binden können.

Diese Fähigkeit ist natürlich nicht auf Text Werte beschränkt. Sie können beinahe jede Eigenschaft eines XAML Elements an Ihre Datenobjekte binden. Zahlenwerte können für die Opacity Eigenschaft, Farben für Foreground und Fill usw. verwendet werden. Der einzige echte Nachteil des Databindings in XAML ist, dass Ihre Seite kaputt gehen wird, wenn Sie die Struktur Ihrer Datenobjekte ändern (stellen Sie sich vor, sie entfernen Subject aus der Appointment Klasse). Insgesamt ist dies aber eine hervorragende Möglichkeit, mit wenig Code eine Collection von Objekten an eine ListBox zu binden.

Zusammenfassung

In diesem Artikel haben wir Schritt für Schritt eine Anwendung entwickelt, welche Daten aus dem Kalender des Anwenders ausliest. Wie eingangs erwähnt, gibt es eine Vielzahl von Anwendungsfällen für diese API – von der Arztpraxis bis hin zu Synchronisierungswerkzeugen.

Um ein komplett lauffähiges Windows Phone Projekt mit dem ganzen Beispielcode herunterzuladen, klicken Sie auf den Download Code Button:

Morgen werden wir in den Namespace NetworkInformation eintauchen und ein Beispiel entwickeln, wie und warum man diese Daten benutzt.

Bis dahin!

Schreibe einen Kommentar

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