Windows Phone 8 - 建立App专属联络人资讯(ContactStore)

原文: Windows Phone 8 - 建立App专属联络人资讯(ContactStore)

在WP7的时候介绍了如何操作联络人的功能,例如:<Windows Phone 7 - 存取联络人与行事历资料>。

到了WP8之後增加了一个非常不一样的联络人相关的API:ContactStore,也是该篇文章要介绍的主题。

?

什麽是ContactStore

?提供让App可以建立自己专属的Contact Store(联络人储存区);

?系统会自动将建立好的Contact Store整合入People Hub中。

?除了在App操作既有的Contact集合的资讯(例如:Windows Live、Facebook…等),

?? 也可以增加自订的Custom Properties加以扩充;

??? => 需注意自订的Custom Properties需储存於Contact Store中;

??? => 并不会显示在People Hub的Contact Card上,而是需要透过App加以程序化操作;

?ContactStore API提供让App自订义的Contact store可与现有Contact Store加以同步与比对变化;

?

[注意]

?每一个App只能有一个Contact Store;

?App建立Contact Store後,系统在整合入People Hub时,可能会造成People Hub在使用上会有lag的状况;

?? => 不用担心,等系统整合完毕後该问题即不存在。

?App建立的contact Store自动整合入People Hub,如果不想让People Hub看起来很混乱;

?? => 可至「设定->应用程式->联络人->筛选我的联络人清单」,取消勾选该App的资源来源。

?

可以利用下图来说明ContactStore与系统、其他应用程式的关系:

ContactStoreRelationShip

一个App可以建立一个专属的ContactStore,并且设定该ContactStore被系统或其他Apps存取的权限,

在操作透过StoredContact物件为单位;App经由Cloud Data Source取得最新的资讯并同步至既有的StoredContact里,

而同步的方式则是依赖StoredContact.RemoteId的属性来维持一致性,所以每一个RemoteId必需是unique的。

?

?

往下便加以针对ContactStore的相关命名空间与类别加以说明:

?

Windows.Phone.PersonalInformation namespace

??? 主要提供APIs负责管理自订的contact store。透过下表来概要说明具有的元素:

类型名称说明
ClassContactAddressRepresents a civic address for StoredContact objects.
?ContactChangeRecordRepresents a change in contact information that occurred between revisions.
?ContactInformationRepresents a contact without an association to a contact store.
?ContactQueryOptionsRepresents query options for retrieving contacts using CreateContactQuery.
?ContactQueryResultRepresents the result of a contact query.

在建立ContactQueryResult可搭配ContactQueryOptions指定查询的条件与排序的方式。

重要的方法:
?GetContactCountAsync
??? Gets the number of contacts in the contact store.

?GetContactsAsync()
??? Retrieves contacts from the contact store.

?GetContactsAsync(UInt32, UInt32)
??? Retrieves contacts from the contact store given the specified starting index and
??? number of items to return.
?
?GetCurrentQueryOptions
??? Gets the current query options.
?ContactStoreRepresents the custom contact store for a Windows Phone app.
?KnownContactPropertiesProvides key names for accessing known properties for StoredContact or ContactInformation objects.
?StoredContactRepresents a contact associated with a custom contact store.
EnumerationsContactChangeTypeIndicates the type of change represented by a ContactChangeRecord.
?ContactQueryResultOrderingThe order in which contacts are returned from CreateContactQuery.
?ContactStoreApplicationAccessModeSpecifies the application access mode for a custom contact store created with CreateOrOpenAsync.
?ContactStoreSystemAccessModeSpecifies the system access mode for a custom contact store created with CreateOrOpenAsync.
?VCardFormatThe format of a vCard.
InterfaceIContactInformationDefines the interface for contact information.

在使该用该Namepsace下的元素时要,要记得开启对应的<Capabilities />:「ID_CAP_CONTACTS 」。

?

上述大致上列出该Namesapce具有的元素,往下一一针对如何操作ContactStore与使用StoredContact来加以说明。

?

ContactStroe

??? 为代表每一个App专属的custom contact store。搭配CreateOrOpenAsync()建立或开启contact store,搭配StoredContact物件加以

操作该contact store里的资料。往下说明可用的方法与使用方式:

