Walkthrough: Accessing contact and calendar data for Windows Phone 8

[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]

 

In this walkthrough, you build a simple contacts and calendar app. The app that you create in this walkthrough displays contacts, appointments, and the accounts that the data comes from. To download a completed version of this app, see Contacts and Calendar Sample.

This topic discusses read-only access to user’s contact data. For information on creating a custom contact store for your app that provides read and write access, see Custom contact store for Windows Phone 8.

In this walkthrough, you perform the following tasks.

  • Access contact and calendar data.

  • Bind contacts, appointments, and accounts to the user interface.

  • Display details for individual contacts (including photo) and appointments.

  • Data-bind contact photos to the user interface by creating a custom data converter.

The completed app looks like the following.

This topic contains the following sections.

 

Prerequisites

To complete this walkthrough, you must have the Windows Phone SDK installed. For more information, see Windows Phone 8 SDK tools.

Creating the app project

First, you create a new Windows Phone app project named ContactsAndCalendarTestApp. In later steps, you will add code to your app that assumes the app name is ContactsAndCalendarTestApp. If you choose a different name for your app, you must change the namespace references in the code.

To create the app project

  1. In Visual Studio, on the File menu, point to New and then click Project.

    The New Project dialog appears.

  2. In the left pane, click Installed Templates, expand Visual C# or Visual Basic, and then click Windows Phone.

  3. In the list of project types, click **Windows Phone App **.

  4. In the Name box, type ContactsAndCalendarTestApp.

  5. Click OK.

    The Windows Phone platform selection dialog box appears.

  6. In the Target Windows Phone Version drop-down list, select Windows Phone OS 7.1 and then click OK.

    The new app project is created and opens in Visual Studio.

Setting Up the app structure

This app contains a main page with three pivot items, one each for contacts, appointments, and accounts. There are also two additional pages for the contact and appointment details.

To set up the app structure

  1. In Solution Explorer, select MainPage.xaml and then press the Delete key to delete it.

  2. In Solution Explorer, right-click the project, point to Add, and then click New Item.

    The Add New Item dialog appears.

  3. Select Windows Phone Pivot Page, enter the name MainPage.xaml, and then click Add.

    The new pivot page opens in the designer.

  4. In the XAML code, locate the GRID element named LayoutRoot. Replace everything from there to the end of the file with the following code. This creates the three pivot items that you will fill in later.

        <!--LayoutRoot is the root grid where all page content is placed-->
        <Grid x:Name="LayoutRoot" Background="Transparent">
    
            <!--Pivot Control-->
            <controls:Pivot Title="Contacts and Calendar Test App" >
    
                <!--Pivot item one-->
                <controls:PivotItem Header="contacts">
                </controls:PivotItem>
    
    
                <!--Pivot item two-->
                <controls:PivotItem Header="accounts">
                </controls:PivotItem>
    
    
                <!--Pivot item three-->
                <controls:PivotItem Header="appointments">
                </controls:PivotItem>
    
            </controls:Pivot>
        </Grid>
    </phone:PhoneApplicationPage>
    
  5. In Solution Explorer, right-click the project, point to Add, and then click New Item.

    The Add New Item dialog appears.

  6. Select Windows Phone Portrait Page, enter the name AppointmentDetails.xaml, and then click Add.

    The new page opens in the designer.

  7. In Solution Explorer, right-click the project, point to Add, and then click New Item.

    The Add New Item dialog appears.

  8. Select Windows Phone Portrait Page, enter the name ContactDetails.xaml, and then click Add.

    The new page opens in the designer.

  9. In Solution Explorer, right-click App.xaml and then click View Code.

    The code-behind file opens in the code editor.

  10. At the top of the class, add the following code. This code creates two global static variables that you use to pass data between the main page and the detail pages.

    public static Microsoft.Phone.UserData.Contact con;
    public static Microsoft.Phone.UserData.Appointment appt;
    
    Public Shared con As Microsoft.Phone.UserData.Contact
    Public Shared appt As Microsoft.Phone.UserData.Appointment
    
  11. (Optional) Disable the frame rate counters by commenting out the following code.

    Application.Current.Host.Settings.EnableFrameRateCounter = true;
    
    Application.Current.Host.Settings.EnableFrameRateCounter = True
    

Checkpoint 1

In this procedure, you run the app to test your progress.

