本文翻译自http://geekswithblogs.net/WindowsEmbeddedCookbook/archive/2009/11/11/xaml2cpp.aspx,介绍一个在Wince R3平台上开发silverlight程序的工具:xaml2cpp.
~在我开始解释这个话题前我需要坦白自己的很多缺点。我很懒。有些人可能会注意到我更新博客的频率,但是我真的想要尽可能多地避免工作。当然,我也愿意尝试新科技和嵌入式设备,但是这对一个懒人来说是个坏消息:因为我必须写一些代码。
当我试验Silverlight for Windows Embedded时,我发现我必须写一些更复杂的示例来继续我的指南,但是我讨厌去写所有的“八股文”:用来连接C++对象和XAML的代码,注册这些对象的事件处理函数。
为了能让你像我这么懒,我决定写个简单的工具来避免一次次的重写代码,因此我写了一个没有多少想象力的工具(懒的给它起个有想象力的名字...),它叫XAML2CPP。
XAML2CPP是个很简单的命令行工具,用来解析XAML文件并且为你生成一些C++代码。它会为XAML文件生成一个类(我假设每个文件里至少有个控件,因为这才有意义),里面有访问XAML中对象的代码和当事件被运行时创建后,调用事件处理函数的代码。
它也生成了一个“累积的”包含文件(为了避免了包含在你的源文件中包含每个类,你知道...我懒)和资源的定义(避免了你自己定义XAML资源)
它为每个含有名字的对象和事件处理函数生成了代码(内存不是个无限的资源,正像你在大学里写桌面程序一样,如果为所有东西声明对象和事件处理函数的话,那对我来说是个浪费)
让我们从一个简单的示例开始(我太懒,写不了一个复杂的!)
< UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:
Width="640" Height="480">
< Grid x: Background="White">
< Button Height="87" Margin="189,106,209,0" VerticalAlignment="Top" Content="Button" x: Click="OnClick"/>
< /Grid>
< /UserControl>
正像你看到的一样,这或多或少的像我们在指南中使用的XAML代码(对懒人来说,修改一个实例总比创建一个容易!)。我只不过为MyButton的Click事件处理函数了"OnClick"的名字。
如果我们用下面的命令行参数运行XAML2CPP:
XAML2CPP Page.XAML
它将会生成5个源文件:
XAML2CPP.rc
XAML2CPP.h
XAML2CPPBase.h
XAML2CPP_res.h
T_Page.h
虽然我懒,但是我的工具不是,它生成了5个文件而不是1个!
从来不要改变这些文件里面的代码,如果你重新运行XAML2CPP它将会重写它们并且你的更改将会永远丢失(即使你不像我这么懒,一遍遍的重写重复的代码也是不智的)
我知道你的改变不会永远丢失因为你是个好的程序员,并且你有你的源文件的多重备份...但是不要改变这个概念:永远不要改变XAML2CPP生成的代码。
当然,永远不要使用与XAML2PP生成的文件相同的文件名来命名你自身的文件。
让我们来看看这些文件包含些什么,你怎样才能用它们和自身的程序进行集成。
XAML2CPP.rc包含了所有我们需要在exe中调用XAML的资源定义.
在这个例子中,它只会包含我们的Page.XAML文件(当然,你可以使用输入多个文件名和通配符来运行XAML2CPP,这样会生成多个XAML资源)
这些是XAML2CPP.rc的内容:
XAML_RESOURCE_Page XAML ".Page.xaml"
很简单,是不是?但是它至少让你少写一行代码。虽然我需要写在命令行后中写"xaml2cpp page.xaml",它不会节省太多的时间...让我们看看在其它文件中包含什么。
XAML2CPP_res.h包含了XAML生成的资源定义:
/*
This file has been generated by XAML2CPP tool.
Modifications to this source code may be overwritten without warning when the XAML2CPP tool is executed.
XAML2CPP (c) 2009 by Valter Minute ([email protected])
This code is provided as is and it's generated automatically. It's up to the developer to check that it works as expected.
*/
/*
This file includes all the resource identifiers of the XAML files embedded inside the application resources
*/
#ifndef XAML2CPP_RES_H
#define XAML2CPP_RES_H
#define IDR_XAML_Page TEXT("XAML_RESOURCE_Page")
#endif //XAML2CPP_RES_H
不包含我的版权声明,还让你少些了4行代码。不错吧!
XAML2CPPBase.h会包含相同的内容,它是XAML2PP生成类的基类(XAML2CPPBase)。即使它不一定必须,这个文件也会在每次XAML2CPP运行时生成。在这种情况下,即XAML2CPP更新了,基类的改变也不需要你的重新部署以及在所有使用到的工程中更新它(在某种情况下,变懒会阻止维护的噩梦)
这里会有一些成员和方法的定义,在版权声明后你会发现基类的定义:
class XAML2CPPBase{...}
在这个类中我们会发现一些XAML运行时对象:
// Pointer to the visual host IXRVisualHostPtr vhost; // Pointer to the root of the XAML visual tree IXRFrameworkElementPtr root;
和一些访问它们的方法(它在基类中被定义为protected)
// returns the visual host IXRVisualHost* GetVisualHost() { return vhost; } // returns the visual tree root IXRFrameworkElement* GetRoot() { return root; }
我们同样发现了2个字符串指针和一个初始化它们的构造函数:
// Pointer to the page title TCHAR* windowtitle; // Pointer to the resource name TCHAR* xamlresourceid; public:
XAML2CPPBase(TCHAR* windowtitle,TCHAR* xamlresourceid) { this->windowtitle=windowtitle; this->xamlresourceid=xamlresourceid; }
这些指针用来设置窗口标题,我们的可视主机(Visual host)(如果你的窗口没有标题栏的话,你可能不需要设置一个标题)和与XAML2CPPBase继承类相关联的XAML资源id。
这些代码被用作两项功能:设置窗口创建参数,加载XAML方法:
// Initializes Windows parameters,can be overridden in the user class to change its appearance
virtual void InitwindowParms(XRWindowCreateParams* wp)
{
wp->Style = WS_OVERLAPPED;
wp->pTitle = windowtitle;
wp->Left = 0;
wp->Top = 0;
}
// Set the XAML source path. By default loads the XAML that is included in the resources script
virtual void SetXAMLSource(HINSTANCE hInstance,XRXamlSource* xamlsrc)
{
xamlsrc->SetResource(hInstance,TEXT("XAML"),xamlresourceid);
}
在这种情况下我们会创建一个有标题的顶层窗口,我们传递它到构造函数中,并且我们会从与构造函数参数相关的资源中加载XAML类。
如果我们想改变这个行为我们可能在我们的继承类中重写InitwindowParms或者SetXAMLSource方法。
懒人喜欢C++的继承!
这些方法被CreateHost方法调用,它初始化我们的可视主机并且加载了我们的XAML:
// create the visual host and loads the XAML
virtual HRESULT CreateHost(HINSTANCE hInstance,IXRApplication* app)
{
HRESULT retcode;
XRWindowCreateParams wp;
ZeroMemory(&wp,sizeof(XRWindowCreateParams));
InitwindowParms(&wp);
XRXamlSource xamlsrc;
SetXAMLSource(hInstance,&xamlsrc);
if (Failed(retcode=app->CreateHostFromXaml(&xamlsrc,&wp,&vhost)))
return retcode;
if (Failed(retcode=vhost->GetRootElement(&root)))
return retcode;
return S_OK;
}
这个方法同样是虚函数,这样如果你需要在你的类中进行一些特殊的初始化 ,可以重写它。通常重写InitwindowParms和SetXAMLSource就足够了,但是一个在初始化时的虚函数调用不怎么的影响性能,所以我宁愿空出一些空间用来定制。
如果你重写CreateHost,请记住:其它的代码需要根节点,并且vhost智能指针会在该函数调用后初始化。
现在,让我们看看XAML2CPP.h中有什么。
/*
This file has been generated by XAML2CPP tool.
Modifications to this source code may be overwritten without warning when the XAML2CPP tool is executed.
XAML2CPP (c) 2009 by Valter Minute ([email protected])
This code is provided as is and it's generated automatically. It's up to the developer to check that it works as expected.
*/
/*
This header includes all the classes generated by XAML2CPP the last time it was executed.
*/
#ifndef XAML2CPP_H
#define XAML2CPP_H
#include "T_Page.h"
#endif //XAML2CPP_H
在这个文件中,你会发现它包含了XAML2CPP从XAML文件中生成类的头文件。这样,如果,你就在每个源文件中包含它,就能访问工具生成的每个类了。在预处理头文件中包含它的话(通常是stdafx.h),可以节省一些编译的时间。
现在,让我们看看T_Page.h的一些细节:
#ifndef Page_TEMPLATE_HEADER_FILE_H
#define Page_TEMPLATE_HEADER_FILE_H
#include "windows.h"
#include "pwinuser.h"
#include "xamlruntime.h"
#include "xrdelegate.h"
#include "xrptr.h"
#include "XAML2CPP_res.h"
在防止重复包含的#ifdef语句后你会发现你包含了一些用来编译需要编译Silverlight for Windows Embedded程序的头文件。这样会节省一些时间。
下面,我们看到了一个模板定义:
/*class generated by XAML2CPP from .Page.xaml
*/
template < class X>
class TPage : public XAML2CPPBase
{
...
}
我使用模板来连接事件处理程序和你自身的代码,而不是工具生成的代码。
我可能使用一个函数来调用我的类中的纯虚方法,并且让你在继承类中实现你的方法,但是这样可能会对性能有(少许的)影响。
我们有个包含2个参数的构造函数,可以把它们传递到XAML2CPPBase的构造函数。这样的话,你就可以改变窗口的标题和XAML资源id,当然,用工具生成的也没问题。
public: TPage(TCHAR* title=TEXT("Page"),TCHAR* xamlid=IDR_XAML_Page) : XAML2CPPBase(title,xamlid) { }
这样,我们就有了2个智能指针:
protected:
// XAML defined objects (declared as smart pointers) IXRGridPtr LayoutRoot; IXRButtonPtr MyButton;
工具使用你在XAML代码中的x:Name属性来命名它们(或者是你在Expression Blend中设置的属性)
这些智能指针需要和XAML对象绑定,这些代码就在Bindobjects方法里:
// binds objects smart pointers to objects created by the runtime
virtual HRESULT Bindobjects()
{
HRESULT retcode;
if (Failed(retcode=root->FindName(L"LayoutRoot",&LayoutRoot)))
return retcode;
if (Failed(retcode=root->FindName(L"MyButton",&MyButton)))
return retcode;
return S_OK;
}
这个函数里的代码看起来很无趣...但是你不需要写它,XAML2CPP已经为你生成好了,这样的话,就能防止输入错误的名称,或者在剪切和粘贴中遗忘某些东西哦了(我知道你使用剪切和复制来写这些代码,就像我一样!)
现在我们需要绑定事件处理函数到这些对象,这些是在BindEventHandlers里做的:
// binds event handlers to template class member functions
// should be called after Bindobjects
virtual HRESULT BindEventHandlers()
{
HRESULT retcode;
//declare your own event handler as
// HRESULT OnClick(IXRDependencyObject* source,XRMouseButtonEventArgs* args)
IXRDelegate< XRMouseButtonEventArgs>* OnClickDelegate;
retcode=CreateDelegate< X,XRMouseButtonEventArgs>((X*)this,&X::OnClick,&OnClickDelegate);
if (Failed(retcode))
return retcode;
if (Failed(retcode=MyButton->AddClickEventHandler(OnClickDelegate)))
return retcode;
OnClickDelegate->Release();
return S_OK;
}
正如你看到的一样,创建了一个代理(delegate),使用类中的OnClick方法传递到模板,那就是你自身的类了。你需要在你的类中声明OnClick方法,如果缺少的话,你会在CreateDelegate中看到一个编译错误。就在那一行上面,你会发现方法(OnClick)的原型,你可以用来剪切和粘贴到你的代码中。
现在我们知道了XAML2CPP是怎样运行的,会生成什么代码,我们还需知道如何把它整合到我们自身的代码中去。
你可以使用Platform Builder来创建一个空的Win32工程。
然后重复我们在Silverlight for Windows Embedded指南第一课所作的: http://geekswithblogs.net/WindowsEmbeddedCookbook/archive/2009/10/01/silverlight-for-embedded-tutorial.aspx
然后把你的XAML文件放到子工程中,再使用XAML2CPP运行它。你会在子工程中看到我们上面描述的文件。
在你的工程中添加一个rc文件,像我们在指南中描述的一样,但是不要添加XAML资源。
移向"resource view"标签,右击你的工程.rc文件,选择"包含资源..."(Resource includes...),在里面添加代码:
#include "XAML2CPP.rc"
然后关闭该对话框
这样的话我们就可以包含所有XAML2CPP为我们生成的资源,无需手动添加(试试吧)
现在你可以编辑你的main.cpp源文件,在源文件中需要包含"XAML2CPP.h":
#include "XAML2CPP.h"
现在可以定义一个类来实现XAML2CPP没有为你实现的代码:
class Page : public TPage< Page>
{
public:
HRESULT OnClick(IXRDependencyObject* source,XRMouseButtonEventArgs* args)
{
MessageBox(NULL,TEXT("Click!"),MB_OK);
return S_OK;
}
};
你可以看到这个类很简单。你只需要写一些代码(你甚至可以将函数原型从TPage.h中拷贝出来,这,多么适合懒人编程啊!)无需初始化代码,无需事件绑定代码。
让我们看看为了创建你自身的XAML UI,在WinMain函数中需要做些什么。
你还是需要添加运行时初始化代码(你可以从上一个例子中拷贝过来,所以还是不要太懒!)
WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdshow)
{
if (!XamlRuntimeInitialize())
return -1;
HRESULT retcode;
IXRApplicationPtr app;
if (Failed(retcode=GetXRApplicationInstance(&app)))
return -1;
}
Page page;
if (Failed(page.Init(hInstance,app)))
return -1;
UINT exitcode;
if (Failed(page.GetVisualHost()->StartDialog(&exitcode)))
return -1;
return 0;
你需要声明你的类实例,对它调用Init方法(传递程序的HINSTANCE和一个指向XAML运行时程序对象的指针),然后你可以通过可视主机的方法来访问它。
正像你看到的,XAML2CPP生成了一些“样板”代码,一些特性和初始化的封装。这不是一个大的框架,但是但愿你能喜欢它。
你可以从这里下载XAML2CPP:
http://cid-9b7b0aefe3514dc5.skydrive.live.com/self.aspx/.Public/XAML2CPP.zip
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。