类型名称说明
PropertyRevisionNumberRead-only。取得contact store的版本号。
MethodCreateContactQuery()使用预设的查询选项建立一个contact query已取得ContactQueryResult。
?CreateContactQuery(ContactQueryOptions)使用自订的查询选项建立一个contact query已取得ContactQueryResult。
?ContactQueryOptions
??? 用於指定contact query的查询选项,具有二个属性可以使用:
??? > DesiredField:ready-only,设定/取得查询结果的contact要显示的属性清单;
??? > OrderBy:设定/取得查询结果的contact要用那一个栏位进行排序;
?CreateOrOpenAsync()开启App专属的custom contact store,如果该store不存在预设会自动建立一个。
?CreateOrOpenAsync(ContactStoreSystemAccessMode, ContactStoreApplicationAccessMode)开启App专属的custom contact store,如果该store不存在预设会自动建立一个。
搭配ContactStoreSystemAccessMode与ContactStoreApplicationAccessMode来指定contact store被存取的权限,说明如下:

?ContactStoreSystemAccessMode

?? 指定系统本身操作该contact store的方式:

?? > ReadOnly:系统仅能读取该contact store;

?? > ReadWrite:系统可以修改该contact store中的contacts;

????????????????????? (through its built-in contacts experience.)

?ContactStoreApplicationAccessMode

?? 指定其他应用程式可以操作该contact store的方式:

?? > LimitedReadOnly:只能读取store中contacts的description、display picture;

?? > ReadOnly:可以读取所有store中contacts的属性;

?

预设指定为:ContactStoreSystemAccessMode.ReadOnly与ContactStoreApplicationAccessMode.LimitedReadOnly;

?DeleteAsync删除custom contact store。
?DeleteContactAsync删除custom contact store中指定contact ID的Contact。
?FindContactByIdAsync检索custom contact store中指定contact ID的Contacts,取得IAsyncOperation<StoredContact>的集合。
?FindContactByRemoteIdAsync检索custom contact store中指定contact remote ID的Contacts,取得IAsyncOperation<StoredContact>的集合。
?GetChangesAsync获取变更与所提供的版本号相关联的联系人存储的列表。
回传值为:IAsyncOperation<IVectorView>
?LoadExtendedPropertiesAsync为custom contact store载入扩充的属性集合。
回传值:IAsyncOperation<IDictionary>。
该方法被用於针对contact store载入扩充的属性,而不是针对个别的contact。

常见情境即是用於有的contact store加入延伸的属性,利用revision number来与remote contact store进行同步,取得extension properties来加入既有的contact store。
?SaveExtendedPropertiesAsync储存新提供的name/value清单至custom contact store的扩充属性。
SaveExtendedPropertiesAsync( IReadOnlyDictionary<String, Object> data ) 。

???

?

StoredContact

??? 代表关联於custom contact store的contact物件。相关的建构子、方法与属性说明如下:

类型名称说明
ConstructorStoredContact(ContactStore)初始化StoredContact类别的实体。
?StoredContact(ContactStore, ContactInformation)初始化StoredContact类别的实体,并给予具有初始化相关属性的ContactInformation物件。
MethodGetDisplayPictureAsync取得stored contact的显示图像。
?GetExtendedPropertiesAsync取得stored contact的扩充属性集合,采用name/value pairs呈现。
?GetPropertiesAsync取得contact已知的属性集合。
?ReplaceExistingContactAsync利用目前的StoredContact取代指定ID的contact。
?SaveAsync储存目前StoedContact的状态与属性至contact store。
?SetDisplayPictureAsync使用IInputStream object设定contact的显示图像。
?ToVcardAsync()采用vCard 3.0 format来将contact加以表示。
?ToVcardAsync(VCardFormat)采用特定vCard format来将contact加以表示。
PropertyDisplayNameRead/write。设定/取得stored contact的显示名称。
?DisplayPictureRead-only。取得stored contact的显示图像。
?FamilyNameRead/write。设定/取得stored contact的家庭名称。
?GivenNameRead/write。设定/取得stored contact的特定名称。
?HonorificPrefixRead/write。设定/取得stored contact的honorific prefix(敬语前缀)。
?HonorificSuffixRead/write。设定/取得stored contact的honorific suffix(敬语後缀)。
?IdRead-only。取得stored contact的local identifier(该值由系统自动给予)。
?RemoteIdRead/write。设定/取得stored contact的remote identifier。
?StoreRead-only。取得stored contact所储存的ContactStore。其值在StoredContact建构子时就会被给予。