To test the app

  1. On the File menu, click Save All. (Ctrl+Shift+S)

  2. On the Build (or the Debug menu), click Build Solution. (Ctrl+Shift+B)

  3. On the standard toolbar, set the deployment target of the app to Windows Phone Emulator or Windows Phone Device.

    Or

  4. On the Debug menu, click Start Debugging. (F5)

    The app starts and the main page appears.

  5. Swipe the pivot items. All three pivot items should appear with the headers contacts, accounts, and appointments.

  6. On the Debug menu, click Stop Debugging. (F5)

Accessing contact data

The process for accessing contact data is to get a reference to the Contacts object, perform an asynchronous search on it by calling SearchAsync, and then capture the results as a collection of Contact objects in the SearchCompleted event handler.

To create the contact pivot item UI

  1. In Solution Explorer, double-click MainPage.xaml to open it in the designer.

  2. Locate the first pivot item and replace it with the following code. This code creates the text box, radio buttons, and button that are used to search for contacts. It also creates the list box that contains the results of the search.

    <controls:PivotItem Header="contacts">
        <StackPanel Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
    
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
    
                <TextBox Grid.Row="0" Grid.ColumnSpan="2" Name="contactFilterString" />
                <RadioButton Grid.Row="1" Grid.Column="0" Checked="FilterChange" Name="name" Content="name" />
                <RadioButton Grid.Row="1" Grid.Column="1" Checked="FilterChange" Name="phone" Content="phone"/>
                <RadioButton Grid.Row="2" Grid.Column="0" Checked="FilterChange" Name="email" Content="email"/>
                <Button Grid.Row="2" Grid.Column="1" Content="search" Click="SearchContacts_Click" />
            </Grid>
    
            <TextBlock Name="ContactResultsLabel" Text="Search for contacts" TextWrapping="Wrap" Margin="12,0,0,0" />
    
            <ListBox Name="ContactResultsData" ItemsSource="{Binding}" Tap="ContactResultsData_Tap" Height="300" Margin="24,0,0,0" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Name="ContactResults" Text="{Binding Path=DisplayName, Mode=OneWay}" FontSize="{StaticResource PhoneFontSizeExtraLarge}" Margin="18,8,0,0" />
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
    </controls:PivotItem>
    

