Share via


Create Custom Behaviors (Compact 2013)

3/26/2014

Creating custom behaviors and attaching them to objects in your XAML for Windows Embedded applications involves four steps:

  1. Adding the behavior to your XAML file.
  2. Registering the behavior.
  3. Implementing the header file for the behavior.
  4. Implementing the behavior in native code.

The xrinteractions.h header file provides the classes you need to create a custom behavior for a XAML for Windows Embedded application. Use the XRBehavior class to implement your custom behavior class and register it. Then you can use the custom behavior on any of your UI controls. If you register your custom behavior after application load time but before visual tree creation time, you can apply your custom behavior to XAML elements. This supports a scenario in which one team member designs the UI in Expression Blend by using a managed version of the custom behavior that matches the native-code XAML for Windows Embedded behavior. The class names and property names must all match for the XAML to work in both projects without any additional modification.

The following walkthrough demonstrates how to implement a simple custom behavior. In this example, you will implement a behavior to change the borders or a rectangle from square to round when the mouse enters and leaves the rectangle. This walkthrough builds on the example project demonstrating built-in behaviors covered in Add Behaviors to Objects in Expression Blend.

To add the behavior to your XAML file

Open MainPage.xaml in your XAML for Windows Embedded project and replace the XAML code inside this file with the following segment.

<UserControl
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="https://schemas.microsoft.com/expression/2010/interactivity" 
    xmlns:ei="https://schemas.microsoft.com/expression/2010/interactions"
    xmlns:sample="clr-namespace:CustomNamespace"
    x:Class="BehaviorSample.MainPage"
    Width="640" Height="480">

    <Grid x:Name="LayoutRoot" Background="White">
        <Button x:Name="myButton" Content="Button" HorizontalAlignment="Left" Height="192" Margin="233,151,0,0"
            VerticalAlignment="Top" Width="220">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <ei:ChangePropertyAction TargetName="LayoutRoot" PropertyName="Background">
                        <ei:ChangePropertyAction.Value>
                            <SolidColorBrush Color="#FF266DEA"/>
                        </ei:ChangePropertyAction.Value>
                    </ei:ChangePropertyAction>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
        <Border x:Name="MyOuterBorder" Margin="203,120,157,107" BorderBrush="Black" BorderThickness="30">
            <i:Interaction.Behaviors>
                <sample:RoundBorderBehavior />
            </i:Interaction.Behaviors>
        </Border>
    </Grid>
</UserControl>

Notice the Border element. This adds a Border control to the XAML user interface. Within the Border element, you can associate the custom RoundBorderBehavior with the border rectangle. The next step is to register this behavior.

To register the custom behavior