要使用StoredContact时预先要取得ContactStore,另外StoredContact是利用 local id(来自系统给予)与 remote id来加以管理,

利用remote + local id来建立关联电话中的contact与cloud上的contact;所以App里如果要管理自订义的Contact可给予唯一的

RemoteId来予後端的系统资料加以对应。

如果要操作的联络人资讯未被储存於ContactStore或StoredContact,可藉由ContactInformation加以描述来使用。

?

?

以上说明完重要的类别与方法後,往下便藉由范例的方式来说明如何加以运用。

?

[范例]

该范例的情境,例如:

a. 做一个App专属於某家公司的零件维修人员专用;

b. App自动会建立专属的联络人,用户不需要手动建立;

c. 专属联络人的资料由後端系统维护;

?

了解该范例的情境後,以下分成几个步骤来加以介绍操作的方式:

?

A. 建立後端联络人资料档案(以XML档为例),负责管理StoredContact.RemoteId;

<?xml version="1.0" encoding="utf-8" ?>
<contacts version="1">
  <contact rid="10000" dname="jenna" fname="wang" gname="jenna" prefix="" suffix="">
    <picture>https://fbcdn-sphotos-f-a.akamaihd.net/hphotos-ak-prn1/t1/75603_10153754407645367_493094653_n.jpg?dl=1</picture>
    <mphone>0932111222</mphone>
    <mail>jenna@live.com</mail>
    <jobtitle>Actor</jobtitle>
  </contact>
  <contact rid="10001" dname="安心亚" fname="" gname="" prefix="" suffix="">
    <picture>https://fbcdn-sphotos-e-a.akamaihd.net/hphotos-ak-prn2/t1/1502517_10153643531660467_768374391_n.jpg?dl=1</picture>
    <mphone>0932111222</mphone>
    <mail>annie@live.com</mail>
    <jobtitle>Actor</jobtitle>
  </contact>
  <contact rid="10002" dname="Zooey Deschanel" fname="Deschanel" gname="" prefix="" suffix="">
    <picture>http://images.artistdirect.com/Images/nad/video/tribune/65426/65426_bc.jpg</picture>
    <mphone>0932111123</mphone>
    <mail>zooey@live.com</mail>
    <jobtitle>Actor</jobtitle>
  </contact>
  <contact rid="10003" dname="Natalie Portman" fname="" gname="" prefix="" suffix="">
    <picture>http://beauty.nownews.com/img/27/i26127-24.jpg</picture>
    <mphone>0932147699</mphone>
    <mail>portman@live.com</mail>
    <jobtitle>Actor</jobtitle>
  </contact>
</contacts>

??? 以上是使用XML的方式建立了联络人的资讯,当然如果具有自行管理的联络人系统就可以不用这样麻烦。

??? 该范例把建立好的XML档放置至於网路空间:Skydrvie或Dropbox均可以。

?

?

B. 建立一个画面专门用於同步与汇入远端联络人资料档;

??? b-1. XAML:

<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
    <TextBlock Text="ContactStore Project" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
    <TextBlock Text="同步" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
?
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <StackPanel>
        <Button Content="点击同步" Click="Button_Click" />
        <TextBlock x:Name="tblTile" Text="同步状态" Margin="15,5,5,10" />
        <toolkit:WrapPanel Width="450" Height="300">
            <TextBlock TextWrapping="Wrap"
                       x:Name="tblResultMsg"
                       Width="440" Height="300"
                       Margin="5"  />
        </toolkit:WrapPanel>
        <Button Content="操作Contact Store" Click="Button_Click_1" />
    </StackPanel>
</Grid>

?

???? b-2. 在OnNavigationTo初始化ContactStore物件;

private ContactStore gMyCStore = null;
?
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
?
    // 进行必要元件的初始化
    if (gMyCStore == null)
    {
        gMyCStore = await ContactStore.CreateOrOpenAsync();
        // 可搭配对应的AsscessMode来初始化ContactStore。
        //gMyCStore = await ContactStore.CreateOrOpenAsync(
        //                        ContactStoreSystemAccessMode.ReadWrite,
        //                        ContactStoreApplicationAccessMode.ReadOnly);
    }
}

?

???? b-3. 建立按钮事件来触发 下载联络人资料档案的任务;

private void Button_Click(object sender, RoutedEventArgs e)
{
    // 下载远端的联络人资料档案
    string tURL = "";
?
    HttpWebRequest tRequest = HttpWebRequest.CreateHttp(tURL);
    tRequest.BeginGetResponse((IAsyncResult ar) =>
    {
        if (ar.IsCompleted)
        {
            HttpWebResponse tResponse = 
                (HttpWebResponse)tRequest.EndGetResponse(ar);
            if (tResponse.StatusCode == HttpStatusCode.OK)
            {
                using (StreamReader tResponseStream = 
                        new StreamReader(tResponse.GetResponseStream()))
                {
                    string strResult = tResponseStream.ReadToEnd();
                    XDocument tXDoc = XDocument.Parse(strResult);
?
                    // 识别是否需要进行更新
                       bool tIsNeedUpdate = true;
                    if (IsolatedStorageSettings.ApplicationSettings.Contains("cs_v") == true)
                    {
                        double tV = double.Parse(
                                IsolatedStorageSettings.ApplicationSettings["cs_v"].ToString());
                        var tNVersoin = double.Parse(tXDoc.Root.Attribute("version").Value);
                        if (tNVersoin <= tV) tIsNeedUpdate = false;
                    }
                    if (tIsNeedUpdate)
                    {
                        // 进行更新
                        FeedDataToContactStore(tXDoc);
                    }
                }
            }
        }
    }, tRequest);
}

??????????? 上述程式除了完成下载联络人资料档之外,也做了版本的比对,这样一来只要管理後端的资料档,

??????????? 等用户按下同步,App就可以取得最新的资料档案进行更新了。

?

???? b-4. 负责汇入联络人资料至ContactStore;

/// <summary>
/// 将远端联络人资讯更新至本地的ContactStore。
/// </summary>
/// <param name="pXDoc"></param>
private async void FeedDataToContactStore(XDocument pXDoc)
{
    try
    {
        var tDataSet = from CItem in pXDoc.Descendants().Elements("contact")
                        select CItem;
        // 遂一更新既有的联络人
        foreach (XElement tCItem in tDataSet)
        {
            StoredContact tCObj = null;
            // 先识别指定的Id是否已经存在了,才不会重覆加入StoredContact
            tCObj = await gMyCStore.FindContactByRemoteIdAsync(tCItem.Attribute("rid").Value);
            if (tCObj == null)
            {
                // 代表指定的RemoteId不存在於custom ContactStore
                tCObj = new StoredContact(gMyCStore);
                tCObj.RemoteId = tCItem.Attribute("rid").Value;
            }
          
            // 填入必要的参数
            tCObj.DisplayName = tCItem.Attribute("dname").Value;
            tCObj.FamilyName = tCItem.Attribute("fname").Value;
            tCObj.GivenName = tCItem.Attribute("gname").Value;
            tCObj.HonorificPrefix = tCItem.Attribute("prefix").Value;
            tCObj.HonorificSuffix = tCItem.Attribute("suffix").Value;
?
            // 利用GetPropertiesAsync填入已知的联络人资讯
            IDictionary<string, object> props = await tCObj.GetPropertiesAsync();
            if (props.ContainsKey(KnownContactProperties.Email))
                props[KnownContactProperties.Email] = tCItem.Element("mail").Value;
            else
                props.Add(KnownContactProperties.Email, tCItem.Element("mail").Value);
            if (props.ContainsKey(KnownContactProperties.MobileTelephone))
                props[KnownContactProperties.MobileTelephone] = tCItem.Element("mphone").Value;
            else
                props.Add(KnownContactProperties.MobileTelephone, tCItem.Element("mphone").Value);
            if (props.ContainsKey(KnownContactProperties.JobTitle))
                props[KnownContactProperties.JobTitle] = tCItem.Element("jobtitle").Value;
            else
                props.Add(KnownContactProperties.JobTitle, tCItem.Element("jobtitle").Value);
?
            // 增加扩充的属性描述
            IDictionary<string, object> extprops = await tCObj.GetExtendedPropertiesAsync();
            if (extprops.ContainsKey("Provider") == false)
                extprops.Add("Provider", "Pou Mason - ContactStoreProj");
?
            // 填入呈现的图像
            try
            {
                HttpWebRequest tRequest = HttpWebRequest.CreateHttp(tCItem.Element("picture").Value);
                tRequest.BeginGetResponse(async (IAsyncResult ar) =>
                {
                    if (ar.IsCompleted)
                    {
                        HttpWebResponse tResponse = (HttpWebResponse)tRequest.EndGetResponse(ar);
                        if (tResponse.StatusCode == HttpStatusCode.OK)
                        {
                            await tCObj.SetDisplayPictureAsync(tResponse.GetResponseStream().AsInputStream());
                            // 由於是不同的thread,所以要再使用一次SaveAsync()
                            await tCObj.SaveAsync();
                        }
                    }
                }, tRequest);
            }
            catch (Exception) { }
?
            // 储存至ContactStore
            await tCObj.SaveAsync();
?
            // 更新进度
            Dispatcher.BeginInvoke(() =>
            {
                tblTile.Text += string.Format("\nname: {0}, \t{1}", tCObj.DisplayName, "success");
            });
        }
    }
    catch (Exception ex)
    {
        Dispatcher.BeginInvoke(() =>
        {
            tblTile.Text += ex.Message;
        });    
    }            
}