To access contact data

  1. In Solution Explorer, right-click MainPage.xaml and then click View Code.

    The code-behind file opens in the code editor.

  2. Replace the code with the following. This code sets the default contact search filter to none, and sets the default contact search type to name. It also contains the code to change the search filter when you select the radio buttons. For more information, see Contact filtering and matching for Windows Phone 8.

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using Microsoft.Phone.Controls;
    using Microsoft.Phone.UserData;
    
    namespace ContactsAndCalendarTestApp
    {
        public partial class MainPage : PhoneApplicationPage
        {
            FilterKind contactFilterKind = FilterKind.None;
    
            // Constructor
            public MainPage()
            {
                InitializeComponent();
    
                name.IsChecked = true;
            }
    
    
            private void FilterChange(object sender, RoutedEventArgs e)
            {
                String option = ((RadioButton)sender).Name;
    
                InputScope scope = new InputScope();
                InputScopeName scopeName = new InputScopeName();
    
                switch (option)
                {
                    case "name":
                        contactFilterKind = FilterKind.DisplayName;
                        scopeName.NameValue = InputScopeNameValue.Text;
                        break;
    
                    case "phone":
                        contactFilterKind = FilterKind.PhoneNumber;
                        scopeName.NameValue = InputScopeNameValue.TelephoneNumber;
                        break;
    
                    case "email":
                        contactFilterKind = FilterKind.EmailAddress;
                        scopeName.NameValue = InputScopeNameValue.EmailSmtpAddress;
                        break;
    
                    default:
                        contactFilterKind = FilterKind.None;
                        break;
                }
    
                scope.Names.Add(scopeName);
                contactFilterString.InputScope = scope;
                contactFilterString.Focus();
            }
        }//End page class
    }//End namespace
    
    Imports Microsoft.Phone.UserData
    
    Partial Public Class MainPage
        Inherits PhoneApplicationPage
    
        Dim contactFilterKind As FilterKind = FilterKind.None
    
    
        Public Sub New()
            InitializeComponent()
    
            name.IsChecked=True
        End Sub
    
    
        Private Sub FilterChange(sender As System.Object, e As System.Windows.RoutedEventArgs)
    
            Dim opt As String = (CType(sender,RadioButton)).Name
    
            Dim scope As InputScope = new InputScope()
            Dim scopeName As InputScopeName = new InputScopeName()
    
            Select Case opt
    
                Case "name"
                    contactFilterKind = FilterKind.DisplayName
                    scopeName.NameValue = InputScopeNameValue.Text
    
                Case "phone"
                    contactFilterKind = FilterKind.PhoneNumber
                    scopeName.NameValue = InputScopeNameValue.TelephoneNumber
    
                Case "email"
                    contactFilterKind = FilterKind.EmailAddress
                    scopeName.NameValue = InputScopeNameValue.EmailSmtpAddress    
    
                Case Else
                    contactFilterKind = FilterKind.None
            End Select
    
            scope.Names.Add(scopeName)
            contactFilterString.InputScope = scope
            contactFilterString.Focus()
        End Sub
    
    End Class'End page class
    
  3. After the constructor, add the following code. This code contains the search button click event, and starts the asynchronous contact search.

    private void SearchContacts_Click(object sender, RoutedEventArgs e)
    {
        ContactResultsLabel.Text = "results are loading...";
        ContactResultsData.DataContext = null;
    
        Contacts cons = new Contacts();
    
        cons.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(Contacts_SearchCompleted);
    
        cons.SearchAsync(contactFilterString.Text, contactFilterKind, "Contacts Test #1");
    }
    
    Private Sub SearchContacts_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
    
        ContactResultsLabel.Text = "results are loading..."
        ContactResultsData.DataContext = Nothing
    
        Dim cons As Contacts = New Contacts()
    
        AddHandler cons.SearchCompleted, AddressOf Contacts_SearchCompleted
    
        cons.SearchAsync(contactFilterString.Text, contactFilterKind, "Contacts Test #1")
    End Sub
    
  4. After the click event, add the following code. This code contains the method to handle the completed event of the asynchronous search. This code binds the contact data to the user interface by setting the data context of the list box equal to the results of the search. It includes error handling in case there are no results. The code also changes the text of the list box label depending on whether there are results or not.

    void Contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
    {
        //MessageBox.Show(e.State.ToString());
    
        try
        {
            //Bind the results to the list box that displays them in the UI
            ContactResultsData.DataContext = e.Results;
        }
        catch (System.Exception)
        {
            //That's okay, no results
        }
    
        if (ContactResultsData.Items.Count > 0)
        {
            ContactResultsLabel.Text = "results (tap name for details...)";
        }
        else
        {
            ContactResultsLabel.Text = "no results";
        }
    }
    
    Private Sub Contacts_SearchCompleted(sender As Object, e As ContactsSearchEventArgs)
    
        Try
            'Bind the results to the list box that displays them in the UI
            ContactResultsData.DataContext = e.Results
    
        Catch ex As System.Exception
    
            'That's okay, no results
        End Try
    
        If ContactResultsData.Items.Count > 0 Then
    
            ContactResultsLabel.Text = "results (tap name for details...)"
        Else
            ContactResultsLabel.Text = "no results"
        End If
    End Sub
    
  5. After the search completed event, add the following code. When you tap a contact in the results list, this code stores the contact in a global variable, and then navigates to the contact details page.

    private void ContactResultsData_Tap(object sender, GestureEventArgs e)
    {
        App.con = ((sender as ListBox).SelectedValue as Contact);
    
        NavigationService.Navigate(new Uri("/ContactDetails.xaml", UriKind.Relative));
    }
    
    Private Sub ContactResultsData_Tap(sender As System.Object , e As System.Windows.Input.GestureEventArgs)
    
        App.con = CType((CType(sender,ListBox)).SelectedValue, Contact)
    
        NavigationService.Navigate(new Uri("/ContactDetails.xaml", UriKind.Relative))
    End Sub
    

Displaying the contact details

When you tap a contact in the results list on the main page, the app navigates to the contact details page.

