随着移动市场的快速发展和各种技术的革新,人机交互已经成为衡量一个产品是否能在市场上赖以生存的关键因素。传统的把开发者和界面设计者融为一起的开发模式已经不能胜任当今社会对产品快速形成的需求。Android 使用XML 语言来划分这应用程序开发者和UI设计者的界限。这种思想在QT 和Wince 上也得到了快速的借鉴和推广。微软在09年发布wince6.0 R3 其中加入了Silverlight 作为本地应用程序UI是一大亮点,使用其自身设计的XAML 语言来设计UI。并通过Visual Studio 2005 作为开发环境把XAML可作为其中的资源来管理。虽然在Windows Embedded Compact 7 上的得以完善,并开发了WindowsEmbedded Silverlight Tools 来帮用户自动生成本地应用程序代码。但对于很多还在Wince 6.0 R3 版本的初级用户来说,使用其SilverLight 开发界面却是个迫不及待的期待。本文以自己对SilverLight 的浅识来写一个SilverLight的简单应用程序框架,帮助读者更快的在Wince6.0 上开发SilverLight 应用程序。
首先我们要使用BSP(BoardSupport Package)来编译一个OS Image。使用Platform Builder 打开你的OS 制作工程在Catalog Item View 标签页下的 CEBASE->Shell And User Interface-> User Interface 下选中SilverLight For Windows Embedded。如下图 :
图1
假如你的系统不支持中文,则可以在标签页的CEBASE->International->Locale Specific Support->Chinese->Fonts 我们选择 第二项, 这里为了减少占用空间的大小 。假如你想支持Wince 自带的某个输入法,则可以选择相应的输入法。 如下图所示 :
图2
然后再在 菜单栏的 项目->属性页 的配置属性页下面的 locale 标签页下选择中文。如下图: 此时再重新编译 Image即可 。
图3
此时 你的Image 已经支持SilverLight和中文显示,在开发应用程序的时候一般采用USB 来作为调试。假如你的系统支持USB,你使用Microsoft ActiveSync,则我们需要在OS 中选中相应的组件 CEBASE->Applications-End user 下的 ActiveSync 如下图:
图4
在开发 SilverLight 应用程序时,大多数人喜欢把其作为 Platform Buider的子工程来做,但我还是建议你使用SDK 开发,这样独立性强。首先导出SDK。然后安装,安装的时候不要选择全安装,我们不安装文档,避免安装错误。
安装完SDK后我们还需要做一件事情就是 在XamlRuntime.h头文件中加入
#include <pwinuser.h>
并在原先BSP中搜索到这个文件,把这个文件拷贝到你的SDK相应的头文件中,这是一个Bug,否则你的应用程序编译通不过提示某些结构体未定义。
首先建立工程,我们使用VS2008,新建项目的时候选择VC++-> 智能设备->W32智能设备项目,输入项目的名称,在下一步,下一步,在项目设置中选择“windows 应用程序”,附加中选择”空项目”。
Windows 的程序入是 WinMain函数, 新建一个WinMain.cpp在其中加入如下代码:
#include"stdAfx.h"
其中在 StdAfx.h中加入如下头文件:是SilverLight 运行所必须的
// Xaml Runtime Header Files
#include<XamlRuntime.h>
#include<XRDelegate.h>
#include<XRPtr.h>
#include<XRCustomControl.h>
INTWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhInstPrev,LPWSTRlpCmdLine, intnShowCmd) { AppAppInstance; HRESULThr=AppInstance.Initialize(hInstance); if(SUCCEEDED(hr)) { hr =AppInstance.Run(); } returnAppInstance.GetWinMainResultCode(); }
WinMain的第一个参数是应用程序实例句柄,第二个参数是先前的应用程序实例句柄,我们不需要,第三个参数是当命令行运行应用程序时,所传递的字符串参数,最后一个参数是命令行的参数个数。 当我们的应用程序在启动的时候选择不同的XMAL文件加载时,可以选择把要加载的XAML文件路径和文件名作为lpCmdLine 参数传进去。
在WinMain函数中,首先声明一个应用程序实例 AppInstance。 然后调用 AppInstance的initialize 并传递应用程序实例句柄。 若初始化成功则调用Run方法运行。在应用程序退出时我们调用AppInstance的GetWinMainResultCode() 方法获取返回处理和返回值,退出应用程序。 以上是 WindowsEmbedded Compact 7 的Windows Embedded Silverlight Tools 自动生成的代码,当然在Wince6上仍然适用,我们可以做一参考。
接下来实现App这样一个类和上述所说的方法。 新建一个App.cpp 和App.h 文件,在StdAfx.h中加入 #include “App.h” 并在头文件中添加如下代码:
classApp { public: App(): m_bInitialized(FALSE), m_nResult(0){} ~App() {} //初始化XamlRunTimeAPI,IXRApplication 对象指针 HRESULTInitialize(HINSTANCEhInstance); // 运行应用程序直到退出消息循环 HRESULTRun(); // 获取从WinMain退出的结果代码 intGetWinMainResultCode(); // 设置从WinMain退出的结果代码 voidSetWinMainResultCode(intnResult); // 获得应用程序实例 staticHINSTANCEGetHInstance(); // 退出应用程序 HRESULTExit(); //在消息泵退出时,可视化树和IXRApplication被销毁之前调用 HRESULTOnExit(); // 获取应用程序的可视化树 staticHRESULTGetVisualHost(IXRVisualHost**ppHost); // 获取应用程序 staticHRESULTGetApplication(IXRApplication**ppApp); protected: staticHINSTANCE m_hInstance; // The HINSTANCEof this process staticIXRApplicationPtr m_pApplication;// IXRApplication for this process staticIXRVisualHostPtr m_pVisualHost; // IXRVisualHost for this process private: // 为应用程序设置可视化树 staticvoidSetVisualHost(IXRVisualHost*pHost); // 为应用程序设置IXRApplication 对象 staticvoidSetApplication(IXRVisualHost*pApp); protected: BOOL m_bInitialized;//Initialization succeeded int m_nResult; // WinMain result code };
其中我们要关注的三个protected 底下三个成员变量:
m_hInstance: 为WinMain函数传递的应用程序实例句柄,为之后关于此实例的扩张使用。
m_pApplication: 为指向运行SilverLight 应用程序的单体对象,此对象用来加载管理分析XAML文件。
m_pVisualHost: 指向Windows(HWND)容器对象对象树,以便在运行时,用C++ 或XAML 创建的对象能否相应事件消息,并显示或隐藏其 XAML 或C++类创建的窗口。我们可以通过m_pVisualHost 获取XAML文件上所有的对象指针。其实我们可以简单的理解为XAML文件的脉络。通过它来找到 XAML文件中的我们需要添加事件相应或动画的对象。
Initialize中我们初始化 XamlRuntime,获得 m_pApplication 对象指针。在Run()方法中我们建立主界面对象,并把主界面对象的对象树设置给App 的对象树指针,并启动主界面显示。如下为 App的 方法实现:
#include"stdafx.h" // The MAX_LOADSTRINGconstant needs to be equal to or greater // than the length of thestring referenced by IDS_APP_TITLE #defineMAX_LOADSTRING 100 //============================================================================ // Static class memberinstantiation. //============================================================================ HINSTANCEApp::m_hInstance; //HINSTANCE of this process IXRApplicationPtrApp::m_pApplication; //IXRApplication for this process IXRVisualHostPtrApp::m_pVisualHost; //IXRVisualHost for this process HRESULTApp::Initialize(HINSTANCEhInstance) { HRESULThr=E_FAIL; XRWindowCreateParams WindowParameters= {0}; m_hInstance =hInstance; BOOLm_bInitialized=XamlRuntimeInitialize(); // Create IXRApplication instance if (m_bInitialized) { hr =GetXRApplicationInstance(&m_pApplication); } if (SUCCEEDED(hr)) { hr =m_pApplication->AddResourceModule(m_hInstance); } returnhr; } //Initialize // ============================================================================ // Run // // Description: Run the application until themessage pump exits. //============================================================================ HRESULTApp::Run() { HRESULThr=E_FAIL; UINTuiExitCode= 0; MainPage* pMainPage= new MainPage(MAKEINTRESOURCE(IDR_XAML_MAIN_PAGE),BasePage::ParamIsID); SetVisualHost(pMainPage->GetVisualHost()); if (m_pVisualHost!=NULL) { hr =m_pVisualHost->StartDialog(&uiExitCode); SetWinMainResultCode(uiExitCode); } OnExit(); delete pMainPage; m_pVisualHost =NULL; m_pApplication =NULL; if (m_bInitialized) { m_bInitialized =FALSE; XamlRuntimeUninitialize(); } m_hInstance=NULL; returnhr; } // Run //============================================================================ // GetWinMainResultCode // // Description: Get the result code to bereturned from WinMain //============================================================================ intApp::GetWinMainResultCode() { returnm_nResult; } //GetWinMainResultCode //============================================================================ // SetWinMainResultCode // // Description: Set the result code to bereturned from WinMain // // Parameters: nResult - The result code to be returned from WinMain //============================================================================ voidApp::SetWinMainResultCode(intnResult) { m_nResult =nResult; } //SetWinMainResultCode //============================================================================ // GetHInstance // // Description: Get the application HINSTANCE // ============================================================================ HINSTANCEApp::GetHInstance() { return App::m_hInstance; } //GetHInstance //============================================================================ // Exit // // Description: Exit the application //============================================================================ HRESULTApp::Exit() { HRESULThr=E_FAIL; if (NULL!=m_pVisualHost) { hr =m_pVisualHost->EndDialog(0); } returnhr; } // Exit //============================================================================ // GetVisualHost // // Gets the visual host forthis application //============================================================================ HRESULTApp::GetVisualHost(IXRVisualHost **ppHost) { if (!ppHost) returnE_INVALIDARG; if (App::m_pVisualHost) { *ppHost =m_pVisualHost; (*ppHost)->AddRef(); returnS_OK; } returnE_FAIL; } // ============================================================================ // SetVisualHost // // Sets the visual host forthis application //============================================================================ voidApp::SetVisualHost(IXRVisualHost*pHost) { // Smart pointer automatically calls AddRef m_pVisualHost =pHost; } //============================================================================ // GetApplication // // Gets IXRApplication forthis class // ============================================================================ HRESULTApp::GetApplication(IXRApplication**ppApp) { HRESULThr=E_FAIL; if (!ppApp) returnE_INVALIDARG; if (m_pApplication) { *ppApp =m_pApplication; (*ppApp)->AddRef(); hr =S_OK; } returnhr; } //============================================================================ // SetApplication // // Sets IXRApplication forthis class // ============================================================================ voidApp::SetApplication(IXRVisualHost*pApp) { // Smart pointer automatically calls AddRef m_pApplication =pApp; } //============================================================================ // OnExit // // Description: OnExit is called after themessage pump is exited // and before the visual host, andIXRApplication are destroyed. // ============================================================================ HRESULTApp::OnExit() { // TODO: Add one-time cleanup code here. returnS_OK; } // OnExit
如上所示 在Run() 方法中:
MainPage 是我们要根据特定的XMAL文件所对应的类,我们这里创建一个类的实例,在利用这个类的方法GetVisualHost() 获取一个对象树的指针,然后把这个对象树的指针设置给App的对象树成员,然后调用StartDialog()方法建立并显示一个模态对话框。
我们假设MainPage对象的构造函数有两个参数,第二个参数指定第一个参数传递的是XAML的路径还是XAML的资源ID,这里对于XAML文件我们有两种方式加入到我们的工程:一种是把XAML文件当成资源导入工程,这样做的好处是隐藏XAML的实现细节,并且是编译执行,效率高,但可执行文件大;第二种是在程序运行时动态加载XAML文件,这样的好处是当程序界面需要改变时,不需要从新编译应用程序,并且可执行文件较小。
当然由于每个XAML文件的加载 显示,都一样的,寻找它的根对象指针的方法都一样,为了避免代码的重复性我们实现了一个基类:BasePage。把共有的方法抽象出来放到基类中,然后让我们真正所要根XAML关联的类都从这个类继承。上述BasePage::ParamIsID为基类定义的一个宏,指示第一个参数所传递的为XAML的资源ID号。
现在我们再建立两个文件BasePage.h和BasePage.cpp 来实现 BasePage类。 下面为BasePage类以及方法的声明:
class BasePage { public: enum ParamType{ ParamIsID=0, ParamIsPath }; BasePage(); BasePage(const WCHAR *psz, ParamType type); virtual ~BasePage(); HRESULT SetXamlResources(constWCHAR *pID); HRESULT SetXamlFile(constWCHAR *psz); void SetWindowParams(XRWindowCreateParamsparm); HRESULT GetWindowParameters(XRWindowCreateParams*pWindowParameters); HRESULT CreateVisualHost(XRWindowCreateParams*pCreateParams); IXRVisualHostPtrGetVisualHost(); HRESULTShowWindow(); protected: XRWindowCreateParams m_windowParameters;// XRWindowCreateParams for window styles XRXamlSource m_xamlSource; // IXRVisualHostPtr m_pVisualHost; // IXRVisualHostfor this process IXRFrameworkElementPtr m_pRoot; // HWND m_hWnd; // the hwnd of the window #pragmaregionAddedConponentCode //============================================================================ // The conponent ofmain page you need to add //============================================================================ virtual HRESULT InitializeComponent(); virtual HRESULT AddEventHandler(); virtual HRESULT OnStartup(); IXRGridPtr m_pLayoutRoot; // <Gridx:Name="LayoutRoot"> IXRButtonPtr m_pShutDownBtn; // <Buttonx:Name="ShutDownBtn"> #pragmaendregionAddedConponentCode };
三个成员变量的说明:
m_windowParameters: 为保存当前对象设置的窗口属性;
m_xamlSource:加载XAML文件的对象;
m_pVisualHost: 指向当前XMAL对象的对象树;
m_pRoot:XAML文件框架的根,利用此对象的FindName()方法可找到其所有包含的子对象;
m_hWnd: 当前对象的窗口句柄, 多用于调用传统的W32 API函数作为其参数;
AddedConponentCode代码区域的函数分别为初始化公用对象,添加事件相应,对话框启动前只调用一次的方法。 这三个方法分别以虚方法来实现,若用户可根据自己的需求来重写这三个方法。
如下实际的方法实现:
#include"stdafx.h" BasePage::BasePage() { } BasePage::BasePage(constWCHAR *psz,ParamTypetype) { if(type == BasePage::ParamIsID){ SetXamlResources(psz); } else if(type ==BasePage::ParamIsPath){ SetXamlFile(psz); } else{ return; } GetWindowParameters(&m_windowParameters); CreateVisualHost(&m_windowParameters); InitializeComponent(); AddEventHandler(); OnStartup(); } BasePage::~BasePage() { } HRESULTBasePage::SetXamlResources(constWCHAR *pID) { HRESULT hr = S_OK; m_xamlSource.SetResource(App::GetHInstance(),TEXT("XAML"),pID); return hr; } HRESULTBasePage::SetXamlFile(constWCHAR *psz) { m_xamlSource.SetFile(psz); return true; } HRESULTBasePage::GetWindowParameters(XRWindowCreateParams*pWindowParameters) { static WCHAR szTitle[512]; // title bartext HRESULT hr = E_INVALIDARG; if (pWindowParameters) { ZeroMemory(pWindowParameters, sizeof(XRWindowCreateParams)); pWindowParameters->Style = WS_POPUP |WS_VISIBLE/*| WS_OVERLAPPED | WS_SYSMENU*/; pWindowParameters->ExStyle = WS_EX_TOPMOST; // Set thetitle bar text //LoadString(m_hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); pWindowParameters->pTitle = szTitle; // Set windowposition pWindowParameters->Left = 0; pWindowParameters->Top = 0; // TODO: Tospecify a window size for the visual host set Width and Height // If Widthand Height are zero the Width and Height specified in the // XAML areused //pWindowParameters->Width = GetSystemMetrics(SM_CXSCREEN); //pWindowParameters->Height = GetSystemMetrics(SM_CYSCREEN); hr =S_OK; } return hr; }// GetWindowParameters voidBasePage::SetWindowParams(XRWindowCreateParamsparm) { m_windowParameters= parm; } HRESULTBasePage::CreateVisualHost(XRWindowCreateParams*pCreateParams) { HRESULT hr = E_FAIL; IXRApplicationPtrpApp; App::GetApplication(&pApp); if(pCreateParams){ if(pApp){ hr=pApp->CreateHostFromXaml(&m_xamlSource,pCreateParams,&m_pVisualHost); m_windowParameters= *pCreateParams; hr=m_pVisualHost->GetContainerHWND(&m_hWnd); } } return hr; } IXRVisualHostPtrBasePage::GetVisualHost() { return m_pVisualHost; } HRESULT BasePage::ShowWindow() { HRESULT hr = E_FAIL; UINT exitCode; if(m_pVisualHost){ hr = m_pVisualHost->StartDialog(&exitCode); } return hr; } HRESULTBasePage::AddEventHandler() { HRESULT hr = E_FAIL ; // Add calls toFindName or Add___EventHandler() methods after this comment. hr = S_OK; return hr; } //============================================================================ // OnStartup // // Description:OnStartup is called after the visual host is created. // andbefore the message loop is entered. //============================================================================ HRESULTBasePage::OnStartup() { HRESULT hr = E_FAIL; return hr; }// OnStartup #pragmaregionAddedConponentCode //============================================================================ // The conponent of main page you need to add // After VisualHost is created ,call this method //============================================================================ HRESULTBasePage::InitializeComponent() { HRESULT hr = E_FAIL; if(m_pVisualHost){ hr = m_pVisualHost->GetRootElement(&m_pRoot); } return hr; } #pragmaendregionAddedConponentCode