开始使用 Mango — 开发人员视角





0/5 (0投票)
本文档解释了从 Windows Phone 7 平台迁移到 Mango 升级的过程。它还解释了 Mango 支持是如何实现的,并讨论了如何同时支持不同的 Windows Phone 版本。
引言
不久前,微软发布了 Windows Phone 7 的更新,代号为 Mango,带来了许多新功能。旧的(WP7.0)手机和 Mango(WP7.1)手机之间将存在显著差异。虽然 Mango 开发者工具是免费提供的,但第一批真机预计将在几周内上市。
对于每一个操作系统更新,开发者都需要解决一系列问题,从迁移到新技术、同时为不同平台开发,到高效利用新功能。
Windows Phone 7 是 Resco 的核心平台之一,我们在此提供了一个控件库 — Resco MobileForms Toolkit, Windows Phone Edition (Resco.Controls)。我们的用户开始询问该库是否与 Mango 兼容。在两种情况下,我甚至收到信号说 AdvancedList 控件可能存在问题。
好的,随着开发者对 Mango 的兴趣日益增长,我们别无选择,只能加入。下面我将分享我在迁移到 Mango,特别是其 Beta 2 版本时遇到的经验。
请注意,我不会讨论 Mango 的新功能,而是会谈论我遇到的问题。我还会尝试深入了解其内部机制,以便读者更好地理解 Mango 更新是如何实现的。
根据微软的说法,Mango 中的重大更改
WP7.0 和 WP7.1 之间的重大更改的官方列表可以在 WP SDK 7.1 版本说明中找到,并可以从 此处 下载。该列表包含许多非常具体的条目,这些条目不应影响大多数开发者。
然而,一些更改可能会影响现有代码,例如
- 在 WP7.1 中,“默认情况下,图像解码发生在后台线程上,而不是 UI 线程上。” 如 此处 所讨论的,后台解码可能会破坏现有代码。(例如,在调用 BitmapImage.SetSource 后立即无法使用图像尺寸;而必须等待 ImageOpened 事件。)微软可能会在最终 SDK 版本中撤销此更改。
- 在 WP7.0 中,WebClient 响应始终在 UI 线程上接收;而在 WP7.1 中,响应在创建请求的线程上接收。依赖于原始行为(例如,直接在响应处理中更新 UI)的开发者将遇到崩溃。此问题 在此处 有所讨论。
- WP7.1 提供了改进的 ListBox 滚动功能。此改进的代价是 a) ScrollViewer 属性的延迟更新;b) ScrollViewer Manipulation Delta 事件被抑制。有关解决方法,请参见 此处。
其他潜在的不兼容性或行为更改 在此处 有报告。
到目前为止,理论准备已经完成 — 我得出结论,迁移到 Mango 应该几乎不会有风险。
安装 Mango 支持
当然,我们必须从安装开始,这包括
- Visual Studio 2010 SP1(如果您还没有安装 — 请参见 Visual Studio 关于对话框)
- Windows Phone SDK 7.1 (Beta 2)
安装过程很漫长(由于网络下载),但很直接。
完成后,我打开了一个现有项目并查看了发生了什么变化。
表面上看没什么变化,但当我查看项目属性时,我发现了一个新选项 — **Target Windows Phone version**,您可以选择“WP7.0”或“WP7.1”。好的,这似乎是应该首先测试的内容。
测试现有项目
我总共测试了 8 个非平凡的应用程序,即所有基本的 Silverlight 控件以及 `Resco.Controls` 库中的所有自定义控件。代码使用了大量的绑定和模板化、动态控件构建、动画等。可能唯一没有测试的重要控件是 `Pivot` 和 `Panorama` — 也许下次吧。
每个项目都分两步进行测试
- 简单地重新编译未更改的旧项目,并在 a) “WP7.0”设备,b) “WP7.1”模拟器上运行。
- 将项目修改为面向“WP7.1”并在“WP7.1”模拟器上进行测试。
好吧,我将模拟器称为“WP7.1”,但这仅仅意味着安装后留下的模拟器。它看起来像 Mango 模拟器,但目前我只能说这么多。
测试结果
我必须说,结果比预期的要好:一个次要问题和一个严重问题。
第一个问题是此生成错误
"MSBUILD : error : Xap packaging failed. Could not find file '...ApplicationIcon.png'."
结果发现,我将 png 文件移动到了 `Images` 子文件夹,但没有更新 `WMAppManifest.xml` 文件。Mango 比旧 SDK 更严格;事实上,它正确地报告了一个现有错误。
第二个问题严重得多。我使用了一个基于 `ListBox` 的类,带有自定义 `ListBoxItem`。它在布局阶段崩溃,并出现一个无意义的错误。(非特定错误,没有任何信息。)简直是无从下手的情况。
在摸索并认为所有可用信息都无用后,我决定采取“暴力破解”方法:简化,简化,再简化……
可能花了十几次迭代,删除了 95% 的代码,我才发现 Mango 不喜欢这种构造(在 ListBoxItem 模板中使用)
<ControlTemplate>
<ContentPresenter Content="{TemplateBinding Content}"/>
</ControlTemplate>
一旦我意识到 ContentPresenter 在 ControlTemplate 中隐式绑定 Content,我就删除了 TemplateBinding,一切都正常工作了(即使在“WP7.0”情况下也是如此)
<ControlTemplate>
<ContentPresenter/>
</ControlTemplate>
(模板被简化了,但您能理解意思。)
新编译器
纯粹出于好奇:让我们先看看新编译器的影响。严格来说,这并不是 Mango 更新的结果,但如果您没有安装 VS SP1(我的情况),Mango 会要求您安装。
我拿了一个现有项目,保存了它的输出(即一个 dll 文件),然后重新编译以获得新的 dll。
旧/新输出 dll 的二进制比较显示了太多的差异,但我强烈感觉其中大部分是简单的代码偏移。
我在 `Reflector` 中打开了旧的和新的 dll,提取了源代码(顺便说一句,非常易读),然后进行了比较。结果:源代码基本相同。唯一的区别是形式上的 — 类声明中 `TemplatePartAttribute` 的顺序。
如预期的那样,编译器更改(希望如此)不会起任何作用。
实际安装了什么?
回到 Mango。让我们看看安装在计算机上的文件。
模拟器位于
C:\Program Files (x86)\Microsoft XDE
并且显然被 `7.1 SDK` 覆盖了。(不可逆的更改。)
旧手机程序集位于文件夹 _
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone_。
此文件夹包含安装在 Mango 之前手机上的所有系统程序集的副本。它一直存在,其内容显然不受新 SDK 安装的影响。相反,Mango 手机程序集被复制到新文件夹中,并排存在
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone71
看起来 7.0 和 7.1 环境都可用,并且模拟器等工具可以决定使用哪一个。目前这只是一个假设,但我们将在后面证明它。
有几点值得注意
- WP7.1 包含几个新的程序集(system.data.linq, mscorlib.extensions, 几个 XNA 程序集)。如果您想知道它们包含什么,请在 `Reflector` 中打开它们或阅读 Mango 文档。
- 大多数旧的 WP7 程序集已更改。
- 已更改的程序集保留了旧版本。
嗯,在过去的美好时光里,我们了解到版本是为了消除所谓的 dll 地狱,因为它们提供了唯一的标识。显然,独立平台(Silverlight、`.NET`、手机)的组合过于复杂,无法依赖版本信息。
无论如何,我的未来应用程序使用 Mango 特定系统类,却在 `WP7.0` 手机上运行的可能性理论上让我有点担心。
新概念 — Profile
嗯,“新”这个词并不完全准确。这个概念在 Mango 引入之前就存在了。只是因为当时只有一个配置文件,所以没有被强调。
微软使用“**Profile**”一词来表示“**Target Windows Phone version**”。目前有两个配置文件
- WindowsPhone
- WindowsPhone71
您可以轻松验证上述陈述:在二进制查看器中打开一个 dll 文件,查找字符串 `Profile`。
- 旧程序集(dll 文件)包含字符串 `Profile=WindowsPhone`。
- 正如您可能猜到的,为 Mango 构建的程序集使用字符串 `Profile=WindowsPhone71`。
这是否是防止上述冲突的关键概念?
WP7.0 和 WP7.1 项目有什么区别?
如前所述,项目属性提供了目标选择。但是,事情并非如此简单:旧项目可以升级到 `WP7.1`,但一旦这样做,Visual Studio 就不会让您将项目降级回 `WP7.0`。(相应的选项将丢失。)
另一方面,绕过此限制并不难。以下是如何将 `WindowsPhone71` 项目降级到 7.0 的方法
- 在 `*.csproj` 文件中
TargetFrameworkProfile: 将 `WindowsPhone71` 替换为 `WindowsPhone`
- 在 `WMAppManifest.xml` 中
AppPlatformVersion: 将 `7.1` 替换为 `7.0`
删除功能 `ID_CAP_ISV_CAMERA, ID_CAP_CONTACTS, ID_CAP_APPOINTMENTS`
- 在项目引用中
删除对 `mscorlib.extensions` 的引用。
如何为控件库创建安装程序
问题:我们有一个为旧 WP7 开发的控件库,即它不引用 Mango 特定功能。
- 我们可以为 Mango 手机提供此库吗?
- 如果可以,我们是否需要使用新的 Mango SDK 重新编译它?选择哪个目标?(7.0 或 7.1)我们需要提供两个二进制文件 — 每个目标一个吗?
当然,理想的解决方案是使用单个二进制文件。让我们找找看。
Google 和 `stackoverflow.com` 没有提供任何帮助,我只能靠自己。
我一如既往地开始进行实验。我创建了 `Resco.Controls.dll` 的两个版本 — 一个针对 `WP7.0`,另一个针对 `WP7.1`。我尝试在不同目标的项目中使用这些 dll。
请注意,您可以通过多种方式在应用程序项目中使用控件库
-
如果已安装的库与项目类型匹配,则会提供该库
对于 `WP7.0`,安装意味着库 dll 文件所在的文件夹会写入注册表项
HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Silverlight for Phone\v4.0\AssemblyFoldersEx.这样安装的库会在“Add References”对话框的“.NET”选项卡中提供,前提是它们的(配置文件)与项目目标匹配。换句话说,WP7.0 库提供给 WP7.0 项目,WP7.1 库提供给 WP7.1 项目。
-
通过浏览,您可以添加向下兼容的库
如果您使用“Add References”对话框的“Browse”选项卡,您可以选择任何库 dll 文件。因此,您可以添加一个兼容库的引用(如上所述),也可以从 `WP7.1` 项目添加对 `WP7.0` 库的引用。
但是,您无法从 `WP7.0` 项目引用 `WP7.1` 库。(您会收到错误:“无法将对更高版本或不兼容程序集的引用添加到项目中。”)
-
通过破解 xap 文件,您可以添加任何库
`Xap` 文件是您项目的最终产品,用作应用程序安装程序。它是一个 `zip` 文件,因此您可以浏览其内容。(去做吧,这很有启发性。您会在这里找到您的应用程序 dll,以及库 dll。)
令人惊讶的是,您可以修改 `Xap` 文件,将另一个版本的控件库放入其中。这绕过了任何兼容性检查,因为这样您就可以使用 `WP7.1` 库安装 `WP7.0` 应用程序。
我在已解锁的 `WP7.0` 设备上进行了此操作,并成功运行了一个使用为 WP7.1 目标构建的 Resco.Control 库的 WP7.0 应用程序。也许 Marketplace 会使用更强的安全检查来阻止此类技巧。
有趣的结果,但它们没有回答最初的问题。
我决定进行最后一次尝试 — 安装 Microsoft 的 Mango 版 `Silverlight for Windows Phone Toolkit` 并分析 `Registry` 更改。
`WP7.1` 库似乎在以下位置注册
HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Silverlight for WindowsPhone\v4.0\AssemblyFoldersEx
知道了以上信息,我创建了一个新的库安装程序,它使用了 `WP7.0` 库并将其注册在上述两个 `Registry` 文件夹中。
这就是 — 单个二进制文件服务于所有类型的项目。(即,该库可以从“Add References”对话框的“.NET”选项卡添加到 `WP7.0` 和 `WP7.1` 项目。)
Visual Studio 和模拟器中共存 WP7.0 和 WP7.1
上面我提出了一个假设,即 Mango 模拟器读取应用程序配置文件并决定使用 `WP7.0` 还是 `WP7.1` 库。下面我将对此进行证明。我甚至会展示 Visual Studio 在编译项目时也是如此。
让我们一步一步来,从测试应用程序的描述开始,该应用程序充当了实验室。
一天,我注意到 Mango 应用使用不同的数字键盘。这引起了我的注意,我编写了一个测试所有可能键盘的应用程序。让我们开始解释该应用程序的功能。
![]() |
![]() |
图 6 — 带有数字键盘的 TextBox,WP7.0
|
图 7 — WP7.1 的同一个 TextBox
|
编写测试应用程序
键盘通常在 Xaml 中定义,例如
<TextBox InputScope="Number"/>.
`XAML` 语法简单直观,但它不适合我的目的 — 我想找到键盘处理方面的所有差异。上面 `Xaml` 代码的 `C#` 等效代码在这里
var scopeName = new InputScopeName() { NameValue = InputScopeNameValue.Number };
var keyboard = new InputScope();
keyboard.Names.Add( scopeName );
TextBox box = new TextBox() { InputScope = keyboard };
(如果您问为什么 `XAML` 如此简单,那是因为它使用了 `InputScopeNameConverter`,它通过从字符串构建 `InputScope` 来完成繁重的工作。)
枚举 `InputScopeNameValue` 包含了所有可能的键盘类型。显然,解决方案是为每种可能的键盘(即每个 `InputScopeNameValue` 成员)构建一个 `TextBox`。
问题在于 `InputScopeNameValue` 是一个很长的 `enum`,我有点懒。此外,我不确定 `enum` 的定义在 Mango 中是否已更改。(嗯,此刻我理论上承认了这一点,但无法想象这将如何完成。)
我决定通过代码枚举 `InputScopeNameValue` 的值。`.NET` 提供了方便的方法 — `Enum.GetValues`。不幸的是,此方法已从 Silverlight 库中删除。
没问题,这段代码可以完成任务
public static IEnumerable GetEnumValues(Type enumeration)
{
IList<object> enums = new Collection<object>();
if (enumeration.IsEnum)
{
foreach (FieldInfo fieldInfo in
enumeration.GetFields(BindingFlags.Static | BindingFlags.Public))
enums.Add((Enum)fieldInfo.GetValue(enumeration));
}
return enums;
}
其余的很简单 — 我为每种键盘类型创建了一个 `TextBox`,并将其添加到网格中,同时添加了一个标签(`TextBlock`)来描述使用的键盘。这是代码
foreach (InputScopeNameValue scope in GetEnumValues(typeof(InputScopeNameValue)))
{
TextBlock txt = new TextBlock() { Text=scope.ToString(), FontSize=15 };
var keyboard = new InputScope();
var scopeName = new InputScopeName() {NameValue = scope};
keyboard.Names.Add( scopeName );
TextBox box = new TextBox() {InputScope = keyboard};
// Omitted code: Adding txt/box elements to the grid
}
所以我们有一个可以枚举所有键盘并将其应用于各自 `TextBox` 的应用程序。让我们测试一下。我第一次运行应用程序就立即崩溃了。
好吧,让我们看看调试器怎么说……它显示在 `InputScopeName` 构造函数中崩溃。因为代码是正确的,唯一的解释是某些 `InputScopeNameValue` 是非法的。
令人惊讶的是…… `msdn` 的说法恰恰相反。但好吧,我在 `InputScopeName` 构造周围添加了 try/catch,并在发生异常时,用文本“Unsupported”填充 TextBox。下图显示了结果。
所以,我有一个可以测试每一个键盘的 `WP7.0` 应用程序。接下来,我创建了一个完全相同的项目并将其目标设置为 `WP7.1`。
最后,我终于可以开始比较 `WP7.0` 与 `WP7.1` 了。
两个平台都支持的键盘
两个平台(`WP7.0` 和 `WP7.1`)都显示相同的不支持的键盘。您在上面的图片中可以看到所有这些;其余的(超过 50 种键盘)工作良好。
`WP7.1` 提供了两种新键盘 — `NumericPassword` 和 `Formula`。您在 `msdn` 中找不到它们,但如果您打开 `InputScopeNameValue` 的元数据(右键单击 `InputScopeNameValue`,然后选择“转到定义”),您会看到此注释:“*请勿使用*”。
此外,在打开所有 `TextBoxes` 后,似乎(我不会发誓)唯一具有不同视觉外观的键盘是那些带有数字字符的键盘。(`DateDay`、`DateMonth`、`DateYear`、`TimeHour`、`TimeMinorSec`……)
从元数据中可以读到什么
上面我解释了如何从元数据中获取 `InputScopeNameValue` 的定义。如果您在两个项目中执行相同的操作,您可以比较 `WP7.0`/`WP7.1` 定义的 `enum`。这是 `WP7.0` 代码的一部分
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone\System.Windows.dll
using System;
namespace System.Windows.Input
{
public enum InputScopeNameValue
{
// Summary:
// Not supported. For internal use in Silverlight for Windows Phone.
EnumString = -5,
//
// Summary:
// The text input pattern for XML.
Xml = -4,
……………………………………………………………
//
// Summary:
// The SIP layout for entering a map location.
Maps = 55,
//
// Summary:
// Not supported. For internal use in Silverlight for Windows Phone.
ApplicationEnd = 55,
}
}
`WP7.1` 代码非常相似。`enum` 定义中唯一的区别是添加的两个范围,即 `NumericPassword` 和 `Formula`。除了一个细微的区别,如果两个 `enums` 都未在 `diff tool` 中打开,我可能会忽略它。那就是标题行,它读取
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone71\System.Windows.dll
注释指向用于生成显示 `enum` 定义的程序集 dll 文件。(与我列出手机 dll 安装位置的章节进行比较。)生成的代码的一部分 — 即注释 — 来自伴随 dll 文件的 `System.Windows.xml` 文件。此文件充当 Visual Studio 上下文帮助和智能提示的数据源。
看到了它的含义吗?
- Visual Studio 根据项目目标链接不同的手机 dll。
- Visual Studio 的上下文帮助也取决于项目目标。
在模拟器上同时运行 WP7.0/WP7.1 应用
在测试键盘应用程序时,它们同时安装在 Mango 模拟器上。
现在,当您运行 `WP7.0` 应用程序时,它显示 `WP7.0` 键盘。反之亦然 — `WP7.1` 应用程序显示 `WP7.1` 键盘。在同一个模拟器上!
对我来说,这证明了模拟器的行为就像 Visual Studio — 它查看应用程序的目标(配置文件),并动态链接到相应的 .Net 程序集。
这并不是我感到惊讶(我早就预料到了),但当你的假设得到证实时,还是很令人高兴的。
摘要
代码兼容性
- 旧的 WP7.0 代码应该可以在 Mango 手机上运行,尽管偶尔存在不兼容性并非不可能。
- 根据官方文档,只有少数重大更改不应影响绝大多数开发者。
- 一些 API 更改甚至没有被微软报告,因为它们被认为是完全安全的。这种更改的一个例子是 InputScopeNameValue 枚举的扩展。
系统手机程序集
- 在安装 Mango 开发者工具时,两个 WP 平台的手机操作系统程序集(Microsoft.Phone.dll 等)会被复制到开发计算机上的不同文件夹中,并排存在。
- 文档 xml 文件(Microsoft.Phone.xml 等)被复制到相同的文件夹。
- WP7.1 包含所有 WP7.0 程序集。其中大部分未更改,但少数已更新。
- WP7.1 包含许多新的程序集,即开发者可以使用 API 的数量已大大增加。
- WP7.1 程序集的版本与 WP7.0 程序集相同。此版本指的是 Silverlight,因此不能用于区分手机目标。
Visual Studio
- VS 允许您选择项目目标 — WP7.0 或 WP7.1。
- 根据目标,VS 会选择合适的手机程序集。这些程序集随后用于多种用途,例如帮助系统、编译等。
- 项目输出(应用程序、控件库)包含有关其构建目标的信息。这些信息以配置文件字符串的形式出现,可以有两个值 — WindowsPhone 或 WindowsPhone71。
模拟器
- 您可以在同一个模拟器实例上并排安装 WP7.0 和 WP7.1 应用程序。
- 当应用程序启动时,Mango 模拟器会读取其配置文件并动态链接到相应的手机程序集。
- 这样,模拟器就可以正确模拟 WP7.0 和 WP7.1 设备。
控件库
- 控件库像普通应用程序一样包含配置文件字符串。因此,我们可以区分 WP7.0 和 WP7.1 库。
- 在开发应用程序时,您可以添加对兼容库的引用。唯一非法的组合是:您不能从 WP7.0 项目引用 WP7.1 库。
- 如果您的控件库兼容 WP7.0,您可以分发单个(WP7.0)二进制文件。在安装过程中,该库应被注册为同时兼容 WP7.0 和 WP7.1。
关于作者
Jan Slodicka。编程超过 30 年。涉足多种桌面平台和编程语言。自 2003 年起在 Resco 从事移动技术工作 — Palm OS、Windows Mobile、Windows Phone 7、Android。
您可以通过 `jano at resco.net` 或通过 Resco 论坛联系我。
`Resco MobileForms Toolkit — Windows Phone 7 Edition` 可从 http://www.resco.net/developer/mobileformstoolkit 下载。该工具包包含一组有用的控件,可简化 Windows Phone 7 编程。除了 WP7,还有 Windows Mobile、Android 和 iOS 版本。
Resco 是一家拥有悠久移动编程传统的公司,涵盖许多平台,以及面向最终用户和开发者的应用程序工具。