To display the contact details

  1. In Solution Explorer, double-click ContactDetails.xaml to open it in the designer.

  2. Locate the GRID element named LayoutRoot. Replace everything from there to the end of the file with the following code. This creates a text block for the contact’s name, an image for the contact’s photo, and list boxes to hold other contact data.

        <!--LayoutRoot is the root grid where all page content is placed-->
        <Grid x:Name="LayoutRoot" Background="Transparent">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
    
            <!--TitlePanel contains the name of the application and page title-->
            <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,0">
                <TextBlock x:Name="ApplicationTitle" Text="Contacts and Calendar Test App" Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock x:Name="PageTitle" Text="contact details" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
            </StackPanel>
    
            <!--ContentPanel - place additional content here-->
            <StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    
                <TextBlock Text="{Binding Path=DisplayName, Mode=OneWay}" Foreground="{StaticResource PhoneAccentBrush}" FontSize="{StaticResource PhoneFontSizeExtraLarge}" />
    
                <Border BorderThickness="2" HorizontalAlignment="Left" BorderBrush="{StaticResource PhoneAccentBrush}" >
                    <Image Name="Picture" Height="85" Width="85" HorizontalAlignment="Left" />
                </Border>
    
                <TextBlock Text="phone numbers" Margin="12,12,0,0"/>
                <ListBox ItemsSource="{Binding Path=PhoneNumbers}" Height="60"  Margin="36,0,0,0">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Grid.Column="0" Text="{Binding Path=Kind, Mode=OneWay}" />
                                <TextBlock Grid.Column="1" Text=":  " />
                                <TextBlock Grid.Column="2" Text="{Binding Path=PhoneNumber, Mode=OneWay}" />
                            </Grid>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
    
                <TextBlock Text="email addresses" Margin="12,12,0,0" />
                <ListBox ItemsSource="{Binding Path=EmailAddresses}" Height="60"  Margin="36,0,0,0">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Grid.Column="0" Text="{Binding Path=Kind, Mode=OneWay}" />
                                <TextBlock Grid.Column="1" Text=":  " />
                                <TextBlock Grid.Column="2" Text="{Binding Path=EmailAddress, Mode=OneWay}" />
                            </Grid>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
    
                <TextBlock Text="web sites" Margin="12,12,0,0" />
                <ListBox ItemsSource="{Binding Path=Websites}" Height="60"  Margin="36,0,0,0" />
    
                <TextBlock Text="company information" Margin="12,12,0,0" />
                <ListBox ItemsSource="{Binding Path=Companies}" Height="60"  Margin="36,0,0,0">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Grid.Column="0" Text="{Binding Path=CompanyName, Mode=OneWay}" />
                                <TextBlock Grid.Column="1" Text=":  " />
                                <TextBlock Grid.Column="2" Text="{Binding Path=JobTitle, Mode=OneWay}" />
                            </Grid>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
    
                <TextBlock Text="accounts" Margin="12,12,0,0" />
                <ListBox ItemsSource="{Binding Path=Accounts}" Height="60" Margin="36,0,0,0">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Grid.Column="0" Text="{Binding Path=Kind, Mode=OneWay}" />
                                <TextBlock Grid.Column="1" Text=":  " />
                                <TextBlock Grid.Column="2" Text="{Binding Path=Name, Mode=OneWay}" />
                            </Grid>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </StackPanel>
        </Grid>
    </phone:PhoneApplicationPage>
    
  3. In Solution Explorer, right-click ContactDetails.xaml and then click View Code.

    The code-behind file opens in the code editor.

  4. Replace the code with the following. This code sets the data context of the page to the contact that was selected on the main page. It also calls the GetPicture method and displays the result in the image control.

    using System;
    using Microsoft.Phone.Controls;
    using System.Windows.Media.Imaging;
    
    namespace ContactsAndCalendarTestApp
    {
        public partial class ContactDetails : PhoneApplicationPage
        {
            public ContactDetails()
            {
                InitializeComponent();
            }
    
            protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
            {
                base.OnNavigatedTo(e);
    
                //Set the data context for this page to the selected contact
                this.DataContext = App.con;
    
                try
                {
                    //Try to get a picture of the contact
                    BitmapImage img = new BitmapImage();
                    img.SetSource(App.con.GetPicture());
                    Picture.Source = img;
                }
                catch (Exception)
                {
                    //can't get a picture of the contact
                }
            }
        }
    }
    
    Imports System.Windows.Media.Imaging
    
    Partial Public Class ContactDetails
        Inherits PhoneApplicationPage
    
        Public Sub New()
            InitializeComponent()
        End Sub
    
        Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)
    
            MyBase.OnNavigatedTo(e)
    
            'Set the data context for this page to the selected contact
            Me.DataContext = App.con
    
             Try
                Dim img As BitmapImage = new BitmapImage()
                img.SetSource(App.con.GetPicture())
                Picture.Source = img
    
            Catch ex As System.Exception
    
                'can't get a picture of the contact.
            End Try
        End Sub
    End Class
    

Checkpoint 2

In this procedure, you run the app to test your progress.

Note

Windows Phone Emulator contains sample contacts. You can test this procedure using Windows Phone Emulator or a physical device. However, the sample contacts do not have photos.

To test the app

  1. Start your app in the emulator or on a device. If necessary, use the instructions from the first checkpoint.

    The app starts and the main page appears.

  2. In the text box, enter the letter A, and then click search.

    The results populate the list. If you test the app on Windows Phone Emulator, you see the following.

    Andrew Hill

    Arturo Lopez

  3. Tap a name in the result list.

    The contact details page opens with the contact details displayed. Not all data is available for all contacts.

  4. On the Debug menu, click Stop Debugging. (F5)

