信息
您所需的主题如下所示。但此主题未包含在此库中。

从 Windows Phone 8 的 SD 卡读取

2014/6/18

仅适用于:Windows Phone 8 和 Windows Phone Silverlight 8.1

Windows Phone 应用可以使用 Microsoft.Phone.Storage API 读取 SD 卡中的特定文件类型。若要读取文件,您的应用必须在应用清单文件中注册文件关联,并声明它能处理的文件类型(扩展)。因为文件关联是必需的,所以也可以启动您的应用来处理不在 SD 卡上的文件。有关文件关联的更多信息,请参见使用 Windows Phone 8 的文件和 URI 关联自动启动应用

本主题所述的代码来自路由映射器示例。该示例演示处理文件关联和读取 SD 卡的主要步骤。

说明注意:

路由映射器示例读取 GPX 文件中的路由,并在地图控件上显示它们。它可以从 SD 卡和文件关联中读取 GPX 文件。一个页面 RoutePage.xaml 用于处理这两种情况。为了从 SD 卡读取 GPX 文件,MainPage.xaml 通过启动该页面的 URI 将指向该文件的路径发送至 RoutePage.xaml。有关更多信息,请参见示例

本主题包括以下部分。

在应用清单文件 WMAppManifest.xml 中,指定用于访问 SD 卡的 ID_CAP_REMOVEABLE_STORAGE 功能。

<Capability Name="ID_CAP_REMOVABLE_STORAGE" />

若要处理特定的文件类型,请注册文件关联扩展。扩展在 WMAppManifest.xml 中指定。紧跟在 Tokens 元素之后,在 Extensions 元素内,文件关联扩展使用以下 FileTypeAssociation 元素指定。

<FileTypeAssociation TaskID="_default" Name="GPX" NavUriFragment="fileToken=%s">
  <Logos>
    <Logo Size="small" IsRelative="true">Assets/Route_Mapper_Logo33x33.png</Logo>
    <Logo Size="medium" IsRelative="true">Assets/Route_Mapper_Logo69x69.png</Logo>
    <Logo Size="large" IsRelative="true">Assets/Route_Mapper_Logo176x176.png</Logo>
  </Logos>
  <SupportedFileTypes>
    <FileType ContentType="application/gpx">.gpx</FileType>
  </SupportedFileTypes>
</FileTypeAssociation>

Windows Phone 清单设计器不支持 Extensions 元素。有关编辑扩展的更多信息,请参见如何修改 Windows Phone 8 的应用清单文件

因为需要文件关联才能读取 SD 卡中的特定文件类型,因此可以启动您的应用来处理不在 SD 卡上的文件。若要处理这些方案,则添加自定义 URI 映射器,以便将文件关联的标记传递至正确的页面。此代码来自示例的 CustomURIMapper.cs 文件。


using System;
using System.Windows.Navigation;
using Windows.Phone.Storage.SharedAccess;

namespace sdkRouteMapperWP8CS
{
    class CustomURIMapper : UriMapperBase
    {
        private string tempUri;

        public override Uri MapUri(Uri uri)
        {
            tempUri = uri.ToString();

            // File association launch
            // Example launch URI: /FileTypeAssociation?fileToken=89819279-4fe0-4531-9f57-d633f0949a19
            if (tempUri.Contains("/FileTypeAssociation"))
            {
                // Get the file ID (after "fileToken=").
                int fileIDIndex = tempUri.IndexOf("fileToken=") + 10;
                string fileID = tempUri.Substring(fileIDIndex);

                // Map the file association launch to route page.
                return new Uri("/RoutePage.xaml?fileToken=" + fileID, UriKind.Relative);
            }

            // Otherwise perform normal launch.
            return uri;
        }
    }
}


在本例中,当文件关联启动应用时,URI 映射器将传入的 URI 映射至名为 RoutePage.xaml 的页面,并将 fileToken 值传递至目标页面,以便该页面可以打开文件。如果应用因为任何其他原因而启动,则映射器按原始状态返回传入的 URI。

以下代码显示如何在 App.xaml.cs 文件的 InitializePhoneApplication 方法中将 URI 映射器分配给 App 对象。


private void InitializePhoneApplication()
{
    if (phoneApplicationInitialized)
        return;

    // Create the frame but don't set it as RootVisual yet; this allows the splash
    // screen to remain active until the application is ready to render.
    RootFrame = new PhoneApplicationFrame();
    RootFrame.Navigated += CompleteInitializePhoneApplication;

    // Assign the URI-mapper class to the application frame.
    RootFrame.UriMapper = new CustomURIMapper();


    // Handle navigation failures
    RootFrame.NavigationFailed += RootFrame_NavigationFailed;

    // Handle reset requests for clearing the backstack
    RootFrame.Navigated += CheckForResetNavigation;

    // Ensure we don't initialize again
    phoneApplicationInitialized = true;
}


启动页面时,通过使用页面的 NavigationContext 对象的 QueryString 属性,页面可以访问 URI(启动了该页面)中的所有参数。以下示例显示如何从 URI 中提取 fileToken 参数的值,然后将其分配给变量供以后使用。此代码来自示例的 RoutePage.xaml.cs 文件。


// Assign the path or token value, depending on how the page was launched.
protected override async void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    // Route is from a file association.
    if (NavigationContext.QueryString.ContainsKey("fileToken"))
    {
        _fileToken = NavigationContext.QueryString["fileToken"];
        await ProcessExternalGPXFile(_fileToken);
    }
    // Route is from the SD card.
    else if (NavigationContext.QueryString.ContainsKey("sdFilePath"))
    {
        _sdFilePath = NavigationContext.QueryString["sdFilePath"];
        await ProcessSDGPXFile(_sdFilePath);
    }
}


