从 C++ 向 C# 迁移
发布日期: 1/24/2005 | 更新日期: 1/24/2005
John Kennedy
Microsoft Corporation
本页内容
与生活中的许多事情一样,有时要学习某些知识的唯一方式就是亲自去尝试。当然,也有一些很显著的例外。我可不建议通过这种方式去学习外科手术(哈哈),但是对于学习智能设备扩展 (SDE) 和用 C# 编写适用于 Pocket PC 的程序,这绝对是一个很好的方式。
诚然,我们会越来越多地接触到 Microsoft .NET Compact Framework,不管是在本专栏还是在实际的开发环节中,这一点不容置疑。更加不容置疑的是,C# 是 .NET Compact Framework 用得最多的一种语言。用 C# 开发了一些程序之后,我对它的易用性、灵活性和优雅得体有着越来越深的印象。当然,当前发行的 Beta 版在某些地方还存在着所谓的操作“欠佳”的情况,但这不久就会得到解决。我坚信,一旦您继续了解一些新概念,您就会真正地喜欢使用 C# 和 .NET Compact Framework。
纸牌游戏
我们以前在本专栏中讨论过 XML Web 服务,但有些读者希望我能够使用智能设备扩展来创建其他一些连接性不大的编程项目。并不是每个软件都需要连接到 Internet,因此这个新平台中有些很强大的功能往往会被忽视。
所以在本月的专栏中,我们将演练一下如何开发一个适用于 Pocket PC 的更加传统的应用程序 — 实际上是一个游戏,它采用 C# 编写并使用 .NET Compact Framework。使用 C# 进行开发与使用 eMbedded Visual C++ 进行开发之间存在着一些很明显的差异,对于在编写这个程序时所碰到的一些很明显的问题,我希望拿出来与大家分享一下。
所要讨论的游戏非常简单。一个旧版本的纸牌匹配游戏 — Pelmanism。然而,要阐明一些重要的区别和方法还是很复杂的,所以即使您认为它很容易,也不妨试试。如果您要加载并试玩,可以下载源代码。
这个游戏的设计十分简单,正如您在以下屏幕截图中所看到的。(这些照片是我的团队(即 Visual C++ 小组)的所有成员。您可能会认为,现在当我找他们要求拍照时,他们会更加谨慎了。当然,我更愿意使用 Drew Barrymore 的照片,但有着严格的流程……)。屏幕上有十六张纸牌,玩家通过点击指示笔来“翻转它们”。如果纸牌相匹配,它们就会从屏幕中消失。我将留给读者一个练习 — 添加代码以便统计翻转次数、保存成绩等等。是的,这表明我在偷懒。不过要知道,我也忙乎了一天呢!第一个人在问我花多少时间在 Xbox 上玩 Halo 时怀着一种很鄙夷的目光。
图 1. 您知道,我被 Xbox 团队逼着讨论这张图的许可权。当然,实际上这是个谎言。
图形和 SDE
C# 程序与 eMbedded Visual C++ 的工作方式有着很大的不同,它从 Visual Basic 继承了窗体 的概念。从技术角度看,改变并不大,但我希望能够引起您的注意。您可能也知道,窗体其实就是一个页面,您可以在其中添加控件、显示文本、从中接收屏幕点击消息等等。当我们在 Visual Studio .NET 中创建一个默认项目时,系统会自动为我们提供一个默认窗体。
图 2. 一个空白窗体,准备让我们展现自己的想象力和才干。
您可以发现,窗体的左边是一组控件,我们可以将它们拖放到窗体中,也就是拖放到我们的程序中。与使用 eMbedded Visual C++ 工具箱相比,您会发现,编辑器中控件的可视化外观与因此而创建的源代码之间的联系更加紧密。试试一些控件并观察代码。Visual Studio 保持跟踪的方式给人的印象很深。
一句话警告源代码中有些地方不要手动进行更改。由于某种原因我忽视了这个告诫,当我在设计器中调整窗体时就丢失了代码,所以要谨记。
可以在窗体中添加的一个控件是 PictureBox,这是一个显示图像的控件。我打算对游戏所显示的每张纸牌都使用一个 PictureBox。它与最初采用 C++ 编写这个程序的方式有所不同。如果用 C++ 创建,我就需要创建窗口(现在我们称之为窗体)并获得 HDC(显示上下文句柄),然后使用 BitBlt 将一大堆图形数据变为实际的图形。这次,我打算使用十六个 PictureBox 控件,并使用它们的方法来定义它们显示什么图像。您将会看到,这种方式是相当得体的。
当然,知道我们要显示十六张纸牌,您可能会试着将十六个 PictureBox 拖到窗体中,然后调整它们的大小并将它们排列整齐。这是个好主意,但通过编程方式处理每个控件很快就会变成一项很琐碎的事情。更好的做法是创建一组 PictureBox,然后使用索引对它们进行处理,一会儿您将看到,我就是这样做的。所以,尽管设计器十分有用,但我们并不一定要使用它来添加 PictureBox 控件。
让我们来简单地看一些源代码。C# 程序在大部分地方很像 C++ 程序。比如函数、if() 语句、do/while 和大量圆括号。然而,也有一些细微(而非很显著)的区别。
举一些基本的例子,比如很好而又老旧的 #define 语句。我如何向您说明呢?它已经过时了。当然,它也没有完全过时,只是做了点更改。在 C# 程序中,#define 只能用于创建标识符。您不能使用它来创建常量,比如 #define X_WIDTH 100。
了解这一点之后,我们再看一些数组声明。需要二维整数数组吗?不要再认为是 int grid[4][4];,而应该是 int[,] grid = new int[4,4] ; 。
了解此信息之后,您应该能够明白游戏开头的这段代码有何用处:
图 3. 通过编程方式创建控件很容易,而且它们在编辑器中显示了所有方法以提示您下一步做什么。
这段代码声明了一些数组:一个用于存储玩纸牌时用到的 9 张不同的图像,一个用于存储可能存有纸牌的 16 个位置中的纸牌类型,一个用于存储 PictureBox 控件本身。
然后,我们按相同的方式处理所有这些 PictureBox 对象,创建它们、将它们添加到窗体中,并调整它们的大小和位置。请注意应用于每个对象的方法和属性(例如 Location)如何通过句点与对象分开。Visual Studio 集成开发环境 (IDE) 非常智能,一旦您键入句点,它就会弹出一个列表,其中包含您可以对这个对象执行的所有正确操作。这只是一个方面,说明了 C# 让人感到编写代码是一件很愉快的事。
如果您也和我一样,您就会喜欢在程序中添加 MessageBox 调用,以便随时了解发生的事情。使用智能设备扩展也可以拥有这样的能力:只需使用以下语法:
Messagebox.Show("Hello");
当然还有更多的选项,有关完整的列表,请参阅联机帮助。
考虑图像
想知道当我们玩纸牌时如何加载以显示一些图像吗?我也很想知道,因为 .NET Compact Framework 缺少 .NET Framework 的一些功能,连我首选的方法都没有。不过它仍然十分简单,如果您在通读示例源代码时稍加注意,就会发现它与 Image 对象有点关系。
下面是另一段代码。这是一个函数,通过调用它,可以从磁盘(其实是内存,只要您明白我的意思)加载图像,并将前面声明的图像数组中的元素分配给它。
图 4. 从内存中加载图像非常容易。
.bmp 文件是我在一个涂鸦程序中创建的图像集合,并预先对它进行缩放以适合 PictureBox 控件的大小。我习惯于使用 .bmp 格式,不过该控件也支持其他常见的文件格式。
我相信您已经很熟悉 try/catch 异常处理了,它是一种可选方式,不过建议您养成使用它的习惯。如果图像加载失败,就会执行 catch() { } 中的代码。这明显能使代码更加健壮可靠,不过如果能多做些事情(而不只是警告用户程序将要终止),那就更有用了。
如果您使用小巧的 Pocket PC 模拟器(它附带智能设备扩展)来试运行这个示例程序,您可能不知道如何将 .bmp 图像文件从开发用的计算机移到模拟的 Pocket PC 中。让我告诉你吧,因为这个过程与早期的 Pocket PC 开发工具相比有了一些更改 — 您一猜就知道我对它有很深的印象。
将文件复制到模拟器
1.
启动模拟器(如果尚未启动)。
2.
从 Start 菜单中,选择 Settings,然后选择 System 选项卡。
3.
单击 About,然后单击 DeviceID 标签。
4.
将设备名称从 Pocket_PC 改成其他的名称。
图 5. 更改设备 ID,否则当模拟器与桌面计算机通信时会报错。
5.
现在启动 Pocket PC 上的文件浏览器程序。
6.
点击网络共享图标。该图标位于屏幕底部的最右边。
7.
输入通过 LAN 访问您的计算机时所需的详细信息。
8.
浏览至桌面计算机上的共享文件夹,然后选择要复制的文件。
9.
点击 Pocket PC 上的文件并按住不放。
注 模拟器存在一个 bug,您可能需要按住鼠标键 30 秒钟才会看到红点以及随后的弹出菜单。
10.
复制文件,返回到 Pocket PC 自己的目录下并粘贴。
图 6. 浏览至桌面计算机上的共享文件夹,然后复制文件并将其粘贴到您的(模拟的)Pocket PC。
这就是所有的步骤。
郑重宣布,这就是这个小程序的全部内容了。当然,我还添加了一些菜单,使用窗体设计器,要多简单就有多简单。有一件事令我考虑了许久,那就是如何处理用户的指示笔点击操作。
我对 Visual C++ 程序的理论分析是这样的:占据整个屏幕并接受鼠标按下的消息,解出 X 和 Y 坐标,然后算出用户点击的纸牌。在这种情况下,每张纸牌都是一个控件,而每个控件都会单独地响应鼠标按下的消息。我无法包含那种具有全局性质的“点击屏幕并获得消息”代码。
然而,为每个控件指定处理程序是很简单的。这就是源代码的第一个代码块中的行:
this.pictureBox[x,y].Click += new System.EventHandler(this.UserTapsScreen);
您可以看到,我对每个控件都添加了相同的函数 (UserTapsScreen)。这明显有个疑问,那就是“在该函数中,我如何知道消息由哪个控件发出?”以下就是实现这一点的源代码:
private void UserTapsScreen(object sender, System.EventArgs e)
{
int cardx = -1;
int cardy = -1;
// Find the card that send the message
for (int x=0; x<4; x++)
for (int y=0; y<4; y++)
if (sender.Equals(pictureBox[x,y]))
{
cardx = x;
cardy = y;
y=4;x=4;
}...
请注意,这段代码使用了传递给 UserTapsScreen 函数的 sender 参数的一个属性,以及它的 Equals 方法。
声音效果
一个游戏要是没有声音效果那就毫无价值。这就是我们下个月要介绍的内容。同时,请下载源代码并试玩一下。如果您有任何问题,请随时给我发送电子邮件。但是,如果您是专门发送垃圾邮件的公司,打算一天要给我发送十二次电子邮件来向我提供一些衣着、电子和色情信息,那就另当别论了!
John Kennedy 白天是 Visual C++ 小组的技术作家/编程人员;而夜晚,他享受着 Pocket PC 开发人员的秘密生活。
Larry Roof 创立了 larryroof.com,这是一家专门从事移动项目咨询和 eMbedded Visual Basic、智能设备扩展和 SQL Server CE 方面培训的公司。