Accessing appointment data

The process for accessing calendar data is to get a reference to the Appointments object, perform an asynchronous search on it by calling SearchAsync, and then capture the results as a collection of Appointment objects in the SearchCompleted event handler.

To create the appointment pivot item UI

  1. In Solution Explorer, double-click MainPage.xaml to open it in the designer.

  2. Locate the third pivot item and replace it with the following code. This code creates labels for the start and end dates, the search button, and the list box that contains the results of the search.

    <controls:PivotItem Header="appointments">
        <StackPanel Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
    
            <TextBlock Text="appointments between" Foreground="{StaticResource PhoneAccentBrush}" />
    
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
    
                <TextBlock Grid.Row="0" Grid.Column="0" Text="start date" />
                <TextBlock Grid.Row="0" Grid.Column="1" Text="end date" />
                <TextBlock Grid.Row="1" Grid.Column="0" Text="placeholder" Name="StartDate" />
                <TextBlock Grid.Row="1" Grid.Column="1" Text="placeholder" Name="EndDate" />
                <Button Grid.Row="2" Grid.ColumnSpan="2" Content="search" Click="SearchAppointments_Click" HorizontalAlignment="Center" />
            </Grid>
    
            <TextBlock Name="AppointmentResultsLabel" Text="search for appointments" TextWrapping="Wrap" />
    
            <ListBox Name="AppointmentResultsData" ItemsSource="{Binding}" Tap="AppointmentResultsData_Tap" Height="400"  ScrollViewer.ManipulationMode="Control" Margin="24,0,0,0" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=Subject, Mode=OneWay}"  FontSize="{StaticResource PhoneFontSizeExtraLarge}" />
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
    </controls:PivotItem>
    

To access appointment data

  1. In Solution Explorer, right-click MainPage.xaml and then click View Code.

    The code-behind file opens in the code editor.

  2. In the MainPage class, after the constructor, add the following code. This code contains the search button click event, and starts the asynchronous appointment search. This code searches for appointments during the upcoming week.

    private void SearchAppointments_Click(object sender, RoutedEventArgs e)
    {
        AppointmentResultsLabel.Text = "results are loading...";
        AppointmentResultsData.DataContext = null;
        Appointments appts = new Appointments();
    
        appts.SearchCompleted += new EventHandler<AppointmentsSearchEventArgs>(Appointments_SearchCompleted);
    
        DateTime start = DateTime.Now;
        DateTime end = start.AddDays(7);
    
        appts.SearchAsync(start, end, 20, "Appointments Test #1");
    }
    
    Private Sub SearchAppointments_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
    
        AppointmentResultsLabel.Text = "results are loading..."
        AppointmentResultsData.DataContext = Nothing
    
        Dim appts As Appointments = new Appointments()
    
        AddHandler appts.SearchCompleted, AddressOf Appointments_SearchCompleted
    
        Dim start As DateTime = new DateTime.Now
        Dim endTime As DateTime = start.AddDays(7)
    
        appts.SearchAsync(start, endTime, 20, "Appointments Test #1")
    End Sub
    
  3. After the click event, add the following code. This code contains the method to handle the completed event of the asynchronous search. This code binds the appointment data to the user interface by setting the data context of the list box equal to the results of the search. It includes error handling in case there are no results. The code also changes the text of the list box label depending on whether there are results or not.

    void Appointments_SearchCompleted(object sender, AppointmentsSearchEventArgs e)
    {
        StartDate.Text = e.StartTimeInclusive.ToShortDateString();
        EndDate.Text = e.EndTimeInclusive.ToShortDateString();
    
        try
        {
            //Bind the results to the list box that displays them in the UI
            AppointmentResultsData.DataContext = e.Results;
        }
        catch (System.Exception)
        {
            //That's okay, no results
        }
    
        if (AppointmentResultsData.Items.Count > 0)
        {
            AppointmentResultsLabel.Text = "results (tap for details...)";
        }
        else
        {
            AppointmentResultsLabel.Text = "no results";
        }
    }
    
    Private Sub Appointments_SearchCompleted(sender As Object, e As AppointmentsSearchEventArgs)
    
        StartDate.Text = e.StartTimeInclusive.ToShortDateString()
        EndDate.Text = e.EndTimeInclusive.ToShortDateString()
    
        Try
            'Bind the results to the list box that displays them in the UI
            AppointmentResultsData.DataContext = e.Results
    
        Catch ex As System.Exception
    
            'That's okay, no results
        End Try
    
        If AppointmentResultsData.Items.Count > 0 Then
    
            AppointmentResultsLabel.Text = "results (tap for details...)"
        Else
            AppointmentResultsLabel.Text = "no results"
        End If
    End Sub
    
  4. After the search completed event, add the following code. When you tap an appointment in the results list, this code stores the appointment in a global variable, and then navigates to the appointment details page.

    private void AppointmentResultsData_Tap(object sender, GestureEventArgs e)
    {
        App.appt = ((sender as ListBox).SelectedValue as Appointment);
    
        NavigationService.Navigate(new Uri("/AppointmentDetails.xaml", UriKind.Relative));
    }
    
    Private Sub AppointmentResultsData_Tap(sender As System.Object , e As System.Windows.Input.GestureEventArgs)
    
        App.appt = CType((CType(sender, ListBox)).SelectedValue, Appointment)
    
        NavigationService.Navigate(new Uri("/AppointmentDetails.xaml", UriKind.Relative))
    End Sub
    

