5.Step by Step

为了偷懒,我只是简单翻译一下一个国外朋友的博客文章,文章不错,我只是在此基础上做一点补充,并提供带Embedded XAML Runtime引擎的SDK(下一篇文章),让你可以在这个SDK提供的模拟器上测试你编写的SE Application,这样你不需要每次都编译一下CE OS的subproject。但是有一点需要申明,在模拟器跑起来的效果会很差,一方面因为没有硬件加速(没有硬件加速的情况下不可能使用任何技术实现即非常绚丽又快速的效果),另一方面PC上的显示器清晰度比一些CE高DPI的设备低很多。

1.使用Expression Blend 2生成XAML等资源

创建一个Silverlight application:


你需要使用Expression Blend 2 SP1,该版本支持Silverlight 2。从向导中你会看到你需要选择开发语言是C#还是Visual Basic,SE只能用C++编写,很可惜Expression Blend目前并不支持C++,所以我们不需要Expression Blend生成的任何代码。

那么怎样不让Expression Blend帮我们生成的C#或者VB.NET代码呢?请看下图:


创建一个新的项目之后,Expression Blend已经帮你生成好一个空XAML文件(默认叫page.xaml)。


我们从Silverlight提供的控件集中拖一个简单的按钮:


使用属性窗口我们给新按钮分配一个名称:


属性窗口允许你自定义按钮的各种属性:颜色、旋转角度(你可以让一个垂直的按钮旋转45度!)、透明度等等。

我们在下一篇文章中体验这些特性,现在我们仅仅保持按钮原样,并保存XAML文件。

如果你用一个记事本打开一个XAML文件你会发现它们非常简单:




1

2

3

4

5

6

7

8

9

10

11

12

13




​<​​​​UserControl​​​​xmlns​​​​=​​​​"​​http://schemas.microsoft.com/winfx/2006/xaml/presentation​​"​​   


​xmlns:x​​​​=​​​​"​​http://schemas.microsoft.com/winfx/2006/xaml​​"​​​​x:Class​​​​=​​​​"SimpleApp.Page"​


​Width​​​​=​​​​"640"​​​​Height​​​​=​​​​"480"​​​​>​


​    ​​​​<​​​​Grid​​​​x:Name​​​​=​​​​"LayoutRoot"​​​​Background​​​​=​​​​"White"​​​​>​


​        ​​​​<​​​​Button​​​​Height​​​​=​​​​"87"​​​​Margin​​​​=​​​​"189,106,209,0"​​​​VerticalAlignment​​​​=​​​​"Top"​​​​Content​​​​=​​​​"Button"​​​​x:Name​​​​=​​​​"MyButton"​​​​/>​


​    ​​​​</​​​​Grid​​​​>​


​</​​​​UserControl​​​​>​




UserControl作为顶部容器包含我们的按钮,Grid用于布局(对象被组织成以列和行的形式)。

按钮具有各种属性,比如在grid cell中的定位、对齐方式、content(字符串值“Button”)和name(字符串值”MyButton”)。


2.编码

到这一步我们做好了Design,下一步我们可以启动Platform Builder,在我们自己的OS Design项目上创建一个新的subproject。(在后面我会提供一个Alchemy SDK,在这个SDK上编写SE based Application你甚至不需要安装任何CE系统的开发环境。)

在solution窗口,右击新建一个subproject,创建一个WCE Application:


选择A simple Windows Embedded CE application:


这会创建一个应用程序仅仅包含WinMain函数,对与我们第一个SE应用程序来说已经够了。

第一件要做的事是包含一些头文件:



​​


1

2

3

4




​#include "pwinuser.h"​​​​#include "xamlruntime.h"​​​​#include "xrdelegate.h"​​​​#include "xrptr.h"​




下一步我们添加Expression Blend为我们生成好的XAML到程序资源中。

包含资源文件到exe文件中,我们需要添加一个资源脚本文件(rc文件)到subproject中:

在subproject上右击,选择”add\new item”并从对话框中选择资源文件并起一个名字:


创建一个新资源:


现在可以导入XAML文件,XAML中的数据将被以二进制的形式包含到exe文件中:


为新资源起个类型名字叫”XAML”:


这里为了方便我们保留资源默认的ID (IDR_XAML1)不变,在一个真正的项目中,起个meaningful names是更好的主意:


为了使用资源ID,我们在我们的cpp文件中需要包含"resource.h":



​​


1




​#include "resource.h"​




然后在WinMain中我们开始使用XAML运行时,首先需要初始化它:



​​


1

2




​if​​​​(!XamlRuntimeInitialize())       ​​​​return​​​​-1;​




如果XamlRuntimeInitialize执行成功,SE的运行时会被你的应用程序加载并准备好处理UI。

每个SE应用程序只有一个”Application”对象让我们用来访问全局应用程序的属性和特性。

为了访问它们,我们需要使用GetXRApplicationInstance API。



​​


1

2

3

4

5




​HRESULT​​​​retcode;​​​​ ​​​​IXRApplicationPtr app;​​​​ ​​ ​​ ​​​​if​​​​(FAILED(retcode=GetXRApplicationInstance(&app)))​​​​     ​​​​return​​​​-1;​