??????????? 上述程式根据取得的XML资料加以汇入至ContactStore,每一个<contact />搭配一个rid的唯一值来对应ContactStore

??????????? 中的StoredContact物件,加以识别是否具有存在的Properties与ExtendedProperties,并采用非同步设定它的DisplayPicture。

?

??????????? [注意]

??????????? a. 里面使用了HttpWebRequest的并没有做最佳化的处理,所以当大量资料汇入时可能会有问题。

??????????? b. 采用非同步下载图像资料时,由於与汇入基本资料属於不同的Thread,所以要设定二次的SaveAsync()。?

??????????? c. 如果在撰写汇入资料至ContactStore太久时,建议可以分批同步,可搭配Backgroun Agent的方式来定期分批汇入;??

?

至步骤B就完成了汇入联络人至ContactStore的任务,以下为执行的画面:

wp_ss_20140209_0002wp_ss_20140209_0004wp_ss_20140209_0005

?

透过上图可以得知自行App所建立的ContactStore对於People Hub也是一个筛选的资料来源,因此,

如果用户安装了你的App之後一定会发现不认识的联络人增加了很多,此时,可以在App给一些提示告知他们如果隐藏这些联络人:

可至「设定->应用程式->联络人->筛选我的联络人清单」,取消勾选该App的资源来源。

?

?

C. 搜寻指定的联络人

???? c-1. 定义搜寻的画面:

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="13*" />
        <RowDefinition Height="25*" />
        <RowDefinition Height="62*" />
    </Grid.RowDefinitions>
    <TextBox Grid.Row="0" x:Name="txtName" />
    <StackPanel Grid.Row="1">
        <Button Content="Query" x:Name="btnQuery" Click="btnQuery_Click" />
        <Button Content="Delete" x:Name="btnDelete" Click="btnDelete_Click" />
    </StackPanel>
    <ListBox x:Name="lstContact" Grid.Row="2">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" Margin="15">
                    <Border BorderThickness="2"
                            HorizontalAlignment="Left"
                            BorderBrush="{StaticResource PhoneAccentBrush}" >
                        <Image Source="{Binding Converter={StaticResource ContactPictureConverter}}" 
                               Width="80" Height="60" 
                               Stretch="Fill" />
                    </Border>
                    <StackPanel Margin="5">
                        <TextBlock Text="{Binding Path=DisplayName, Mode=OneWay}" VerticalAlignment="Center" />
                        <TextBlock Text="{Binding Path=FamilyName, Mode=OneWay}" VerticalAlignment="Center" />
                        <TextBlock Text="{Binding Path=RemoteId, Mode=OneWay}" VerticalAlignment="Center" />
                    </StackPanel>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

?

???? c-2. 定义了IValueConvert负责将StoredContact中的DisplyPicture转换成WriteableBitmap;

/// <summary>
/// 客制一个转换联络人图片的类别,透过实作IValueConverter Interface覆写指定转换的功能。
/// </summary>
public class ContactPictureConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null) return null;
?
        StoredContact tC = value as StoredContact;
        // 取得DisplayPciture的值,采用Task的方式取代await的使用方法,强迫执行序等待
        var tTask = tC.GetDisplayPictureAsync().AsTask();
        tTask.Wait();
        // 将取得的IRandomAccessStream转换成Steam并建立成WriteableBitmap
        WriteableBitmap tWbmap = Microsoft.Phone.PictureDecoder.DecodeJpeg(tTask.Result.AsStream());
?
        return tWbmap;
    }