Displaying the appointment details

When you tap an appointment in the results list on the main page, the app navigates to the appointment details page.

To display the appointment details

  1. In Solution Explorer, double-click AppointmentDetails.xaml to open it in the designer.

  2. Locate the GRID element named LayoutRoot. Replace everything from there to the end of the file with the following code. This creates a text block for the appointment subject, and a scroll viewer to hold the other appointment data.

        <phone:PhoneApplicationPage.Resources>
    
            <DataTemplate x:Key="AttendeeTemplate">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" Text="{Binding Path=DisplayName, Mode=OneWay}" TextWrapping="Wrap" />
                    <TextBlock Grid.Column="1" Text=":  " />
                    <TextBlock Grid.Column="2" Text="{Binding Path=EmailAddress, Mode=OneWay}" TextWrapping="Wrap" />
                </Grid>
            </DataTemplate>
        </phone:PhoneApplicationPage.Resources>
    
    
        <!--LayoutRoot is the root grid where all page content is placed-->
        <Grid x:Name="LayoutRoot" Background="Transparent">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
    
            <!--TitlePanel contains the name of the application and page title-->
            <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,6">
                <TextBlock x:Name="ApplicationTitle" Text="Contacts and Calendar Test App" Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock x:Name="PageTitle" Text="appt details" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
            </StackPanel>
    
            <TextBlock Grid.Row="1" Text="{Binding Path=Subject, Mode=OneWay}" Foreground="{StaticResource PhoneAccentBrush}" FontSize="{StaticResource PhoneFontSizeExtraLarge}" TextWrapping="Wrap" />
    
            <!--ContentPanel - place additional content here-->
            <ScrollViewer x:Name="ContentPanel" Grid.Row="2" Margin="12,0,12,0">
                <StackPanel>
    
                    <TextBlock Text="{Binding Path=Details, Mode=OneWay}"  Margin="12,0,0,0" TextWrapping="Wrap" />
    
                    <TextBlock Text="{Binding Path=StartTime, Mode=OneWay}"  Margin="12,12,0,0"/>
                    <TextBlock Text="{Binding Path=EndTime, Mode=OneWay}"  Margin="12,0,0,0"/>
    
                    <TextBlock Text="{Binding Path=Location, Mode=OneWay}"  Margin="12,12,0,0"/>
    
                    <TextBlock Text="{Binding Path=Status, Mode=OneWay}"  Margin="12,12,0,0"/>
    
                    <TextBlock Text="organizer" Margin="12,12,0,0" />
                    <ListBox ItemsSource="{Binding Path=Organizer}" ItemTemplate="{StaticResource AttendeeTemplate}" Margin="24,0,0,0" />
    
                    <TextBlock Text="attendees" Margin="12,12,0,0" />
                    <ListBox ItemsSource="{Binding Path=Attendees}" ItemTemplate="{StaticResource AttendeeTemplate}" Margin="24,0,0,0" />
    
                    <TextBlock Text="accounts" Margin="12,12,0,0" />
                    <ListBox ItemsSource="{Binding Path=Accounts}" Margin="24,0,0,0">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto"/>
                                        <ColumnDefinition Width="Auto"/>
                                        <ColumnDefinition Width="*"/>
                                    </Grid.ColumnDefinitions>
                                    <TextBlock Grid.Column="0" Text="{Binding Path=Kind, Mode=OneWay}" />
                                    <TextBlock Grid.Column="1" Text=":  " />
                                    <TextBlock Grid.Column="2" Text="{Binding Path=Name, Mode=OneWay}" />
                                </Grid>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </StackPanel>
            </ScrollViewer>
        </Grid>
    </phone:PhoneApplicationPage>
    
  3. In Solution Explorer, right-click AppointmentDetails.xaml and then click View Code.

    The code-behind file opens in the code editor.

  4. Replace the code with the following. This code set the data context of the page to the appointment that was selected on the main page.

    using Microsoft.Phone.Controls;
    
    namespace ContactsAndCalendarTestApp
    {
        public partial class AppointmentDetails : PhoneApplicationPage
        {
            public AppointmentDetails()
            {
                InitializeComponent();
            }
    
            protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
            {
                base.OnNavigatedTo(e);
    
                //Set the data context for this page to the selected appointment
                this.DataContext = App.appt;
            }
        }
    }
    
    Partial Public Class AppointmentDetails
        Inherits PhoneApplicationPage
    
        Public Sub New()
            InitializeComponent()
        End Sub
    
        Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)
    
            MyBase.OnNavigatedTo(e)
    
            'Set the data context for this page to the selected appointment
            Me.DataContext = App.appt
        End Sub
    End Class
    

