Yêu Cầu Hệ Thống Và Công Cụ
Để xây dựng ứng dụng Silverlight cho Windows Embedded trên nền tảng Windows CE 7.0, nhà phát triển cần chuẩn bị môi trường bao gồm Visual Studio 2008, Microsoft Expression Blend 3 và bộ SDK hỗ trợ Silverlight tương thích. Quy trình dưới đây hướng dẫn cách thiết lập cấu trúc project từ đầu, xử lý file cấu hình build và triển khai code C++ quản lý vòng đời ứng dụng.
Thiết Kế Giao Diện XAML
Sử dụng Microsoft Expression Blend 3 để khởi tạo một project dạng Silverlight for Windows Embedded Application. Trong môi trường này, chỉ tập trung vào việc thiết kế giao diện và đặt tên cho các điều khiển (control). Lưu ý rằng mã nguồn C# sinh ra từ công cụ này không được sử dụng trong project WinCE, chỉ lấy file XAML làm tài nguyên đầu vào.
Khởi Tạo Project Trong Visual Studio
Mở Visual Studio 2008, tạo mới một project thuộc loại Smart Device > Win32 Smart Device Project. Khi được yêu cầu chọn SDK, hãy đảm bảo chọn phiên bản có hỗ trợ Silverlight. Sau khi project được tạo, tiến hành làm sạch file mã nguồn chính.
Tại file code chính (ví dụ: EmbeddedApp.cpp), chỉ giữ lại khung hàm entry point cơ bản:
// EmbeddedApp.cpp : Điểm khởi đầu của ứng dụng
#include "stdafx.h"
#include "EmbeddedApp.h"
int WINAPI WinMain(HINSTANCE hInst,
HINSTANCE hPrevInst,
LPTSTR lpCmdLine,
int nCmdShow)
{
// Khởi tạo và chạy ứng dụng sẽ được thực hiện ở đây
return 0;
}
Cấu Hình Quy Trình Build (Build Rules)
Để chuyển đổi file XAML thành tài nguyên nhị phân mà runtime có thể đọc được, cần thêm một file quy tắc tùy chỉnh. Tạo file SleBuild.rules với nội dung cấu hình công cụ XRPack:
<?xml version="1.0" encoding="utf-8"?>
<VisualStudioToolFile
Name="SlePack Tool"
Version="8.00"
>
<Rules>
<CustomBuildRule
Name="SlePack Tool"
DisplayName="SlePack Tool"
CommandLine=""$(VSInstallDir)VC\VCWizards\WindowsEmbeddedSilverlightTools\XRPack.exe" "/Root=$(ProjectDir)" "/IntermediateFiles=$(OutDir)" "@$(InputPath)""
Outputs="$(InputName)Generated.h $(InputName)Generated.rc2"
FileExtensions="*.xrpack"
ExecutionDescription="Đang sinh file RC2 và Header..."
>
<Properties>
</Properties>
</CustomBuildRule>
</Rules>
</VisualStudioToolFile>
Tiếp theo, chỉnh sửa file project .vcproj để đăng ký file rules vừa tạo vào danh sách ToolFiles và kích hoạt công cụ trong phần cấu hình build.
Thiết Lập File Cấu Hình XRPack
Tạo file EmbeddedApp.xrpack để hướng dẫn công cụ đóng gói tài nguyên. File này sẽ chỉ định đường dẫn tới project gốc và tên file đầu ra:
# Cấu hình sinh tài nguyên cho Silverlight
/Verbosity=3
/NoResourceCompile
"/TargetRC=EmbeddedAppGenerated.rc2"
"/TargetHeader=EmbeddedAppGenerated.h"
"/Project=..\WinEmbeddedDemo\WinEmbeddedDemo.csproj"
Thêm file .xrpack này vào solution. Khi build, hệ thống sẽ tự động sinh ra file header và resource code tương ứng.
Triển Khai Mã Nguồn Cốt Lõi
Cập nhật file stdafx.h để bao gồm các thư viện runtime cần thiết cho XAML:
// Các header của Xaml Runtime
#include <XamlRuntime.h>
#include <XRDelegate.h>
#include <XRPtr.h>
#include <XRCustomControl.h>
// Định nghĩa GUID cơ bản
extern "C" const GUID __declspec(selectany)IID_IUnknown = __uuidof(IUnknown);
// Kiểu tài nguyên cho file XAML
#define RT_XAML L"XAML"
// Include các class ứng dụng
#include "SystemCore.h"
#include "resource.h"
Quản Lý Vòng Đời Ứng Dụng
Hàm WinMain sẽ chịu trách nhiệm khởi tạo đối tượng ứng dụng và chạy vòng lặp thông điệp:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPTSTR lpCmd, int nShow)
{
SystemCore coreApp;
HRESULT result = coreApp.Init(hInst);
if(SUCCEEDED(result))
{
result = coreApp.Execute();
}
return coreApp.GetExitCode();
}
Xử Lý Giao Diện Và Sự Kiện
File header cho view chính (DashboardView.h) cần kế thừa từ lớp cơ sở của control và định nghĩa ánh xạ tới file XAML:
#pragma once
class __declspec(uuid("{A1B2C3D4-5E6F-7A8B-9C0D-E1F2A3B4C5D6}")) DashboardView
: public XRCustomUserControlImpl<DashboardView>
{
QI_IDENTITY_MAPPING(DashboardView, XRCustomUserControlImpl)
public:
static HRESULT GetXamlSource(__in XRXamlSource* pSource)
{
HRESULT hr = E_INVALIDARG;
if (pSource)
{
pSource->SetResource(SystemCore::GetInstance(), IDR_DASHBOARD_VIEW);
hr = S_OK;
}
return hr;
}
static HRESULT Register()
{
return XRCustomUserControlImpl<DashboardView>::Register(__uuidof(DashboardView), L"DashboardView", L"clr-namespace:DemoNamespace");
}
#pragma region GeneratedCode
HRESULT OnLoaded(__in IXRDependencyObject* pRoot);
HRESULT SetupComponents();
IXRGridPtr m_pRootGrid;
IXRButtonBasePtr m_actionButton;
IXRDelegate<XRMouseButtonEventArgs> *m_eventDelegate;
#pragma endregion
};
Trong file implementation (DashboardView.cpp), xử lý sự kiện click cho nút bấm:
#include "stdafx.h"
#include "EmbeddedAppGenerated.h"
#include "DashboardView.h"
#include "SystemCore.h"
#define ENABLE_CLICK_HANDLER 1
#if ENABLE_CLICK_HANDLER
class ButtonActionHandler
{
public:
HRESULT OnClick(IXRDependencyObject* src, XRMouseButtonEventArgs* args)
{
MessageBox(NULL, TEXT("Đã Nhấn!"), TEXT("Silverlight WinCE Demo"), MB_OK);
return S_OK;
}
};
#endif
HRESULT DashboardView::OnLoaded(__in IXRDependencyObject* pRoot)
{
UNREFERENCED_PARAMETER(pRoot);
return SetupComponents();
}
HRESULT DashboardView::SetupComponents()
{
HRESULT hr = E_FAIL;
m_eventDelegate = NULL;
FindName(L"LayoutRoot", &m_pRootGrid);
#if ENABLE_CLICK_HANDLER
{
HRESULT ret = 0;
ButtonActionHandler handler;
if(FAILED(ret = FindName(TEXT("MainBtn"), &m_actionButton)))
return -1;
if(FAILED(ret = CreateDelegate(&handler, &ButtonActionHandler::OnClick, &m_eventDelegate)))
return -1;
if(FAILED(ret = m_actionButton->AddClickEventHandler(m_eventDelegate)))
return -1;
}
#endif
if (m_pRootGrid && m_actionButton)
{
hr = S_OK;
}
return hr;
}
Lớp Quản Lý Hệ Thống (SystemCore)
Class SystemCore (tương đương App class gốc) đảm nhiệm việc khởi tạo runtime, tạo host window và quản lý tài nguyên. Các biến thành viên như m_pVisualHost được đổi tên thành m_hostPtr để phân biệt:
#pragma once
#include <XamlRuntime.h>
typedef HRESULT (*PFN_REGISTER_VIEW)();
class SystemCore
{
public:
SystemCore(): m_bReady(FALSE), m_exitCode(0) {}
~SystemCore() {}
HRESULT Init(HINSTANCE hInst);
HRESULT CreateHost(XRWindowCreateParams* pParams);
HRESULT Execute();
HRESULT RegisterViews();
HRESULT GetWindowParams(XRWindowCreateParams* pParams);
int GetExitCode();
void SetExitCode(int code);
static HINSTANCE GetInstance();
HRESULT Shutdown();
HRESULT OnStartup();
HRESULT OnExit();
HRESULT SetupComponents();
static HRESULT GetVisualHost(IXRVisualHost** ppHost);
static HRESULT GetAppInterface(IXRApplication ** ppApp);
private:
static void SetVisualHost(IXRVisualHost* pHost);
static void SetAppInterface(IXRVisualHost* pApp);
protected:
BOOL m_bReady;
int m_exitCode;
static HINSTANCE m_hInst;
static IXRApplicationPtr m_appPtr;
static IXRVisualHostPtr m_hostPtr;
};
Phương thức khởi tạo trong SystemCore.cpp sẽ gọi các API public của XamlRuntime để thiết lập môi trường thực thi:
inline HRESULT SystemCore::Init(HINSTANCE hInst)
{
HRESULT hr = E_FAIL;
XRWindowCreateParams winParams = {0};
m_hInst = hInst;
BOOL initStatus = XamlRuntimeInitialize();
if (initStatus)
{
hr = GetXRApplicationInstance(&m_appPtr);
}
if (SUCCEEDED(hr))
{
hr = m_appPtr->AddResourceModule(m_hInst);
}
if (SUCCEEDED(hr)) hr = RegisterViews();
if (SUCCEEDED(hr)) hr = SetupComponents();
if (SUCCEEDED(hr)) hr = GetWindowParams(&winParams);
if (SUCCEEDED(hr)) hr = CreateHost(&winParams);
if (SUCCEEDED(hr)) hr = OnStartup();
return hr;
}
Xử Lý Lỗi Tài Nguyên
Trong quá trình biên dịch và chạy thử, ứng dụng có thể không khởi động được do lỗi tải tài nguyên XAML. Mã lỗi thường gặp là -2147023082 (không tìm thấy resource). Nguyên nhân thường do sự không đồng bộ giữa file .rc và file sinh tự động.
Để khắc phục, cần mở file resource của project (EmbeddedApp.rc) và đảm bảo rằng file include secondary là EmbeddedAppGenerated.rc2 thay vì tên mặc định ban đầu. Kiểm tra kỹ hai vị trí khai báo resource ID trong file này để đảm bảo chúng trỏ đúng đến file header đã được sinh ra bởi công cụ XRPack.
Sau khi điều chỉnh xong, tiến hành build lại project. Khi chạy trên thiết bị hoặc emulator, giao diện sẽ hiển thị chính xác và sự kiện click vào nút bấm sẽ kích hoạt hộp thoại thông báo như mong đợi.