Visual Hosts and Visual Trees in XAML for Windows Embedded (Compact 2013)
3/26/2014
A visual host represents a XAML for Windows Embedded visual tree in a Win32 window. The concept of a visual host is unique to XAML for Windows Embedded. Unlike Microsoft Silverlight for the desktop browser, which is based on the .NET Framework and uses managed code, XAML for Windows Embedded is a framework that uses Win32 and C++ programming. All visual elements that are parsed from XAML are stored in a visual tree, and only one visual tree can belong to one visual host. By loading XAML markup into a visual tree that belongs to a visual host, you can populate the on-screen content for the UI of a XAML for Windows Embedded application. Then, you can provide the C++ code to search, modify, and add to the XAML elements in the visual tree. For more information about visual trees, see Silverlight Object Trees on MSDN.
To work with the visual tree, you must first obtain a handle to the visual host. You can obtain this handle when you create the visual host by calling IXRApplication::CreateHostFromXaml or IXRApplication::CreateHostFromElementTree.
Each visual host object contains an IXRFrameworkElement object that is a pointer to the root of its visual tree. You can obtain a pointer to the root by calling IXRVisualHost::GetRootElement. Then, to find an element, call IXRFrameworkElement::FindName. To find an IXRDependencyObject-derived object that matches a UI element, create an XRPtr<Interface> smart pointer. You can then use the smart pointer as the following example shows.
Important
For readability, the following code example does not contain security checking or error handling. Do not use the following code in a production environment.
#include "XamlRuntime.h"
#include "XRPtr.h"
IXRButtonPtr pButton;
IXRFrameworkElementPtr pRootElement;
pVisualHost->GetRootElement(&pRootElement);
pRootElement->FindName(L"MyIXRButton", pButton);
After you obtain a smart pointer to an object that is stored in the visual tree, you can work with that object. The following example code shows how to add items to a list box that was parsed from XAML and is stored in the visual tree.
Important
For readability, the following code example does not contain security checking or error handling. Do not use the following code in a production environment.
#include "windows.h"
#include "XamlRuntime.h"
#include "XRPtr.h"
#include "XRDelegate.h"
void AddNewItemToListBox(IXRApplication* pApplication, IXRListBox* pListBox, CustomObject* pObject)
{
// Initialize variables
IXRListBoxItemPtr pLocationBasedItem;
IXRImageBrushPtr pImageBrush;
IXRBitmapImagePtr pDinerBitmap;
float itemHeight = 50;
float itemWidth = 100;
XRValue itemValue;
itemValue.vType = VTYPE_READONLY_STRING;
itemValue.pReadOnlyStringVal = "Ninth Avenue Diner";
// Create new XR objects
pApplication->CreateObject(&pLocationBasedItem);
pApplication->CreateObject(&pImageBrush);
pApplication->CreateObject(&pDinerBitmap);
// Set values for the image brush to paint the list-box item
pDinerBitmap->SetUriSource(L"Assets/ninthAve.png");
pImageBrush->SetImageSource((IXRImageSource*)&pDinerBitmap);
// Set values for the list-box item
pLocationBasedItem->AddMouseDownEventHandler(CreateDelegate(pObject, &CustomObject::OnMouseDown));
pLocationBasedItem->AddMouseEnterEventHandler(CreateDelegate(pObject, &CustomObject::OnMouseEnter));
pLocationBasedItem->AddMouseLeaveEventHandler(CreateDelegate(pObject, &CustomObject::OnMouseLeave));
pLocationBasedItem->AddOnLoadedEventHandler(CreateDelegate(pObject, &CustomObject::OnLoaded));
pLocationBasedItem->SetHeight(itemHeight);
pLocationBasedItem->SetWidth(itemWidth);
pLocationBasedItem->SetContent(&itemValue);
pLocationBasedItem->SetBackground((IXRBrush*)&pImageBrush);
// Add the new list-box item to the item collection
IXRItemCollectionPtr pItemCollection;
UINT index = 0;
XRValue xrValue;
xrValue.vType = VTYPE_OBJECT;
xrValue.pObjectVal = pLocationBasedItem;
pListBox->GetItems(&pItemCollection);
pItemCollection->Insert(index, &xrValue);
}
Access the Host Window from the Visual Host
To access the host Win32 window, first call IXRVisualHost::GetContainerHWND in the source code file for your main page. Call IXRVisualHost::GetContainerHWND after the application is loaded. The IXRVisualHost::GetContainerHWND method will return an HWND, which you can then use to call Windows Embedded Compact Win32 functions that take an HWND as a parameter, such as UpdateWindow, IsChild, or SetParent. By having access to the host window handle, you can call Win32 functions directly in XAML for Windows Embedded, which is a feature that is not available in Silverlight for the desktop browser.
Handle Additional Window Messages in the Visual Host
The visual host provides event handling at run time for objects that are stored in the XAML for Windows Embedded visual tree. For example, when the host window receives a WM_PAINT message, the visual host draws UI elements on the screen.
When the window receives a user-input event, such as a button click, the visual host must route it to the correct element in its visual tree. By default, window messages such as WM_PAINT, WM_TIMER, WM_SETTINGCHANGE, WM_SIZE, WM_MOUSEMOVE, WM_SYSKEYUP, WM_CHAR, WM_INPUTLANGCHANGE, WM_KILLFOCUS, WM_MOVE, and WM_GESTURE are processed in the internal WndProc of the visual host.
You can provide custom handling for these window messages or handle other window messages that the XAML for Windows Embedded WndProc does not automatically handle, for example, window messages that are developed for a specific feature, such as Bluetooth wireless technology. To provide custom handling for window messages, do one of the following:
- In your C++ code, implement a hook procedure for the window message by using the XR_HOOKPROC type and add the event-handling code directly to the HostHookProc window procedure.
- Create a subclass of the default WndProc for the host window. When the window message is received, you can provide message-handling code that calls IXRUIElement::HitTest to determine which XAML for Windows Embedded element that the message is for.
To create a hook procedure
Define a procedure that matches the signature that is provided in XR_HOOKPROC, and give it a custom name.
In your procedure, create a message loop that handles WM_* messages.
In your application startup code, do the following:
- Create an XRWindowCreateParams structure, populate its values, and then pass the name of the hook procedure that you implemented into the pHookProc member of XRWindowCreateParams.
- Pass XRWindowCreateParams into the call that creates the visual host, such as IXRApplication::CreateHostFromXaml or IXRApplication::CreateHostFromElementTree.
The following code shows an example hook procedure.
Important
For readability, the following code example does not contain security checking or error handling. Do not use the following code in a production environment.
#include "XamlRuntime.h" BOOL CALLBACK CustomHookProc ( VOID* pv, HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT* pRetVal) { switch (Msg) { // Add cases such as WM_CREATE, WM_COMMAND, WM_PAINT if you do not // want to pass these messages along for default processing. case WM_CAPTURECHANGED: // implement custom message-handling code return TRUE; case WM_MOUSEWHEEL: // implement custom message-handling code return TRUE; } return FALSE; }