Checkpoint 3

In this procedure, you run the app to test your progress.

Important Note:

Windows Phone Emulator does not contain sample appointments. You can test this procedure using Windows Phone Emulator, but you will see appointments only on a physical device.

To test the app

  1. Start your app in the emulator or on a device. If necessary, use the instructions from the first checkpoint.

    The app starts and the main page appears.

  2. Swipe to the appointments pivot item, and then click the search button.

    The results populate the list.

  3. Tap an appointment in the list.

    The appointment details page opens with the appointment details displayed. If the details are long, scroll the page to see all the details.

  4. On the Debug menu, click Stop Debugging. (F5)

Accessing account data

Windows Phone provides an aggregated view of the user’s contact data across the user's different accounts. Information can come from sources such as data entered in the phone itself, social networking sites, and other data service providers. In this procedure, you display the accounts that the contacts and calendar data comes from.

To access account data

  1. In Solution Explorer, double-click MainPage.xaml to open it in the designer.

  2. Locate the second pivot item and replace it with the following code. This code creates two list boxes, one for the contact accounts and one for the appointment accounts. This code also uses an item template which you will add in the next step.

    <controls:PivotItem Header="accounts">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
    
            <StackPanel Grid.Row="0" >
    
                <TextBlock Text="contact accounts" Foreground="{StaticResource PhoneAccentBrush}" Style="{StaticResource PhoneTextLargeStyle}" />
                <ListBox Name="ContactAccounts" ItemsSource="{Binding}" ItemTemplate="{StaticResource AccountTemplate}" Height="200" Margin="24,0,0,0" />
            </StackPanel>
    
            <StackPanel Grid.Row="1" >
    
                <TextBlock Text="appointment accounts" Foreground="{StaticResource PhoneAccentBrush}" Style="{StaticResource PhoneTextLargeStyle}" />
                <ListBox Name="CalendarAccounts" ItemsSource="{Binding}" ItemTemplate="{StaticResource AccountTemplate}" Height="200" Margin="24,0,0,0" />
            </StackPanel>
        </Grid>
    </controls:PivotItem>
    
  3. After the PHONE:PHONEAPPLICATIONPAGE element and before the GRID element, add the following code. This template displays the name and kind of account on a single line for each entry in the list.

    <phone:PhoneApplicationPage.Resources>
    
        <DataTemplate x:Key="AccountTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" Text="{Binding Path=Name, Mode=OneWay}" />
                <TextBlock Grid.Column="1" Text=" account: " />
                <TextBlock Grid.Column="2" Text="{Binding Path=Kind, Mode=OneWay}" />
            </Grid>
        </DataTemplate>
    
    </phone:PhoneApplicationPage.Resources>
    
  4. In Solution Explorer, right-click MainPage.xaml and then click View Code.

    The code-behind file opens in the code editor.

  5. In the constructor, after any existing code, add the following. This code binds the account data to the user interface by setting the data context of the contacts list box to the Accounts property and the appointments list box to the Accounts property.

    ContactAccounts.DataContext = (new Contacts()).Accounts;
    CalendarAccounts.DataContext = (new Appointments()).Accounts;
    
    ContactAccounts.DataContext = (new Contacts()).Accounts
    CalendarAccounts.DataContext = (new Appointments()).Accounts
    

Checkpoint 4

In this procedure, you run the app to test your progress.

To test the app

  1. Start your app in the emulator or on a device. If necessary, use the instructions from the first checkpoint.

    The app starts and the main page appears.

  2. Swipe to the accounts pivot item.

    The results populate the lists. If you test the app on Windows Phone Emulator, you see the following in both lists.

    Phone account: Phone

  3. On the Debug menu, click Stop Debugging. (F5)

Data-binding contact photos to the UI

In a previous procedure, you displayed the photo for a single contact on the contact details page. In this procedure, you data-bind multiple contact photos directly to the results list on the main page. Because contact photos are not a property (they are returned by calling the GetPicture method), you must create a custom data converter before you can data-bind them.

To data-bind contact photos to the UI

  1. In Solution Explorer, right-click MainPage.xaml and then click View Code.

    The code-behind file opens in the code editor.

  2. Locate the following code. In the next step, you add new code after this line.

    }//End page class
    
    End Class'End page class
    
  3. After the MainPage class code, add the following new class. This is the custom data converter.

    public class ContactPictureConverter : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Contact c = value as Contact;
            if (c == null) return null;
    
            System.IO.Stream imageStream = c.GetPicture();
            if (null != imageStream)
            {
                return Microsoft.Phone.PictureDecoder.DecodeJpeg(imageStream);
            }
            return null;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }//End converter class
    
    Public Class ContactPictureConverter
        Implements System.Windows.Data.IValueConverter
    
        Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object _
            Implements System.Windows.Data.IValueConverter.Convert
    
            Dim c as Contact = CType(value, Contact)
    
            If c Is Nothing Then
                 Return Nothing
            End If
    
            Dim imageStream as System.IO.Stream = c.GetPicture()
    
            If imageStream IsNot Nothing Then
    
                Return Microsoft.Phone.PictureDecoder.DecodeJpeg(imageStream)
            End If
    
            Return Nothing
        End Function
    
        Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object _
            Implements System.Windows.Data.IValueConverter.ConvertBack
    
            throw new NotImplementedException()
        End Function
    End Class
    
  4. In Solution Explorer, double-click MainPage.xaml to open it in the designer.

  5. In the <phone:PhoneApplicationPage> tag, with the other namespace declarations, add the following code. This allows you to access the custom data converter.

    xmlns:MyApp="clr-namespace:ContactsAndCalendarTestApp"
    
  6. Locate the PHONE:PHONEAPPLICATION.RESOURCES element that you added in a previous procedure. After the data template, before the end </phone:PhoneApplicationPage.Resources> tag, add the following.

    <MyApp:ContactPictureConverter x:Key="ContactPictureConverter" />
    
  7. In the first pivot item, in the contact results, locate the following data template.

    <DataTemplate>
        <TextBlock Name="ContactResults" Text="{Binding Path=DisplayName, Mode=OneWay}" FontSize="{StaticResource PhoneFontSizeExtraLarge}" Margin="18,8,0,0" />
    </DataTemplate>
    
  8. Replace the existing data template with the following. This adds the photo of the contact, and data-binds it using the custom data converter.

    <DataTemplate>
        <StackPanel Orientation="Horizontal" >
            <Border BorderThickness="2" HorizontalAlignment="Left" VerticalAlignment="Center" BorderBrush="{StaticResource PhoneAccentBrush}" >
                <Image Source="{Binding Converter={StaticResource ContactPictureConverter}}" Width="48" Height="48" Stretch="Fill"  />
            </Border>
            <TextBlock Name="ContactResults" Text="{Binding Path=DisplayName, Mode=OneWay}" FontSize="{StaticResource PhoneFontSizeExtraLarge}" Margin="18,8,0,0" />
        </StackPanel>
    </DataTemplate>
    

Checkpoint 5

In this procedure, you run the app to test your progress.

Note

Windows Phone Emulator contains sample contacts. You can test this procedure using Windows Phone Emulator or a physical device. However, the sample contacts do not have photos.

To test the app

  1. Start your app in the emulator or on a device. If necessary, use the instructions from the first checkpoint.

    The app starts and the main page appears.

  2. In the text box, enter the letter A, and then click search.

    The results populate the list. If you test the app on a physical device, some contacts may have photos. Scroll the list until you see contacts that have photos.

  3. On the Debug menu, click Stop Debugging. (F5)

Next Steps

You can expand the functionality of the app in the following ways.

  • Change the appointment search to get the start and end dates from the user.

  • Change the contact details page to use a scroll viewer, similar to the appointment details page.

  • Change the contact search results to display the contact’s name, email, or phone number depending on the type of search the user performs.

  • Add the ability to search for all contacts pinned to the Start screen. For more information, see Contact filtering and matching for Windows Phone 8.

See Also

Reference

Microsoft.Phone.UserData

Other Resources

How to access contact data for Windows Phone 8

How to access calendar data for Windows Phone 8

How to display the photo of a contact for Windows Phone 8