Kontaktdaten lassen sich relativ einfach aus dem Windows Phone Adressbuch abfragen und über eine Datenbindung anzeigen. Jedoch ist das Kontaktbild nicht als Eigenschaft verfügbar, sondern nur über die Methode GetPicture(), die ein Stream-Objekt zurückliefert. Um das Kontaktbild trotzdem über eine Databinding im Stil von MVVM anzuzeigen, kann ein Wertkonverter verwendet werden. In folgendem Artikel wird ein solcher Konverter vorgestellt und wie er in eine App eingebunden werden kann.

Die Contact-Klasse ermöglicht den Zugriff auf das Profilbild über GetPicture() Die Contact-Klasse ermöglicht den Zugriff auf das Profilbild über GetPicture()

1. Kontakte abfragen

Um die Kontaktdaten abzufragen, kann die Contacts-Klasse verwendet werden.

1
2
3
4
5
6
7
8
9
10
Contacts cons = new Contacts();

// Methode festlegen, die nach der Suche aufgerufen wird
cons.SearchCompleted += new EventHandler<ContactsSearchEventArgs>((sender, e) => {
  // In e.Results befindet sich die Liste aller Kontakte
  this.Contacts = e.Results;
});

// Alle Kontakte abrufen (leerer Filter)
cons.SearchAsync(string.Empty, FilterKind.None, "Kontakt Test");

Über SearchAsync mit einer leeren Zeichenfolge als Filterkriterium und dem FilterKind.None werden alle Kontakte aus dem Adressbuch abgerufen.

2. Kontaktabfrage mit async/await Pattern

Die Abfrage der Kontakte kann wie in folgendem Codeausschnitt in eine asynchrone Methode gekapselt werden. Das vereinfacht den Aufruf der Methode noch etwas, da kein Event mit EventHandler verwendet werden muss und die in Apps gängige async/await Syntax benutzt werden kann.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private Task<IEnumerable<Contact>> GetContactsAsync()
{
    var tcs = new TaskCompletionSource<IEnumerable<Contact>>();
    Contacts contacts = new Contacts();

    EventHandler<ContactsSearchEventArgs> search = null;
    search = (s, e) =>
    {
        contacts.SearchCompleted -= search;
        tcs.TrySetResult(e.Results);
    };

    contacts.SearchCompleted += search;

    // Empty search filter for all contacts.
    contacts.SearchAsync(string.Empty, FilterKind.None, null);
    return tcs.Task;
}

3. Datenkonverter für GetPicture {.title}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ContactPictureConverter : 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 (imageStream != null)
        {
            return Microsoft.Phone.PictureDecoder.DecodeJpeg(imageStream);
        }

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Als Eingabewert wird hier nicht nur eine Eigenschaft, sondern das gesamte Contact-Objekt übergeben. Somit kann über GetPicture der Stream abgefragt und über PictureDecoder.DecodeJpeg direkt ein Bild zurückgeliefert werden.

4. XAML-Code {.title}

In der Seite muss der Namespace des Konverters eingebunden werden und der Konverter in den Ressourcen bekannt gemacht werden. Daraufhin kann er über StaticResource verwendet werden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<phone:PhoneApplicationPage
    x:Class="ContactData.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:c="clr-namespace:ContactData.Converters"
    SupportedOrientations="Portrait"
    Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

    <phone:PhoneApplicationPage.Resources>
        <c:ContactPictureConverter x:Name="ContactPictureConverter"/>
    </phone:PhoneApplicationPage.Resources>

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <phone:LongListSelector ItemsSource="{Binding Contacts}">
            <phone:LongListSelector.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="0,0,0,6">
                        <Border BorderBrush="Gray" BorderThickness="2" HorizontalAlignment="Left" Margin="0,0,10,0">
                            <Image Source="{Binding Converter={StaticResource ContactPictureConverter}}" Width="80" Height="80" />
                        </Border>
                        <StackPanel>
                            <TextBlock Text="{Binding DisplayName}"/>
                        </StackPanel>
                    </StackPanel>
                </DataTemplate>
            </phone:LongListSelector.ItemTemplate>
        </phone:LongListSelector>
    </Grid>
</phone:PhoneApplicationPage>

Im XAML muss beachtet werden, dass als Image-Source das gesamte Contact-Objekt über {Binding} gesetzt wird und der Konverter mit angegeben wird.

Beispielprojekt

Das Beispielprojekt ist hier auf GitHub zu finden.