Beginning with Windows Phone 8, each Windows Phone app is provided with its own contact store. Your app can add or remove contacts from the store, and contacts that are present in the store are displayed in the phone’s People Hub.
The contact store is retrieved by calling the Windows.Phone.PersonalInformation.ContactStore
class’s static CreateOrOpenAsync
method. If the store does not exist, it is created the first time the method is called. To add a contact to the contact store, create a StoredContact
instance and call its SaveAsync
method, as demonstrated in the following example:
ContactStore store = await ContactStore.CreateOrOpenAsync();
StoredContact contact = new StoredContact(store)
{RemoteId = remoteId, GivenName = "Annie", FamilyName = "Easley"};
await contact.SaveAsync();
ContactStore.CreateOrOpenAsync
has an overload that allows you to control the visibility of contacts within the store to other apps. The signature of the method is as follows:
CreateOrOpenAsync(ContactStoreSystemAccessMode access,
ContactStoreApplicationAccessMode sharing)
The ContactStoreSystemAccessMode
and ContactStoreApplicationAccessMode
enumeration parameters specify the level of access the system and other apps have to your app’s contact store. ContactStoreSystemAccessMode
can be either ReadOnly
or ReadWrite
. ReadOnly
indicates that only your app can modify your contacts. ReadWrite
means that the phone can modify your contacts through its built-in contacts experience that can be reached via the People Hub.
ContactStoreApplicationAccessMode
can be either LimitedReadOnly
or ReadOnly
. LimitedReadOnly
means that other apps can read only the display name and the profile picture of your app’s contacts, whereas ReadOnly
allows other apps to read all the properties of your contacts.
The default settings are ContactStoreSystemAccessMode.ReadOnly
and ContactStoreApplicationAccessMode.LimitedReadOnly.
StoredContact
contains the following properties for storing a contact’s personal details:
DisplayName
DisplayPicture
FamilyName
GivenName
HonorificPrefix
HonorificSuffix
Id
RemoteId
If not explicitly set in your code, the DisplayName
property getter returns a concatenation of the following properties:
HonorificPrefix + GivenName + FamilyName + HonorificSuffix
The Id
property is a read-only string that is assigned by the contact store when the contact is saved. Id
can be used to retrieve a particular contact from the store, as shown in the following example:
ContactStore store = await ContactStore.CreateOrOpenAsync();
StoredContact contact = await store.FindContactByIdAsync(id);
The RemoteId
property is also used to retrieve a contact. But, unlike the Id
property, it is not read-only and can be set to a value that is known by your back-end system; such as a field in a database table. The following example demonstrates how to retrieve a contact using its remote ID:
ContactStore store = await ContactStore.CreateOrOpenAsync();
StoredContact contact = await store.FindContactByRemoteIdAsync(id);
The DisplayPicture
property returns a Windows.Storage.Streams.IRandomAccessStreamReference
to an image that is set either by the user via the People Hub or by your app using the StoredContact
class’s SetDisplayPictureAsync
method, which is discussed later in the chapter.
If the data you want to associate with your contact does not fit into any of the StoredContact
properties, the class also offers a property dictionary for storing any number of other pieces of information. The following excerpt demonstrates how to save an email address along with the StoredContact
object:
IDictionary<string, object> properties = await contact.GetExtendedPropertiesAsync();
properties.Add(KnownContactProperties.Email, email);
The built-in KnownContactProperties
class provides various strongly typed names of common contact properties. You can use the fields of the KnownContactProperties
class for key values if you want the fields to be consistent with the phone’s internal contact property names.
Property names, however, are not limited to those in the KnownContactProperties
class. Indeed, you can supply any key value that you want, as demonstrated in the following excerpt:
properties.Add("Partner Name", "Mary");
To delete a contact from the store, either call DeleteAsync
on an existing StoredContact
object or use the DeleteContactAsync
method, as shown in the following example:
ContactStore store = await ContactStore.CreateOrOpenAsync();
await store.DeleteContactAsync(id);
Now that you have looked at the essential operations of the contact store, the next section explores the sample for this section. The source code is located in the ContactsAndAppointments/CustomContactStore directory in the WPUnleashed.Example project of the downloadable sample code.
The sample contains two views: ContactStoreView
and ContactCreationView
, and two associated viewmodels: ContactStoreViewModel
and ContactCreationViewModel
.
ContactStoreViewModel
loads the list of StoredContacts
from the app’s contact store. Each contact can be either deleted via a DeleteContactCommand
or viewed in detail via a ViewContactCommand
.
The Load
method of the ContactStoreViewModel
retrieves the list of contacts from the contact store (see Listing 14.9). The ContactQueryOptions
class allows you to specify the known properties that you want returned using its DesiredFields
property. In the current version of the Windows Phone SDK, however, the DesiredFields
property appears to serve no purpose.
Use the OrderBy
property of the ContactQueryOptions
to specify the order of the results returned by the query. OrderBy
is an enumeration value and can be either SystemDefault
, FamilyNameGivenName
, or GivenNameFamilyName
.
Note
To provide a consistent user experience across different apps, use the default ordering (ContactQueryResultOrdering.SystemDefault
) to order contact query results.
The ContactStoreViewModel
class’s Contacts
property is set to the list returned by the store.
public async void Load()
{
ContactStore store = await ContactStore.CreateOrOpenAsync();
/* These values are not used in the view, but merely demonstrates
* how to load them as part of the query. */
ContactQueryOptions options = new ContactQueryOptions();
options.OrderBy = ContactQueryResultOrdering.SystemDefault;
ContactQueryResult contactQuery = store.CreateContactQuery(options);
IReadOnlyList<StoredContact> storedContacts
= await contactQuery.GetContactsAsync();
Contacts = storedContacts;
}
The ContactStoreViewModel
class’s DeleteContactCommand
calls the DeleteContact
method, which deletes the contact and reloads the contact list, as shown:
async void DeleteContact(StoredContact storedContact)
{
ContactStore store = await ContactStore.CreateOrOpenAsync();
await store.DeleteContactAsync(storedContact.Id);
Load();
}
The ContactStoreViewModel
class’s ViewContactCommand
calls the ViewContact
method, which uses the viewmodel’s base class method to navigate to the ContactCreationView
page; passing it the ID of the StoredContact
via a query string parameter, as shown:
void ViewContact(StoredContact storedContact)
{
Navigate("/ContactsAndAppointments/CustomContactStore/"
+ "ContactCreationView.xaml?ContactId=" + storedContact.Id);
}
The ContactStoreView
page uses a ListBox
to display the list of contacts retrieved by the viewmodel (see Listing 14.10). An image displays a delete icon. The image element uses the custom commanding infrastructure to bind to the DeleteContactCommand
in the viewmodel.
Tapping on the TextBlock
containing the DisplayName
of the StoredContact
, executes the ViewContactCommand
.
The use of the bridge
resource allows the data template to reach outside of its data context to bind to properties of the ContactStoreViewModel
instance.
An AppBarHyperlinkButton
allows the user to create a new contact by navigating to the ContactCreationView
without passing a contact ID.
<u:AppBar>
<u:AppBarHyperlinkButton
NavigateUri="/ContactsAndAppointments/.../ContactCreationView.xaml"
Text="new contact"
IconUri="/ContactsAndAppointments/.../Images/Add.png" />
</u:AppBar>
...
<ListBox ItemsSource="{Binding Contacts}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid d:DataContext="{d:DesignInstance p:StoredContact}"
Margin="0,0,0,20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="/ContactsAndAppointments/.../Delete.png"
c:Commanding.Command="{Binding Content.DeleteContactCommand,
Source={StaticResource bridge}}"
c:Commanding.CommandParameter="{Binding}" />
<TextBlock Text="{Binding DisplayName}"
Style="{StaticResource PhoneTextLargeStyle}"
c:Commanding.Command="{Binding Content.ViewContactCommand,
Source={StaticResource bridge}}"
c:Commanding.CommandParameter="{Binding}"
Grid.Column="1" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Figure 14.36 shows the ContactStoreView
page with a number of contacts present in the app’s contact store.
When the user taps a name in the list of contacts, the app navigates to the ContactCreationView
page, which passes the query string dictionary containing the contact ID to the ContactCreationViewModel
class, as shown:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
ViewModel.Load(NavigationContext.QueryString);
}
If a contact ID is not present in the query string, the viewmodel creates a new contact, or else assumes its edit mode for an existing contact (see Listing 14.11) and retrieves and copies the properties of the contact to matching viewmodel properties.
public async void Load(IDictionary<string, string> queryString)
{
string id;
if (!queryString.TryGetValue("ContactId", out id))
{
return;
}
ContactStore store = await ContactStore.CreateOrOpenAsync();
StoredContact contact = await store.FindContactByIdAsync(id);
if (contact != null)
{
contactId = id;
GivenName = contact.GivenName;
FamilyName = contact.FamilyName;
HonorificPrefix = contact.HonorificPrefix;
IDictionary<string, object> extendedProperties
= await contact.GetExtendedPropertiesAsync();
object tempEmail;
extendedProperties.TryGetValue(KnownContactProperties.Email, out tempEmail);
EmailAddress = (string)tempEmail;
object tempCodeName;
extendedProperties.TryGetValue("CodeName", out tempCodeName);
CodeName = (string)tempCodeName;
}
}
The ContactCreationViewModel
class contains a SaveContactCommand
that calls the SaveContact
method, shown in Listing 14.12. The various viewmodel properties are copied to a new or existing contact. The method retrieves an image resource, which becomes the contact’s display picture. The contact is then saved and the page navigates back to the ContactStoreView
page.
async void SaveContact()
{
ContactStore store = await ContactStore.CreateOrOpenAsync();
StoredContact contact;
if (string.IsNullOrEmpty(contactId))
{
contact = new StoredContact(store);
}
else
{
contact = await store.FindContactByIdAsync(contactId);
}
contact.GivenName = givenName;
contact.FamilyName = familyName;
contact.HonorificPrefix = honorificPrefix;
IDictionary<string, object> extendedProperties
= await contact.GetExtendedPropertiesAsync();
extendedProperties[KnownContactProperties.Email] = emailAddress;
extendedProperties["CodeName"] = codeName;
Uri uri = new Uri("ContactsAndAppointments/CustomContactStore/Images/Contact.png",
UriKind.Relative);
StreamResourceInfo resourceInfo = Application.GetResourceStream(uri);
using (var stream = resourceInfo.Stream)
{
await contact.SetDisplayPictureAsync(stream.AsInputStream());
}
await contact.SaveAsync();
Navigate("/ContactsAndAppointments/CustomContactStore/ContactStoreView.xaml");
}
Figure 14.37 shows the ContactCreationView
page. Tapping the save button adds or updates the contact in the app’s contact store.
3.144.123.147