Open the App.h header file in your XAML for Windows Embedded application. You must add two segments of code to this file:

  • At the top of the header file, include the additional header file corresponding to the custom behavior. You will implement this header file in the next step. To include the header, add the following code to App.h.

    #include "RoundBorderBehavior.h"
    
  • Locate the App::Initialize method and add the code that is required to register the custom behavior to it, as shown in this code segment.

    inline HRESULT App::Initialize(HINSTANCE hInstance)
    {
    ...
    
        if (SUCCEEDED(hr))
        {
            hr = RoundBorderBehavior::Register();
        }
        ...
    

To implement the custom behavior header

Create a new header file in your XAML for Windows Embedded project. Name the file RoundBorderBehavior.h. The header file should contain the following code.

#pragma once
#include "XRInteractions.h"

class __declspec(uuid("{4892E998-F3B9-4957-9189-E53DCD05C31B}")) IRoundBorderBehavior : 
    public IXRBehavior
{
public:
};

class __declspec(uuid("{0750AAD8-7DC2-4873-8DB3-D4B9EE878154}")) RoundBorderBehavior : 
    public XRBehavior< RoundBorderBehavior, IRoundBorderBehavior >
{
public:
    RoundBorderBehavior() :
      m_pDelegateMouseEnter(NULL),
      m_pDelegateMouseLeave(NULL)
      {}

    virtual ~RoundBorderBehavior() 
    {
        if (m_pDelegateMouseEnter) {m_pDelegateMouseEnter->Release(); m_pDelegateMouseEnter = NULL;}
        if (m_pDelegateMouseLeave) {m_pDelegateMouseLeave->Release(); m_pDelegateMouseLeave = NULL;}
    }

    static HRESULT Register();
    virtual HRESULT STDMETHODCALLTYPE OnAttached(IXRDependencyObject * pAssociatedObject);
    virtual HRESULT STDMETHODCALLTYPE OnDetaching();

private:
    IXRDelegate<XRMouseEventArgs>* GetMouseEnterDelegate();
    IXRDelegate<XRMouseEventArgs>* GetMouseLeaveDelegate();
    HRESULT OnMouseEnter(IXRDependencyObject *pSender, XRMouseEventArgs *pArgs);
    HRESULT OnMouseLeave(IXRDependencyObject *pSender, XRMouseEventArgs *pArgs);

private:
    IXRDelegate<XRMouseEventArgs> *m_pDelegateMouseEnter;
    IXRDelegate<XRMouseEventArgs> *m_pDelegateMouseLeave;
};

To implement the custom behavior

Add a new source file to your XAML for Windows Embedded project. Name the file RoundBorderBehavior.cpp. This file should contain the following code.

#include <stdAfx.h>
#include "RoundBorderBehavior.h"

#define INTERACTIVITY_CUSTOM_NAMESPACE L"clr-namespace:CustomNamespace"

HRESULT RoundBorderBehavior::Register()
{
    HRESULT hr = E_FAIL;

    hr = XRBehavior::Register(L"RoundBorderBehavior", INTERACTIVITY_CUSTOM_NAMESPACE);
    
    if(XR_E_DUPLICATE_REGISTRATION == hr)
    {
        // Ignore duplicate registration for this test code
        hr = S_OK;
    }
    return hr;
}

IXRDelegate<XRMouseEventArgs>* RoundBorderBehavior::GetMouseEnterDelegate()
{
    if (NULL == m_pDelegateMouseEnter)
    {
        CreateDelegate(this, &RoundBorderBehavior::OnMouseEnter, &m_pDelegateMouseEnter);
    }
    return m_pDelegateMouseEnter;
}

IXRDelegate<XRMouseEventArgs>* RoundBorderBehavior::GetMouseLeaveDelegate()
{
    if (NULL == m_pDelegateMouseLeave)
    {
        CreateDelegate(this, &RoundBorderBehavior::OnMouseLeave, &m_pDelegateMouseLeave);
    }
    return m_pDelegateMouseLeave;
}

HRESULT RoundBorderBehavior::OnAttached(IXRDependencyObject * pAssociatedObject)
{
    HRESULT hr = E_FAIL;
    XRPtr<IXRDelegate<XRMouseEventArgs>> pDelegateMouseEnter;
    XRPtr<IXRDelegate<XRMouseEventArgs>> pDelegateMouseLeave;
    XRPtr<IXRDependencyObject> pAssociatedObjectPtr;
    XRPtr<IXRBorder> pAssociatedObjectBorder;

    pDelegateMouseEnter = GetMouseEnterDelegate();
    pDelegateMouseLeave = GetMouseLeaveDelegate();

    XRBehavior::OnAttached(pAssociatedObject);
    if (pAssociatedObject)
    {
        pAssociatedObjectPtr = pAssociatedObject;
        pAssociatedObjectBorder = pAssociatedObjectPtr;
        hr = pAssociatedObjectBorder->AddMouseEnterEventHandler(pDelegateMouseEnter);
        hr = pAssociatedObjectBorder->AddMouseLeaveEventHandler(pDelegateMouseLeave);
    }
    return hr;
}

HRESULT RoundBorderBehavior::OnDetaching()
{
    HRESULT hr = E_FAIL;
    XRPtr<IXRDelegate<XRMouseEventArgs>> pDelegateMouseEnter;
    XRPtr<IXRDelegate<XRMouseEventArgs>> pDelegateMouseLeave;
    XRPtr<IXRDependencyObject> pAssociatedObject;
    XRPtr<IXRBorder> pAssociatedObjectBorder;

    pDelegateMouseEnter = GetMouseEnterDelegate();
    pDelegateMouseLeave = GetMouseLeaveDelegate();

    XRAttachedObject::GetTargetObject(&pAssociatedObject);

    if (pAssociatedObject)
    {
        pAssociatedObjectBorder = pAssociatedObject;
        pAssociatedObjectBorder->RemoveMouseEnterEventHandler(pDelegateMouseEnter);
        hr = pAssociatedObjectBorder->RemoveMouseLeaveEventHandler(pDelegateMouseLeave);
    }
    XRBehavior::OnDetaching();
    return hr;
}

void InflateRect(XRCornerRadius *pCornerRadious, int Delta)
{
    pCornerRadious->TopLeft += Delta;
    pCornerRadious->TopRight += Delta;
    pCornerRadious->BottomRight += Delta;
    pCornerRadious->BottomLeft += Delta;
}

HRESULT RoundBorderBehavior::OnMouseEnter(IXRDependencyObject *pSender, XRMouseEventArgs *pArgs)
{
    HRESULT hr = E_FAIL;
    XRPtr<IXRBorder> pTargetBorder;
    XRCornerRadius CurrentCornerRadius = { 0 };

    GetTargetObject( &pTargetBorder );
    hr = pTargetBorder->GetCornerRadius( &CurrentCornerRadius );
    if (SUCCEEDED(hr))
    {
        InflateRect(&CurrentCornerRadius, 10);
        hr = pTargetBorder->SetCornerRadius( &CurrentCornerRadius );
    }
    return hr;
}

HRESULT RoundBorderBehavior::OnMouseLeave(IXRDependencyObject *pSender, XRMouseEventArgs *pArgs)
{
    HRESULT hr = E_FAIL;
    XRPtr<IXRBorder> pTargetBorder;
    XRCornerRadius CurrentCornerRadius = { 0 };

    GetTargetObject( &pTargetBorder );
    hr = pTargetBorder->GetCornerRadius( &CurrentCornerRadius );
    if (SUCCEEDED(hr))
    {
        InflateRect(&CurrentCornerRadius, -10);
        hr = pTargetBorder->SetCornerRadius( &CurrentCornerRadius );
    }
    return hr;
}

This code implements all of the key elements required for a custom behavior in a XAML for Windows Embedded application, including:

  • Implementing RoundBorderBehavior::Register so that the behavior can be registered with the XAML runtime engine.
  • Implementing RoundBorderBehavior::OnAttached to handle the attachment of this custom behavior to XAML objects.
  • Implementing RoundBorderBehavior::OnDetaching to handle the attachment of this custom behavior to XAML objects.
  • Creating delegates for the OnMouseEnter and OnMouseLeave events.
  • Implementing RoundBorderBehavior::OnMouseEnter and RoundBorderBehavior::OnMouseLeave. These two functions are critical to the functionality of this custom behavior.

To test the custom behavior

To test the custom behavior, build and deploy your OS to a test device or Virtual CEPC. Then attach to your device and run the program. You should observe that when you move the mouse into and out of the Border control, it changes from a square to a round border. The following figure illustrates both states. As you can see, when the mouse enters the rectangle border, the behavior is triggered and the rectangle changes its appearance accordingly, showing a round border.

Testing a XAML custom behavior

See Also

Concepts

Behaviors, Actions, and Triggers