我们用Application对象做的第一件事是告诉它从哪里能找到资源文件(XAML,图片等)。

资源问题我们都已经包含到exe中了,所以我们传递我们的HINSTANCE句柄给它就可以了:



​​


1

2




​if​​​​(FAILED(retcode=app->AddResourceModule(hInstance)))​​​​    ​​​​return​​​​-1;​




现在我们已经初始化了Application对象,我们可以创建主窗口并让SE管理它的内容:



​​


1

2

3

4

5

6

7

8

9

10

11




​XRWindowCreateParams wp;​​​​ZeroMemory(&wp,​​​​sizeof​​​​(XRWindowCreateParams));​​​​wp.Style       = WS_OVERLAPPED;​​​​wp.pTitle      = L​​​​"S4E Test"​​​​;​​​​wp.Left        = 0;​​​​wp.Top         = 0;​​​​XRXamlSource xamlsrc;​​​​xamlsrc.SetResource(hInstance,TEXT(​​​​"XAML"​​​​),MAKEINTRESOURCE(IDR_XAML1));​​​​IXRVisualHostPtr vhost;​​​​if​​​​(FAILED(retcode=app->CreateHostFromXaml(&xamlsrc, &wp, &vhost)))​​​​    ​​​​return​​​​-1;​




VisualHost对象"hosts"运行时并让我们可以访问它的内容,以及从资源中加载我们的XAML(使用XRXamlSource对象)。

SE应用程序中的对象以对象树的方式组织起来。访问对象树中的对象,我们需要得到指向这棵树根部(root)的指针:



​​


1

2

3




​IXRFrameworkElementPtr root;​​​​if​​​​(FAILED(retcode=vhost->GetRootElement(&root)))​​​​    ​​​​return​​​​-1;​




从树的根部我们可以通过在Expression Blend中指定的name来访问我们的按钮:



​​


1

2

3

4




​IXRButtonBasePtr btn;​


​if​​​​(FAILED(retcode=root->FindName(TEXT(​​​​"MyButton"​​​​), &btn)))​​​​    ​​​​return​​​​-1;​




为了能够收到用户点击按钮时的通知,我们需要提供一个委托(delegate)。使用C#开发的朋友对委托的概念应该比较清楚,在这里一个委托即是一个指向具有指定原型(prototype)的C++实例的成员的指针。 

我们可以在我们的cpp文件中声明一个简单的C++类,并在其中实现按钮点击事件的委托:



​​


1

2

3

4

5

6

7

8

9




​class​​​​BtnEventHandler​​​​{​​​​public​​​​:​​​​    ​​​​HRESULT​​​​OnClick(IXRDependencyObject* source,XRMouseButtonEventArgs* args)​​​​    ​​​​{​​​​        ​​​​MessageBox(NULL,TEXT(​​​​"Click!"​​​​),TEXT(​​​​"Silverlight for Windows Embedded test"​​​​),MB_OK);​​​​        ​​​​return​​​​S_OK;​​​​    ​​​​}​​​​};​




当按钮被点击时我们的事件处理器(event handler)仅仅是简单的显示一个消息框。

正如你看到的事件处理器(event handler)有2个参数:一个指向产生事件(我们的按钮)的对象的指针和一个包含事件参数的结构体的指针。 

为了连接事件处理器(event handler)和按钮,我们需要创建一个委托对象:



​​


1

2

3

4

5

6




​BtnEventHandler handler;​​​​IXRDelegate<XRMouseButtonEventArgs>* clickdelegate;​​​​if​​​​(FAILED(retcode=CreateDelegate(&handler,&BtnEventHandler::OnClick,&clickdelegate)))​​​​    ​​​​return​​​​-1;​​​​if​​​​(FAILED(retcode=btn->AddClickEventHandler(clickdelegate)))​​​​    ​​​​return​​​​-1;​




事件处理器已经连上我们的按钮了,现在只需要等待用户去点击我们漂亮的按钮并显示我们的UI:



​​


1

2

3




​UINT​​​​exitcode;​​​​if​​​​(FAILED(retcode=vhost->StartDialog(&exitcode)))​​​​    ​​​​return​​​​-1;​




指向委托对象的指针并不是一个智能指针(smart pointer),我们需要显式释放它:



​​


1




​clickdelegate->Release();​




在我们build我们的subproject前我们需要添加头文件所在的目录和一些SE需要的库文件。

右击subproject打开source script添加如下代码:



​​


1

2

3

4

5

6




​INCLUDES=$(_OEMINCPATH)​​​​and libraries:​​​​TARGETLIBS= \​​​​    ​​​​$(_PROJECTROOT)\cesysgen\sdk\lib\$(_CPUINDPATH)\coredll.lib \​​​​    ​​​​$(_PROJECTROOT)\cesysgen\sdk\lib\$(_CPUINDPATH)\xamlruntime.lib \​​​​    ​​​​$(_PROJECTROOT)\cesysgen\sdk\lib\$(_CPUINDPATH)\uuid.lib \​




(你需要在sources script 中已经包含了TARGETLIBS directive)

现在你可以在设备上跑起来应用程序并点击按钮了!

你可以从这里下载到Demo代码: