Windows 运行时 8.x 到 UWP 案例研究:QuizGame 示例应用
本主题介绍将正常运行的对等测验游戏 WinRT 8.1 示例应用移植到 Windows 10 通用 Windows 平台 (UWP) 应用的案例研究。
通用 8.1 应用是生成同一应用的两个版本:一个适用于 Windows 8.1 的应用包,另一个适用于 Windows Phone 8.1 的应用包。 WinRT 8.1 版本的 QuizGame 使用通用 Windows 应用项目安排,但它采用不同的方法,并为这两个平台构建功能不同的应用。 Windows 8.1 应用包充当测验游戏会话的主机,而 Windows Phone 8.1 应用包充当主机的客户端角色。 测验游戏会话的两半通过对等网络进行通信。
为电脑和手机定制两半,这两个都有意义。 但是,如果可以同时在所选的任何设备上运行主机和客户端,那不会更好吗? 在本案例研究中,我们将这两个应用移植到 Windows 10,其中每个应用都将构建到一个应用包中,用户可以将其安装到各种设备上。
应用使用使用视图和视图模型的模式。 由于这种干净分离,此应用的移植过程非常简单,正如你将看到的。
请注意 ,此示例假定网络配置为发送和接收自定义 UDP 组多播数据包(大多数家庭网络可能不是工作网络)。 此示例还会发送和接收 TCP 数据包。
请注意,在 Visual Studio 中打开 QuizGame10 时,如果看到消息“需要 Visual Studio 更新”,请按照 TargetPlatformVersion 中的步骤操作。
下载
下载 QuizGame Universal 8.1 应用。 这是移植前应用的初始状态。
下载 QuizGame10 Windows 10 应用。 这是移植后应用的状态。
请参阅 GitHub 上此示例的最新版本。
WinRT 8.1 解决方案
以下是 QuizGame(我们要移植的应用)的外观。
Windows 上运行的 QuizGame 主机应用
Windows Phone上运行的 QuizGame 客户端应用
使用中的 QuizGame 演练
这是应用正在使用的简短假设帐户,但如果你想要通过无线网络自行试用该应用,它会提供有用的信息。
酒吧里正在进行一场有趣的测验游戏。 酒吧里有一台大电视,每个人都可以看到。 测验管理员有一台电脑,其输出正在电视上显示。 该电脑的“主机应用”正在运行。 任何想要参加测验的人只需在其手机或 Surface 上安装“客户端应用”。
主机应用处于大厅模式,在大电视上,它正在播发它可供客户端应用连接。 Joan 在移动设备上启动客户端应用。 她将她的名字键入“玩家名称”文本框并点击“加入游戏”。 主机应用通过显示她的姓名确认 Joan 已加入,而 Joan 的客户端应用表示它正在等待游戏开始。 接下来,麦克斯韦在移动设备上执行相同的步骤。
测验主持人单击“开始游戏”,然后主机应用显示一个问题和可能的答案(它还以正常字体粗细和灰色显示已加入玩家的列表)。 同时,答案显示在已加入客户端设备上的按钮上。 Joan 点击按钮,其中包含答案“1975”,其中所有按钮都处于禁用状态。 在主机应用中,琼的名字被画成绿色(并变得大胆),以承认收到她的答案。 麦克斯韦也回答。 测验主持人注意到所有玩家的名字都变为绿色后,单击“下一个问题”。
在同一周期中,问题继续被问及回答。 当最后一个问题显示在主机应用上时,按钮的内容是“显示结果”,而不是“下一个问题”。 当单击“显示结果”时,将显示结果。 通过单击“返回大厅”可返回到游戏开始时的状态,只是已加入的玩家仍然保持加入状态。 但是返回大厅为新玩家提供了加入的机会,甚至为已加入的玩家提供了便于离开的时间(尽管已加入玩家可随时通过点击“离开游戏”离开)。
本地测试模式
若要在单个电脑上试用应用及其交互,而不是分布在设备之间,可以在本地测试模式下生成主机应用。 此模式完全绕过网络使用。 相反,主机应用的 UI 会显示窗口左侧的主机部分,右侧是垂直堆叠的客户端应用 UI 的两个副本(请注意,在此版本中,本地测试模式 UI 固定用于电脑显示器;它不适应小型设备)。 这些 UI 段(全部在同一应用中)通过模拟客户端通信器相互通信,模拟通过网络进行的其他交互。
若要激活本地测试模式,请将 LOCALTESTMODEON (在项目属性中)定义为条件编译符号,然后重新生成。
移植到 Windows 10 项目
QuizGame 具有以下部分。
- P2PHelper。 这是一个包含对等网络逻辑的可移植类库。
- QuizGame.Windows。 这是为面向 Windows 8.1 的主机应用生成应用包的项目。
- QuizGame.WindowsPhone。 这是为面向 Windows Phone 8.1 的客户端应用生成应用包的项目。
- QuizGame.Shared。 这是包含其他两个项目使用的源代码、标记文件和其他资产和资源的项目。
对于此案例研究,我们有一些常见选项, 如你有一个通用 8.1 应用 ,说明要支持哪些设备。
根据这些选项,我们将 QuizGame.Windows 移植到名为 QuizGameHost 的新 Windows 10 项目。 而且,我们将 QuizGame.WindowsPhone 移植到名为 QuizGameClient 的新 Windows 10 项目。 这些项目将面向通用设备系列,因此它们将在任何设备上运行。 此外,我们会将 QuizGame.Shared 源文件等保留在其自己的文件夹中,并将这些共享文件链接到这两个新项目中。 就像以前一样,我们会将所有内容保留在一个解决方案中,我们将将其命名为 QuizGame10。
QuizGame10 解决方案
- 创建一个新的解决方案(“新建项目”>“其他项目类型”>“Visual Studio 解决方案”)并将其命名为 QuizGame10。
P2PHelper
- 在解决方案中,创建一个新的 Windows 10 类库项目(“新建项目”>“Windows 通用”>“类库(Windows 通用)”)并将其命名为 P2PHelper。
- 从新项目中删除Class1.cs。
- 将P2PSession.cs、P2PSessionClient.cs和P2PSessionHost.cs复制到新项目的文件夹中,并在新项目中包括复制的文件。
- 该项目将生成,而无需进行进一步的更改。
共享文件
- 将文件夹 Common、Model、View 和 ViewModel 从 \QuizGame.Shared\ 复制到 \QuizGame10\。
- 常见、模型、视图和 ViewModel 是指在磁盘上引用共享文件夹时的含义。
QuizGameHost
- 创建一个新的 Windows 10 应用项目(“添加”>“新建项目”>“Windows 通用”>“空白应用程序(Windows 通用)”),并将其命名为 QuizGameHost。
- 添加对 P2PHelper 的引用(“添加引用”>“项目”>“解决方案”>“P2PHelper”)。
- 在“解决方案资源管理器”中,为磁盘上的每个共享文件夹创建一个新文件夹。 反过来,右键单击你刚刚创建的每个文件夹,然后单击“添加”>“现有项”并向上导航到文件夹。 打开相应的共享文件夹、选择所有文件,然后单击“添加为链接”。
- 将 MainPage.xaml 从 \QuizGame.Windows\ 复制到 \QuizGameHost\ ,并将命名空间更改为 QuizGameHost。
- 将 App.xaml 从 \QuizGame.Shared\ 复制到 \QuizGameHost\,并将命名空间更改为 QuizGameHost。
- 我们不会覆盖app.xaml.cs,而是将版本保留在新项目中,只需对它进行一个有针对性的更改以支持本地测试模式。 在app.xaml.cs中,替换以下代码行:
rootFrame.Navigate(typeof(MainPage), e.Arguments);
替换为以下内容:
#if LOCALTESTMODEON
rootFrame.Navigate(typeof(TestView), e.Arguments);
#else
rootFrame.Navigate(typeof(MainPage), e.Arguments);
#endif
- 在“属性”>“生成”>“条件编译符号”中,添加 LOCALTESTMODEON。
- 现在,你将能够返回到添加到app.xaml.cs的代码并解析 TestView 类型。
- 在 package.appxmanifest 中,将功能名称从 internetClient 更改为 internetClientServer。
QuizGameClient
- 创建一个新的 Windows 10 应用项目(“添加”>“新建项目”>“Windows 通用”>“空白应用程序(Windows 通用)”),并将其命名为 QuizGameClient。
- 添加对 P2PHelper 的引用(“添加引用”>“项目”>“解决方案”>“P2PHelper”)。
- 在“解决方案资源管理器”中,为磁盘上的每个共享文件夹创建一个新文件夹。 反过来,右键单击你刚刚创建的每个文件夹,然后单击“添加”>“现有项”并向上导航到文件夹。 打开相应的共享文件夹、选择所有文件,然后单击“添加为链接”。
- 将 MainPage.xaml 从 \QuizGame.WindowsPhone\ 复制到 \QuizGameClient\,并将命名空间更改为 QuizGameClient。
- 将 App.xaml 从 \QuizGame.Shared\ 复制到 \QuizGameClient\,并将命名空间更改为 QuizGameClient。
- 在 package.appxmanifest 中,将功能名称从 internetClient 更改为 internetClientServer。
现在,你将能够生成并运行。
自适应 UI
当应用在宽窗口中运行时,QuizGameHost Windows 10 应用看起来很好(这只能在具有大屏幕的设备上运行)。 但是,当应用的窗口很窄(这发生在小型设备上,也可能发生在大型设备上),UI 被挤得太多,以至于无法读。
可以使用自适应视觉状态管理器功能来解决此问题,如案例研究:Bookstore2 中所述。 首先,设置视觉元素的属性,以便默认情况下,UI 以窄状态布局。 所有这些更改都发生在 \View\HostView.xaml 中。
- 在主网格中,将第一个 RowDefinition 的高度从“140”更改为“Auto”。
- 在包含名为
pageTitle
、setx:Name="pageTitleGrid"
和Height="60"
. 的 TextBlock 的网格上。 前两个步骤是这样,我们可以通过视觉状态中的 setter 有效地控制该 RowDefinition 的高度。 - 打开
pageTitle
,设置Margin="-30,0,0,0"
。 - 在注释
<!-- Content -->
指示的网格上,设置x:Name="contentGrid"
和Margin="-18,12,0,0"
。 - 在注释
<!-- Options -->
上方的 TextBlock 上,设置Margin="0,0,0,24"
。 - 在默认 TextBlock 样式(文件的第一个资源)中,将 FontSize setter 的值更改为“15”。
- 在
OptionContentControlStyle
中,将 FontSize setter 的值更改为“20”。 此步骤和上一步将给我们一个很好的类型渐变,适用于所有设备。 这些大小比我们用于 Windows 8.1 应用的“30”要灵活得多。 - 最后,将相应的 Visual State Manager 标记添加到根 网格。
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="WideState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="548"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="pageTitleGrid.Height" Value="140"/>
<Setter Target="pageTitle.Margin" Value="0,0,30,40"/>
<Setter Target="contentGrid.Margin" Value="40,40,0,0"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
通用样式
你会注意到,在 Windows 10 中,按钮的模板中没有相同的触摸目标填充。 两个小的更改将纠正这一点。 首先,将此标记添加到 QuizGameHost 和 QuizGameClient 中的 app.xaml。
<Style TargetType="Button">
<Setter Property="Margin" Value="12"/>
</Style>
第二,将此资源库添加到 \View\ClientView.xaml 中的 OptionButtonStyle
。
<Setter Property="Margin" Value="6"/>
最后一次调整后,应用的行为和外观与端口之前的行为相同,其附加值现在将随处运行。
结束语
在本案例研究中移植的应用相对复杂,涉及多个项目、一个类库,以及大量的代码和用户界面。 即便如此,端口也很简单。 移植的一些简便性直接归因于 Windows 10 开发人员平台与 Windows 8.1 和 Windows Phone 8.1 平台之间的相似性。 有些是由于原始应用的设计方式使模型、视图模型和视图分开。