将 Windows Phone Silverlight 项目移植到 UWP 项目

上一主题是 命名空间和类映射

通过在 Visual Studio 中创建新的 Windows 10 项目并将文件复制到其中,开始移植过程。

创建项目并将文件复制到其中

  1. 启动 Microsoft Visual Studio 2015 并创建新的空白应用程序(Windows 通用)项目。 有关详细信息,请参阅 使用模板(C#、C++、Visual Basic)启动 Windows 运行时 8.x 应用。 新项目将生成将在所有设备系列上运行的应用包(appx 文件)。
  2. 在 Windows Phone Silverlight 应用项目中,标识要重复使用的所有源代码文件和视觉资产文件。 使用文件资源管理器,将数据模型、视图模型、视觉资产、资源字典、文件夹结构以及要重复使用的任何其他内容复制到新项目。 根据需要在磁盘上复制或创建子文件夹。
  3. 将视图(例如 MainPage.xaml 和 MainPage.xaml.cs)复制到新的项目节点。 同样,根据需要创建新的子文件夹,并从项目中删除现有视图。 但是,在过度写入或删除 Visual Studio 生成的视图之前,请保留副本,因为以后引用它可能很有用。 移植 Windows Phone Silverlight 应用的第一个阶段侧重于使其看起来良好且适用于一个设备系列。 稍后,你将注意确保视图能够很好地适应所有外形规格,并选择性地添加任何自适应代码,以充分利用特定设备系列。
  4. 解决方案资源管理器中,确保 显示所有文件 已打开。 选择您复制的文件,右键单击这些文件,然后单击“包含在项目中”。 这将自动包括它们所在的文件夹。 然后,可以根据需要切换 显示所有文件 关闭。 如果愿意,另一种工作流是使用 添加现有项 命令,在 Visual Studio 解决方案资源管理器中创建任何必要的子文件夹。 仔细检查你的视觉资源是否已将 生成操作 设置为 内容,并将 复制到输出目录 设置为 不复制
  5. 命名空间和类名的差异将在此阶段生成大量生成错误。 例如,如果打开 Visual Studio 生成的视图,则会看到它们的类型 Page,而不是 PhoneApplicationPage。 此移植指南中的以下主题详细介绍了许多 XAML 标记和命令性代码差异。 但是,只需执行以下常规步骤即可快速完成:在 XAML 标记的命名空间前缀声明中将“clr-namespace”更改为“using”;使用 命名空间和类映射 主题和 Visual Studio 的 查找和替换 命令对源代码进行批量更改(例如,将“System.Windows”替换为“Windows.UI.Xaml”):在 Visual Studio 的命令性代码编辑器中,使用 解析上下文菜单上 命令进行更有针对性的更改。

扩展软件开发工具包 (SDK)

移植的应用将调用的大多数通用 Windows 平台 (UWP) API 在称为通用设备系列的 API 集中实现。 但是,某些 API 在扩展 SDK 中实现,Visual Studio 仅识别由应用的目标设备系列或你引用的任何扩展 SDK 实现的 API。

如果您收到关于无法找到命名空间、类型或成员的编译错误,这可能是问题的根源。 在 API 参考文档中打开 API 的主题并导航到“要求”部分:该部分将告诉你实现的设备系列是什么。 如果这不是目标设备系列,那么若要使 API 可供项目使用,则需要对该设备系列的扩展 SDK 进行引用。

单击 项目>添加引用>Windows 通用>扩展 并选择相应的扩展 SDK。 例如,如果要调用的 API 仅在移动设备系列中可用,并且它们已在版本 10.0.x.y 中引入,则为 UWP选择 Windows 移动扩展。

这将向您的项目文件添加以下引用:

<ItemGroup>
    <SDKReference Include="WindowsMobile, Version=10.0.x.y">
        <Name>Windows Mobile Extensions for the UWP</Name>
    </SDKReference>
</ItemGroup>

名称和版本号与 SDK 的已安装位置中的文件夹匹配。 例如,上述信息与此文件夹名称匹配:

\Program Files (x86)\Windows Kits\10\Extension SDKs\WindowsMobile\10.0.x.y

除非应用面向实现 API 的设备系列,否则你需要使用 ApiInformation 类在调用 API 之前测试 API 是否存在(这称为自适应代码)。 然后,无论应用在何处运行,都会评估此条件,但只有在 API 存在且可供调用的设备上,条件才会被评估为 true。 在先检查通用 API 是否存在后,才使用扩展 SDK 和自适应代码。 下面部分提供了一些示例。

另请参阅 应用包清单

最大化标记和代码重用

你会发现,通过适量地重构代码以及/或添加自适应代码(如下所述),可以最大限度地利用能在所有设备系列中兼容的标记和代码。 下面是更多详细信息。

  • 通用于所有设备系列的文件不需要特别考虑。 这些文件将由应用在运行的所有设备系列上使用。 这包括 XAML 标记文件、命令性源代码文件和资产文件。
  • 你的应用可以检测它正在运行的设备系列,并导航到专为该设备系列设计的视图。 有关详细信息,请参阅 检测你的应用所运行的平台
  • 如果没有替代方法,可以找到一种类似的技术,即为标记文件或 ResourceDictionary 文件(或包含文件的文件夹)提供特殊名称,以便仅在应用在特定设备系列上运行时才自动在运行时加载它。 此技术在 Bookstore1 案例研究中进行了说明。
  • 若要使用所有设备系列(例如打印机、扫描仪或相机按钮)上不可用的功能,可以编写自适应代码。 请参阅本主题中 条件编译和自适应代码 的第三个示例。
  • 如果要同时支持 Windows Phone Silverlight 和 Windows 10,则可以在项目之间共享源代码文件。 方法如下:在 Visual Studio 中,右键单击 解决方案资源管理器中的项目,选择 添加现有项,选择要共享的文件,然后单击 添加方式链接。 将源代码文件存储在文件系统上的公用文件夹中,其中链接到它们的项目可以看到它们,不要忘记将它们添加到源代码管理。 如果可以考虑命令性源代码,以便文件的大部分(如果不是全部)都可以在两个平台上工作,则无需复制该文件的两个副本。 可以在文件内尽可能将任何特定于平台的逻辑包装在条件编译指令中,或在必要时使用运行时条件。 请参阅下一部分,C# 预处理器指令
  • 为了在二进制级别(而不是源代码级别)重复使用,有可移植类库支持 Windows Phone Silverlight 中提供的 .NET API 子集,以及适用于 Windows 10 应用(.NET Core)的子集。 可移植类库程序集与这些 .NET 平台等二进制兼容。 使用 Visual Studio 创建面向可移植类库的项目。 参见 跨平台开发使用可移植类库

条件编译和自适应代码

如果要在单个代码文件中同时支持 Windows Phone Silverlight 和 Windows 10,则可以执行此操作。 如果在项目属性页上查看 Windows 10 项目,你将看到该项目将WINDOWS_UAP定义为条件编译符号。 一般情况下,可以使用以下逻辑来执行条件编译。

#if WINDOWS_UAP
    // Code that you want to compile into the Windows 10/11 app.
#else
    // Code that you want to compile into the Windows Phone Silverlight app.
#endif // WINDOWS_UAP

如果您有在 Windows Phone Silverlight 应用程序和 Windows 运行时 8.x 应用程序之间共享的代码,那么您可能已经拥有以下所示逻辑的源代码:

#if NETFX_CORE
    // Code that you want to compile into the Windows Runtime 8.x app.
#else
    // Code that you want to compile into the Windows Phone Silverlight app.
#endif // NETFX_CORE

如果是这样,如果你现在想要支持 Windows 10,则你也可以这样做。

#if WINDOWS_UAP
    // Code that you want to compile into the Windows 10/11 app.
#else
#if NETFX_CORE
    // Code that you want to compile into the Windows Runtime 8.x app.
#else
    // Code that you want to compile into the Windows Phone Silverlight app.
#endif // NETFX_CORE
#endif // WINDOWS_UAP

你可能已经使用条件编译将处理硬件后退按钮限制在 Windows Phone 上。 在 Windows 10 中,后退按钮事件是一个通用概念。 在硬件或软件中实现的后退按钮都将引发 BackRequested 事件,因此这就是需要处理的事件。

       Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested +=
            this.ViewModelLocator_BackRequested;

...

    private void ViewModelLocator_BackRequested(object sender, Windows.UI.Core.BackRequestedEventArgs e)
    {
        // Handle the event.
    }

你可能已使用条件编译将处理硬件相机按钮的功能限制为仅适用于 Windows Phone。 在 Windows 10 中,硬件相机按钮是特定于移动设备系列的概念。 由于一个应用包将在所有设备上运行,因此我们使用所谓的自适应代码将编译时条件更改为运行时条件。 为此,我们使用 ApiInformation 类在运行时查询是否存在 HardwareButtons 类。 HardwareButtons 是在移动扩展 SDK 中定义的,因此我们需要向项目中添加对该 SDK 的引用,以便此代码进行编译。 但是,请注意,处理程序只能在实现移动扩展 SDK 中定义的类型(即移动设备系列)的设备上执行。 因此,以下代码段谨慎地使用现有的功能,尽管它是通过不同于条件编译的方式来实现的。

       // Note: Cache the value instead of querying it more than once.
        bool isHardwareButtonsAPIPresent = Windows.Foundation.Metadata.ApiInformation.IsTypePresent
            ("Windows.Phone.UI.Input.HardwareButtons");

        if (isHardwareButtonsAPIPresent)
        {
            Windows.Phone.UI.Input.HardwareButtons.CameraPressed +=
                this.HardwareButtons_CameraPressed;
        }

    ...

    private void HardwareButtons_CameraPressed(object sender, Windows.Phone.UI.Input.CameraEventArgs e)
    {
        // Handle the event.
    }

另请参阅 检测您的应用程序正在上运行的平台。

应用包清单

项目中的设置(包括任何扩展 SDK 引用)决定了应用程序可以调用的 API 范围。 但是,你的应用包清单决定了客户可以从应用商店安装应用的实际设备范围。 有关详细信息,请参阅 TargetDeviceFamily中的示例。

请务必了解如何编辑应用包清单,因为后续主题将讨论如何将其用于某些功能所需的各种声明、功能和其他设置。 可以使用 Visual Studio 应用包清单编辑器对其进行编辑。 如果没有显示 解决方案资源管理器,请从 视图 菜单中选择它。 双击 Package.appxmanifest。 这将打开清单编辑器窗口。 选择相应的选项卡进行更改,然后保存更改。 你可能希望确保移植的应用清单中的 pm:PhoneIdentity 元素与要移植的应用的应用清单中的内容匹配(有关详细信息,请参阅 pm:PhoneIdentity 主题)。

请参阅 Windows 10的 包清单架构参考。

下一主题是 故障排除