?
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

?????????? 由於DisplyPicture的取得需采用非同步的方式,依照过去的写法一定会加上await,但这样会让IValueConvert无法识别回传值,

?????????? 所以没有办法直接使用async + await的方式。因此,这篇改用直接将await转换成一个Task的物件,并且强迫该Task进行等待

?????????? 等待取得图像IRandomAccessStream後,最後再转换成WriteableBitmap回传至Image物件。

?????????? 详细内容参考<XAML Image From Path>。

?

???? c-3. 定义搜寻的逻辑:

private async void btnQuery_Click(object sender, RoutedEventArgs e)
{
    // 建立一个ContactQueryResult
    ContactQueryResult tResultObj = gMyCStore.CreateContactQuery();
?
    // 也可以搭配指定要搜寻的项目
    //ContactQueryOptions tOptions = new ContactQueryOptions();
    //tOptions.DesiredFields.Add(KnownContactProperties.Email);
    //tOptions.OrderBy = ContactQueryResultOrdering.SystemDefault;
    //ContactQueryResult tResultObj = gMyCStore.CreateContactQuery(tOptions);
?
    // 取得目前ContactStore中有联络人的数量
    uint tCount = await tResultObj.GetContactCountAsync();
?
    // 取得目前所有的联络人集合
    IReadOnlyList<StoredContact> tContacts = await tResultObj.GetContactsAsync();
?
    // 根据输入的文字搜寻StoredContact
    IEnumerable<StoredContact> tSearch = tContacts.Where(x => x.DisplayName.Contains(txtName.Text));
?
    lstContact.ItemsSource = tSearch;
}

??????????? 配合ContactQueryResult的物件取得目前在ContactStore中所具有的所有StoredContact,并搭配LINQ进行搜寻。

?

D. 删除指定联络人的资料

private async void btnDelete_Click(object sender, RoutedEventArgs e)
{
    if (lstContact.SelectedIndex != -1)
    {
        StoredContact tContact = lstContact.SelectedItem as StoredContact;
        // 必须使用StoredContact.Id (local id),因为该值是由系统给予的。
        await gMyCStore.DeleteContactAsync(tContact.Id);
?
        MessageBox.Show("移除成功!");
?
        ContactQueryResult tResultObj = gMyCStore.CreateContactQuery();
        lstContact.ItemsSource = await tResultObj.GetContactsAsync();
    }
}

??? 根据选择指定的StoredContact,取得它的Id(local Id)指定给ContactStore来进行删除。

?

看一下执行的画面与结果:

wp_ss_20140209_0006

?

E. 搭配vCard的处理:

??? 可参考<How to perform basic custom contact store operations for Windows Phone - ?Saving a contact from a vCard>

?

以上即是介绍如何操作自行建立的ContactStore。

?

[范例程式]

使用该范例程式时,记得先将里面的RemoteContact.xml档案放至Dropbox或其他免费空间,并更换b-3中的tURL变数才有办法正常使用喔。

======

该篇文章我觉得对於如果您想要做App与People Hub加上整合或是扩充现有联络人的功能,

可以详细去了解其中的运作原理,这样可以让你的App更加的强力。

希望解释的还算清楚对大家有所帮助。

?

References

?Custom contact store for Windows Phone

?How to perform basic custom contact store operations for Windows Phone

?Building a Custom Contact Store in Your Windows Phone 8 Application

?Using the Windows Phone Custom Contact Store

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

?Contact filtering and matching for Windows Phone & Contacts.SearchAsync Method

?Convert XML to Object using LINQ

?《深入浅出Windows Phone 8应用开发》之程序联系人存储

?Tag Archives: convert Stream to IRandomAccessStream

?Display Image from Stream in Windows 8 and Windows Phone 8

?Using User-Provided Images for Secondary Tiles

?Set display picture of StoredContact from byte[]

?与众不同 windows phone (39) - 8.0 联系人和日历

?Drawing / Inking API in WinRT (C#) – II

?

Dotblogs 的标签: Windows Phone

 

enjoy developing application and learning new technology time.


admentorserve.aspx?type=img&z=18&a=11
DotBlogs Tags: Windows Phone

posted on 2014/2/13 16:09 | 我要推荐 | 阅读数 : 392 | 文章分类 [ Windows Phone ] | 订阅

posted on 2014-03-08 20:31 NET未来之路 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/3588545.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值