使用名为 sdFilePath 的参数将 SD 卡上的文件的路径从 MainPage.xaml 传递至 RoutePage.xaml。步骤 6 涵盖该代码。

文件关联在应用启动 URI 中包含标记 fileToken,该标记可用于访问正在启动的文件。如上一步骤所示,将该标记从 URI 映射器复制到页面,以便您的页面可以访问该文件。使用文件之前,先用 CopySharedFileAsync 将文件复制到您的应用的本地文件夹。如果您想在复制文件之前访问文件名称,则可以从页面或 URI 映射器中调用 GetSharedFileName 方法。在示例的 RoutePage.xaml 中,ProcessExternalGPXFile 方法使用该标记将文件复制到应用的本地文件夹。


// Process a route from a file association.
public async Task ProcessExternalGPXFile(string fileToken)
{
    // Create or open the routes folder.
    IStorageFolder routesFolder = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFolderAsync(ROUTES_FOLDER_NAME, CreationCollisionOption.OpenIfExists);

    // Get the full file name of the route (.GPX file) from the file association.
    string incomingRouteFilename = Windows.Phone.Storage.SharedAccess.SharedStorageAccessManager.GetSharedFileName(fileToken);

    // Copy the route (.GPX file) to the Routes folder.
    IStorageFile routeFile = await Windows.Phone.Storage.SharedAccess.SharedStorageAccessManager.CopySharedFileAsync((StorageFolder)routesFolder, incomingRouteFilename, NameCollisionOption.GenerateUniqueName, fileToken);

    // Create a stream for the route.
    var routeStream = await routeFile.OpenReadAsync();

    // Read the route data.
    ReadGPXFile(routeStream.AsStream());
}


本示例中所示的 ReadGPXFile 方法处理路由数据,并在地图控件上绘制它。有关在应用中使用地图的更多信息,请参见 Windows Phone 8 地图和导航

路由映射器示例将 SD 卡功能分为两个独立部分:扫描 SD 卡和打开 Path 中的文件。在以下代码中,应用扫描 SD 卡中的 GPX 文件,并将它们的文件路径保存至名为 RoutesObservableCollection<T>。在 MainPage.xaml 中,Routes 绑定到名为 gpxFilesListBox 的 ListBox,以便应用可以显示 SD 卡上的 GPX 文件的名称。


// Connect to the current SD card.
ExternalStorageDevice _sdCard = (await ExternalStorage.GetExternalStorageDevicesAsync()).FirstOrDefault();

// If the SD card is present, add GPX files to the Routes collection.
if (_sdCard != null)
{
    try
    {
        // Look for a folder on the SD card named Routes.
        ExternalStorageFolder routesFolder = await _sdCard.GetFolderAsync("Routes");

        // Get all files from the Routes folder.
        IEnumerable<ExternalStorageFile> routeFiles = await routesFolder.GetFilesAsync();

        // Add each GPX file to the Routes collection.
        foreach (ExternalStorageFile esf in routeFiles)
        {
            if (esf.Path.EndsWith(".gpx"))
            {
                Routes.Add(esf);
            }
        }
    }
    catch (FileNotFoundException)
    {
        // No Routes folder is present.
        MessageBox.Show("The Routes folder is missing on your SD card. Add a Routes folder containing at least one .GPX file and try again.");
    }
}
else 
{
    // No SD card is present.
    MessageBox.Show("The SD card is mssing. Insert an SD card that has a Routes folder containing at least one .GPX file and try again.");
}


当用户点击在 gpxFilesListBox 中显示的 GPX 文件时,MainPage.xaml 启动 RoutePage.xaml,并将文件路径作为 URI 参数来传递。因为 gpxFilesListBox 绑定到 RoutesRoutes 的类型是 ExternalStorageFile,所以可以使用 gpxFilesListBox_SelectionChanged_1 事件处理程序返回的 sender 参数来访问 SD 卡文件路径。创建 URI 时将访问 Path,如下所示。


// When a different route is selected, launch the RoutePage and send the file path with the URI.
private void gpxFilesListBox_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
    ListBox lb = (ListBox)sender;

    if (lb.SelectedItem != null)
    {
        ExternalStorageFile esf = (ExternalStorageFile)lb.SelectedItem;
        NavigationService.Navigate(new Uri("/RoutePage.xaml?sdFilePath=" + esf.Path, UriKind.Relative));
    }
}


最后,在 RoutePage.xaml 上,ProcessSDGPXFile 方法使用该路径获取 SD 卡上的文件,而且打开指向它的流以执行 ReadGPXFiles 方法。


// Process a route from the SD card.
private async Task ProcessSDGPXFile(string _sdFilePath)
{
    // Connect to the current SD card.
    ExternalStorageDevice sdCard = (await ExternalStorage.GetExternalStorageDevicesAsync()).FirstOrDefault();

    // If the SD card is present, get the route from the SD card.
    if (sdCard != null)
    {
        try
        {
            // Get the route (.GPX file) from the SD card.
            ExternalStorageFile file = await sdCard.GetFileAsync(_sdFilePath);

            // Create a stream for the route.
            Stream s = await file.OpenForReadAsync();

            // Read the route data.
            ReadGPXFile(s);
        }
        catch (FileNotFoundException)
        {
            // The route is not present on the SD card.
            MessageBox.Show("That route is missing on your SD card.");
        }
    }
    else
    {
        // No SD card is present.
        MessageBox.Show("The SD card is mssing. Insert an SD card that has a Routes folder containing at least one .GPX file and try again.");
    }
}


有关 ReadGPXFiles 如何在地图控件上显示路由的更多信息,请下载路由映射器示例

显示: