Create a XAML for Windows Embedded Application (Compact 2013)
10/26/2015
The main building blocks of a XAML for Windows Embedded application are the XAML that defines the UI and the native C++ programming logic behind the UI. Windows Embedded Compact 2013 includes an API that you use to work with XAML for Windows Embedded objects, whether accessing objects that were created from XAML or generating new objects. All of the other Windows Embedded Compact APIs defined in the Reference section are available for you to use in your XAML for Windows Embedded application also.
Choose from the following options to create a XAML for Windows Embedded application for a Windows Embedded Compact powered device:
- You can design your UI (in XAML) in a Blend for Visual Studio project. You can then convert the Expression Blend project into a XAML for Windows Embedded project by using the Windows Embedded XAML Tools in Microsoft Visual Studio 2013 or Microsoft Visual Studio 2015, after which you add the C++ programming logic behind the UI.
- If you have Platform Builder installed, and you have an OS design project, use the procedure in Create Your Application as a Platform Builder Subproject and follow the optional steps for XAML for Windows Embedded.
- If you do not have Platform Builder installed, use the procedure in Create Your Application Using an SDK for an OS Image and follow the optional steps for XAML for Windows Embedded.
When you choose this option, Windows Embedded Compact generates all of the code that initializes the XAML runtime, prepares the visual tree, creates objects from XAML elements, and creates stubs and hooks for any event handler methods that were included in the XAML.
- You can create an application project by using one of the Windows Embedded Compact project templates in Visual Studio, and then manually add the code that initializes the XAML runtime, generates the visual tree, and so on.
This topic describes the second option—creating an application project by using of the Windows Embedded Compact project templates in Visual Studio. This topic also describes some other programming tasks that you can do with either option.
Prerequisites
The procedures in this topic require that you create a project for a Windows Embedded Compact application.
- If you have Platform Builder installed and you can work within an OS design project, use the procedure in Create Your Application as a Platform Builder Subproject. In the Subproject Wizard, select the WCE Application template.
- If you have an SDK for an OS design, use the procedure in Create Your Application Using an SDK for an OS Image.
For help deciding which method you want to use, see the table in Create a Project for a Windows Embedded Compact Application.
Whether you work in an OS design project or work with the SDK that was generated from an OS design project, the OS design must include support for XAML for Windows Embedded. For more information, see Include XAML for Windows Embedded in Your OS Design.
Step 1: Add XAML Files to your Project
To define the UI for your application in XAML instead of in C++ code, copy XAML files that define your UI to your subproject. If you are working in an OS design subproject, you can copy the XAML files to your Resource files folder.
The following example XAML defines a user control that contains a button and a rectangle. To use this XAML, save it in a file and add it to your project. To make your project more closely resemble a typical XAML for Windows Embedded project, you could name the file MainPage.xaml.
Important
For readability, the code examples in this topic do not contain security checking or error handling. Do not use the following code in a production environment.
<UserControl
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="TestXAML.MainPage"
Width="640" Height="480" mc:Ignorable="d">
<Grid x:Name="LayoutRoot" Background="White">
<Rectangle Fill="{StaticResource PeriwinkleBrush}"
Stroke="Black" StrokeThickness="2"
HorizontalAlignment="Center" Width="200" Height="200"/>
<!-- Alternatively, set the Fill attribute to #FF93ACFF -->
<Button Content="Button"
HorizontalAlignment="Center" Width="100" Height="50"/>
</Grid>
</UserControl>
The Rectangle uses a color resource that is defined in the following code block. To use this XAML, save it in a file named App.xaml and add it to your project. Resources are often defined in an App.xaml file.
<Application
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="TestXAML.App">
<Application.Resources>
<SolidColorBrush x:Key="PeriwinkleBrush" Color="#FF93ACFF"/>
</Application.Resources>
</Application>
You can use these XAML examples to work with the code examples in Step 3.
To include XAML files in the project
Add references to the XAML files to the subproject BIB file. In Solution Explorer, expand Subprojects, expand Parameter files, and then double-click the <ProjectName >.bib file to open it for editing.
Create a FILES section and add entries for the XAML files. For example, if you added the preceding code to your project, you could create a FILES section in the <ProjectName>.bib file that looks like the following:
FILES App.xaml App.xaml NK MainPage.xaml MainPage.xaml NK
For more information, see FILES Section in the Windows Embedded Compact Help.
Step 2: Create an Object That Has Event Handlers
Create a custom object that you will populate with method implementations for event handlers for all the interactive UI elements in your application, whether they are defined in XAML or created in C++.
You can create this object in <ProjectName>.cpp before the WinMain routine.
The following example template code shows how to define this custom object.
class CustomObject
{
public:
HRESULT OnKeyDown( IXRDependencyObject* pSender, XRKeyEventArgs* pArgs )
{
//event handler implementations
}
};
Step 3: Prepare the Visual Tree in XAML for Windows Embedded
To display the application UI and modify UI elements, you must first prepare XAML for Windows Embedded to run in an application by parsing a source XAML file to populate the visual tree with UI elements.
To complete the task, you initialize the system, load the source XAML file, generate an object tree, and begin routing and processing messages for the application.
To prepare the XAML for Windows Embedded visual tree
In the C++ source code file, include the header files that are required for XAML for Windows Embedded. For example:
#include "XamlRuntime.h" #include "XRDelegate.h" #include "XRPtr.h"
Include references to the XAML runtime library in your project by using one of the following options:
If your application project includes a Sources file, add a reference to the XAML runtime library in the TARGETLIBS section. For example, if the TARGETLIBS section in your sources file looks similar to the following:
TARGETLIBS= \ $(SG_OUTPUT_ROOT)\sdk\lib\$(_CPUINDPATH)\coredll.lib \
Add the following line to the end of the section:
$(SG_OUTPUT_ROOT)\sdk\lib\$(_CPUINDPATH)\xamlruntime.lib \
Or if the TARGETLIBS section looks similar to the following:
TARGETLIBS=\ $(_COREDLL) \
Add the following line to the end of the section:
$(_XAMLRUNTIME) \
If you are working in a Platform Builder subproject, open the Properties page for the subproject, and then in the Link tab, append the following text, including the preceding space, to the value for Additional Libraries:
$(SG_OUTPUT_ROOT)\sdk\lib\$(_CPUINDPATH)\xamlruntime.lib
If you are working in an individual project, open the Properties page for the project. Expand Configuration Properties, Linker, and select Input. Append the following to the list of values for Additional Dependencies:
xamlruntime.lib
In the WinMain routine, initialize the system by calling XamlRuntimeInitialize. For example:
BOOL bIsXRInitialized = XamlRuntimeInitialize();
Obtain a singleton application object by calling GetXRApplicationInstance. For example:
IXRApplicationPtr pApplication; if (bIsXRInitialized) { GetXRApplicationInstance(&pApplication); }
(Optional) Add a resource module for XAML for Windows Embedded to use when it resolves image source Uniform Resource Identifiers (URIs) in the source XAML that it parses and loads into a visual tree. To do this, use one or both of the following methods:
Call IXRApplication::AddResourceModule and supply the handle to the current instance of the application (HINSTANCE) from the WinMain function signature.
pApplication->AddResourceModule(hInstance);
Create an IXRResourceManager object, and then call IXRApplication::RegisterResourceManager. For example:
XRPtr<IXRResourceManager> pResource; pApplication->RegisterResourceManager(pResource);
(Optional) In the same file that you've been working in, if the source XAML includes an App.xaml file for resources, call IXRApplication::GetResourceDictionary and IXRApplication::LoadResourceDictionary to parse that XAML file.
IXRResourceDictionaryPtr pResourceDictionary; XRXamlSource Source; Source.SetFile(RESOURCE_DIR L"App.xaml"); pApplication->LoadResourceDictionary(&Source, &pResourceDictionary); pApplication->GetResourceDictionary(&pResourceDictionary);
Specify the XAML source by creating an XRXamlSource structure and populating it with information about the source of the XAML markup.
XRXamlSource SourceXaml; SourceXaml.SetFile(L"MainPage.xaml"); // Also set the XAML resource if you called AddResourceModule SourceXaml.SetResource(hInstance, L"XAML", MAKEINTRESOURCE(500));
(Optional) Set up the default window by creating an XRWindowCreateParams structure and populating it with window parameters.
XRWindowCreateParams WindowParameters; ZeroMemory(&WindowParameters, sizeof(WindowParameters)); WindowParameters.Style = WS_POPUP; WindowParameters.pTitle = L"Title Name"; WindowParameters.Left = 100; WindowParameters.Top = 100; WindowParameters.AllowsMultipleThreadAccess = true;
Parse the source XAML markup and generate the visual tree by calling IXRApplication::CreateHostFromXaml.
IXRVisualHostPtr pVisualHost; pApplication->CreateHostFromXaml(&SourceXaml, &WindowParameters, &pVisualHost);
(Optional) Obtain an IXRFrameworkElement pointer to the root of the visual tree so you can add new UI objects to it. These objects can be integrated with the layout system by XAML for Windows Embedded and displayed in the graphical window.
IXRFrameworkElementPtr pRoot; pVisualHost->GetRootElement(&pRoot);
Display the host window for your application by using one of the following options:
For a modal dialog box, call IXRVisualHost::StartDialog.
UINT uiExitCode = 0; if(pVisualHost != NULL) { // save the exit code for WinMain pVisualHost->StartDialog(&uiExitCode); }
For a modeless dialog box, call IXRVisualHost::ShowWindow, and then call IXRApplication::StartProcessing to process messages for objects that were loaded into the visual tree.
pVisualHost->ShowWindow();
The following example code displays the host window for a modeless dialog box.
HWND hwnd = NULL; UINT exitCode = 0; pVisualHost->GetContainerHWND(&hwnd); pVisualHost->ShowWindow(); pApplication->StartProcessing(&exitCode); UpdateWindow(hwnd);
After you complete this task and rebuild the project, the application is displayed on the screen and is ready for user interaction.
In the WinMain routine, after you parse the XAML into an object tree by calling IXRApplication::CreateHostFromXaml, insert code that adds the event handlers into each interactive UI object in the object tree.
You can also add an OnKeyDown event handler to the IXRVisualRoot object that is the default event handler for key events in the window.
To add an event handler to a UI object that you create in C++
In the C++ source code file, add XRDelegate.h and XRPtr.h to the #include list in the XAML for Windows Embedded application.
#include "XRDelegate.h" #include "XRPtr.h"
Initialize an object variable for a smart pointer, and then use the IXRApplication::CreateObject method to convert it into an object and return a reference to the new object.
IXRRectanglePtr pRect; pApplication->CreateObject(&pRect);
Define the new object instance by using its methods.
pRect->SetRadiusX(50); pRect->SetRadiusY(50);
Create a delegate for your custom event handler and attach it to the object. To do this, call the associated Add*EventHandler method, and call the CreateDelegate(class,class::method) inline function in the first argument of the method. Each class for an interactive UI object has one or more Add*EventHandler methods, such as IXRUIElement::AddMouseEnterEventHandler. To complete this step, you must have already implemented a custom object in "Step 4: Add Variables to Enable an Event Handler to Access UI Objects" with the required event handlers.
pRect->AddKeyDownEventHandler(CreateDelegate(this, &CustomObject::OnKeyDown));
To add event handlers to parsed XAML elements, you can create a helper function that finds all the interactive objects that are parsed from XAML and then attaches delegates to each object. You can then call this function after you parse the XAML in the WinMain routine.
To add event handlers to UI objects that are parsed from XAML
Retrieve the x:Name values of the XAML elements that you parsed in your XAML for Windows Embedded application.
You can retrieve the values by requesting a list of x:Name values from the UI designer, or you can scan the XAML files yourself and extract the value of x:Name from each interactive element for which you want to handle events. The following example markup shows a XAML element that has an x:Name value of "Button7."
<Button x:Name="Button7" Content="Remove" Width="80" Height="30" />
In the C++ source code file, add XRDelegate.h and XRPtr.h to the #include list in the XAML for Windows Embedded application.
#include "XRDelegate.h" #include "XRPtr.h"
In the C++ source code file, obtain an IXRFrameworkElement pointer to the root of the visual tree by using the IXRVisualHost object that is returned by IXRApplication::CreateHostFromXaml.
IXRFrameworkElementPtr pRoot; pVisualHost->GetRootElement(&pRoot);
Use IXRFrameworkElement::FindName to locate each interactive UI object by its x:Name value.
IXRButtonPtr pButton; pRoot->FindName(L"Button7", &pButton);
Create a delegate for your custom event handler that you can attach to the UI object.
To create the delegate, call the CreateDelegate(class,class::method) inline function in the first argument of the Add*EventHandler method. Each class for an interactive UI object has one or more Add*EventHandler methods, such as IXRButtonBase::AddClickEventHandler. To complete this step, you must have already implemented a custom object in "Step 4: Add Variables to Enable an Event Handler to Access UI Objects" with the required event handlers. For more information, see Step 2: Create an Object That Has Event Handlers above.
pButton->AddClickEventHandler(CreateDelegate(this, &CustomObject::OnClick));
Step 4: Add Variables to Enable an Event Handler to Access UI Objects
(Optional) If you want to access the visual tree from an event handler, add a global variable to represent the visual host in the event object. Then implement a method called SetHost, which sets the global variable to the visual host of the application, so that the code inside each event handler can access other UI objects in the visual host.
For example:
#include "XamlRuntime.h"
#include "XRPtr.h"
class CustomObject
{
public:
IXRVisualHostPtr g_pHost;
HRESULT SetHost(IXRVisualHost* pHost)
{
HRESULT hr;
ASSERT(! g_pHost);
if(NULL == pHost)
{
hr = S_FALSE;
return hr;
}
m_pHost = pHost;
hr = S_OK;
return hr;
}
};
Call the new method SetHost after you call IXRApplication::CreateHostFromXaml.
CustomObject events;
events.SetHost(pVisualHost);
Step 5: Implement Your Application
Write code to add the functionality that you want to your application.
If you want to change the UI design at run time, you can use the XAML for Windows Embedded API to modify or add new UI objects to the object tree. Use IXRFrameworkElement::FindName to traverse the object tree and get pointers to objects that you want to customize or add new objects to.
For example, you can change the UI design by setting new values for brushes, images, coordinate positions, animation storyboard collections, and so on.
Step 6: Write Shutdown Code
To enable application users to close your XAML for Windows Embedded application, you can add code to an event handler for a Close button that causes the application to exit.
When the user clicks the button, the corresponding event handler can cause the application to exit by calling the appropriate methods on the visual host. When you use smart pointers, object instances will automatically call IUnknown::Release when they go out of scope. If you did not use smart pointers by using XRPtr, call SAFE_DELETE on object instances, and call IUnknown::Release on the IXRFrameworkElement root element instance.
To exit an application that runs as a modal dialog and that you created by calling IXRVisualHost::StartDialog, call IXRVisualHost::EndDialog, which sends a window message to the visual host.
To close a visual host window that you displayed by calling ShowWindow, call IXRVisualHost::DestroyWindow, which shuts down the compositor, frees the off-screen graphical frame buffer and graphics renderer, cleans up window resources, and removes the animation timer.
To hide an application’s window when it is not in use yet keep the application running in the background, call IXRVisualHost::HideWindow.
The following example code calls IXRVisualHost::EndDialog to shut down an application. The example code assumes that the IXRVisualHostPtr object in m_pVisualHost
was already created by the application.
#include "XamlRuntime.h"
// IXRVisualHost object variable
static IXRVisualHostPtr m_pVisualHost;
inline HRESULT App::GetVisualHost(IXRVisualHost ** ppHost)
{
if (!ppHost)
return E_INVALIDARG;
if (m_pVisualHost)
{
*ppHost = m_pVisualHost;
(*ppHost)->AddRef();
return S_OK;
}
return E_FAIL;
}
HRESULT MainPage::OK_Click (IXRDependencyObject* pSender, XRMouseButtonEventArgs* pArgs)
{
HRESULT hr = E_NOTIMPL;
if ((NULL == pSender) || (NULL == pArgs))
{
hr = E_INVALIDARG;
}
IXRVisualHostPtr pHost;
UINT ExitCode = 1;
hr = App::GetVisualHost(&pHost);
if(hr == S_OK)
{
pHost->EndDialog(ExitCode);
}
return hr;
}
Step 7: Build the Application and Your OS Design
In Solution Explorer, expand Subprojects, right-click the subproject that you created, and then click Build.
The build progress is displayed in the Output tab of the Output window.
Rebuild the run-time image, and download it to your device through the connection that you have already configured.
Step 8: Run Your Application
Run the application on the run-time OS image by doing one of the following tasks:
- If you are working in an OS design project, use Platform Builder to download the OS image to a device or virtual CEPC. Then, in Visual Studio, on the Target menu, select Run Programs to start your application on the device or virtual CEPC.
- If you are working in an individual project that uses an SDK for an OS design, use a device that has the OS image stored in flash memory, or you can use a stand-alone virtual CEPC that includes the OS image on a virtual drive. Then, in Visual Studio, on the Tools menu, select Connect to Device to make a connection to your running device or stand-alone VCEPC. Press F5 to run your application.
For information about creating a virtual CEPC and an OS design on which you can test your application, see Use the Sample Virtual Device.