Posts
80
Comments
214
Trackbacks
0
Silverlight for Windows Embedded tutorial (step 6)

Pepita

In this tutorial step we will develop a very simple clock application that may be used as a screensaver on our devices and will allow us to discover a new feature of Silverlight for Windows Embedded (transforms) and how to use an “old” feature of Windows CE (timers) inside a Silverlight for Windows Embedded application.

Let’s start with some XAML, as usual:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="640" Height="480" FontSize="18"
  x:Name="Clock">
 
    <Canvas x:Name="LayoutRoot" Background="#FF000000">
        <Grid Height="24" Width="150" Canvas.Left="320" Canvas.Top="234" x:Name="SecondsHand" Background="#FFFF0000">
            <TextBlock Text="Seconds" TextWrapping="Wrap" Width="50" HorizontalAlignment="Right" VerticalAlignment="Center" x:Name="SecondsText" Foreground="#FFFFFFFF" TextAlignment="Right" Margin="2,2,2,2"/>
        </Grid>
        <Grid Height="24" x:Name="MinutesHand" Width="100" Background="#FF00FF00" Canvas.Left="320" Canvas.Top="234">
            <TextBlock HorizontalAlignment="Right" x:Name="MinutesText" VerticalAlignment="Center" Width="50" Text="Minutes" TextWrapping="Wrap" Foreground="#FFFFFFFF" TextAlignment="Right" Margin="2,2,2,2"/>
        </Grid>
        <Grid Height="24" x:Name="HoursHand" Width="50" Background="#FF0000FF" Canvas.Left="320" Canvas.Top="234">
            <TextBlock HorizontalAlignment="Right" x:Name="HoursText" VerticalAlignment="Center" Width="50" Text="Hours" TextWrapping="Wrap" Foreground="#FFFFFFFF" TextAlignment="Right" Margin="2,2,2,2"/>
        </Grid>
    </Canvas>
</UserControl>

This XAML file defines three grid panels, one for each hand of our clock (we are implementing an analog clock using one of the most advanced technologies of the digital world… how cool is that?). Inside each hand we put a TextBlock that will be used to display the current hour, minute, second inside the dial (you can’t do that on plain old analog clocks, but it looks nice).

As usual we use XAML2CPP to generate the boring part of our code.

We declare a class named “Clock” and derives from the TClock template that XAML2CPP has declared for us.

class Clock : public TClock<Clock>
{
...
};

Our WinMain function is more or less the same we used in all the previous samples. It initializes the XAML runtime, create an instance of our class, initialize it and shows it as a dialog:

int WINAPI WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR     lpCmdLine,
                     int       nCmdShow)
{
    if (!XamlRuntimeInitialize())
            return -1;
 
    HRESULT retcode;
 
    IXRApplicationPtr app;
    
    if (FAILED(retcode=GetXRApplicationInstance(&app)))
        return -1;
    
    Clock clock;
 
    if (FAILED(clock.Init(hInstance,app)))
        return -1;
 
 
    UINT exitcode;
 
    if (FAILED(clock.GetVisualHost()->StartDialog(&exitcode)))
        return -1;
 
    return exitcode;
}

Silverlight for Windows Embedded provides a lot of features to implement our UI, but it does not provide timers. How we can update our clock if we don’t have a timer feature? We just use plain old Windows timers, as we do in “regular” Windows CE applications!

To use a timer in WinCE we should declare an id for it:

#define IDT_CLOCKUPDATE 0x12341234

We also need an HWND that will be used to receive WM_TIMER messages. Our Silverlight for Windows Embedded page is “hosted” inside a GWES Window and we can retrieve its handle using the GetContainerHWND function of our VisualHost object.

Let’s see how this is implemented inside our Clock class’ Init method:

HRESULT Init(HINSTANCE hInstance,IXRApplication* app) 
    {
        HRESULT retcode;
 
        if (FAILED(retcode=TClock<Clock>::Init(hInstance,app)))
            return retcode;
 
        // create the timer user to update the clock
        HWND clockhwnd;
 
        if (FAILED(GetVisualHost()->GetContainerHWND(&clockhwnd)))
            return -1;
 
        timer=SetTimer(clockhwnd,IDT_CLOCKUPDATE,1000,NULL);
        return 0;
    }

We use SetTimer to create a new timer and GWES will send a WM_TIMER to our window every second, giving us a chance to update our clock. That sounds great… but how could we handle the WM_TIMER message if we didn’t implement a window procedure for our window?

We have to move a step back and look how a visual host is created. This code is generated by XAML2CPP and is inside xaml2cppbase.h:

virtual HRESULT CreateHost(HINSTANCE hInstance,IXRApplication* app)
    {
        HRESULT retcode;
        
        XRWindowCreateParams wp;
 
        ZeroMemory(&wp, sizeof(XRWindowCreateParams));
        
        InitWindowParms(&wp);
 
        XRXamlSource xamlsrc;
 
        SetXAMLSource(hInstance,&xamlsrc);
        
        if (FAILED(retcode=app->CreateHostFromXaml(&xamlsrc, &wp, &vhost)))
            return retcode;
 
        if (FAILED(retcode=vhost->GetRootElement(&root)))
            return retcode;
        
        return S_OK;
    }      

As you can see the CreateHostFromXaml function of IXRApplication accepts a structure named XRWindowCreateParams that control how the “plain old” GWES Window is created by the runtime.

This structure is initialized inside the InitWindowParm method:

// Initializes Windows parameters, can be overridden in the user class to change its appearance
virtual void InitWindowParms(XRWindowCreateParams* wp)
{
        wp->Style       = WS_OVERLAPPED;
        wp->pTitle      = windowtitle;
        wp->Left        = 0;
        wp->Top         = 0;
}

This method set up the window style, title and position. But the XRWindowCreateParams contains also other fields and, since the function is declared as virtual, we could initialize them inside our version of InitWindowParms:

// add hook procedure to the standard windows creation parms
virtual void InitWindowParms(XRWindowCreateParams* wp)
{
    TClock<Clock>::InitWindowParms(wp);
 
    wp->pHookProc=StaticHostHookProc;
    wp->pvUserParam=this;
}

This method calls the base class implementation (useful to not having to re-write some code, did I told you that I’m quite lazy?) and then initializes the pHookProc and pvUserParam members of the XRWindowsCreateParams structure. Those members will allow us to install a “hook” procedure that will be called each time the GWES window “hosting” our Silverlight for Windows Embedded UI receives a message.

We can declare a hook procedure inside our Clock class:

// static hook procedure 
static BOOL CALLBACK StaticHostHookProc(VOID* pv,HWND hwnd,UINT Msg,WPARAM wParam,LPARAM lParam,LRESULT* pRetVal)
{
...
}

You should notice two things here. First that the function is declared as static. This is required because a non-static function has a “hidden” parameters, that is the “this” pointer of our object. Having an extra parameter is not allowed for the type defined for the pHookProc member of the XRWindowsCreateParams struct and so we should implement our hook procedure as static. But in a static procedure we will not have a this pointer. How could we access the data member of our class? Here’s the second thing to notice. We initialized also the pvUserParam of the XRWindowsCreateParams struct. We set it to our this pointer. This value will be passed as the first parameter of the hook procedure. In this way we can retrieve our this pointer and use it to call a non-static version of our hook procedure:

// static hook procedure 
static BOOL CALLBACK StaticHostHookProc(VOID* pv,HWND hwnd,UINT Msg,WPARAM wParam,LPARAM lParam,LRESULT* pRetVal)
{
    return ((Clock*)pv)->HostHookProc(hwnd,Msg,wParam,lParam,pRetVal);
}

Inside our non-static hook procedure we will have access to our this pointer and we will be able to update our clock:

// hook procedure (handles timers)
BOOL HostHookProc(HWND hwnd,UINT Msg,WPARAM wParam,LPARAM lParam,LRESULT* pRetVal)
{
    switch (Msg)
    {
    case WM_TIMER:
        if (wParam==IDT_CLOCKUPDATE)
            UpdateClock();
        *pRetVal=0;
        return TRUE;
    }
    return FALSE;
}

The UpdateClock member function will update the text inside our TextBlocks and rotate the hands to reflect current time:

// udates Hands positions and labels
    HRESULT UpdateClock()
    {
        SYSTEMTIME time;
        HRESULT retcode;
 
        GetLocalTime(&time);
 
        //updates the text fields
        TCHAR timebuffer[32];
 
        _itow(time.wSecond,timebuffer,10);
 
        SecondsText->SetText(timebuffer);
 
        _itow(time.wMinute,timebuffer,10);
 
        MinutesText->SetText(timebuffer);
 
        _itow(time.wHour,timebuffer,10);
 
        HoursText->SetText(timebuffer);
 
        if (FAILED(retcode=RotateHand(((float)time.wSecond)*6-90,SecondsHand)))
            return retcode;
 
        if (FAILED(retcode=RotateHand(((float)time.wMinute)*6-90,MinutesHand)))
            return retcode;
 
        if (FAILED(retcode=RotateHand(((float)(time.wHour%12))*30-90,HoursHand)))
            return retcode;
 
        return S_OK;
    }

The function retrieves current time, convert hours, minutes and seconds to strings and display those strings inside the three TextBlocks that we put inside our clock hands.

Then it rotates the hands to position them at the right angle (angles are in degrees and we have to subtract 90 degrees because 0 degrees means horizontal on Silverlight for Windows Embedded and usually a clock 0 is in the top position of the dial.

The code of the RotateHand function uses transforms to rotate our clock hands on the screen:

// rotates a Hand
HRESULT RotateHand(float angle,IXRFrameworkElement* Hand)
{
    HRESULT retcode;
    IXRRotateTransformPtr rotatetransform;
    IXRApplicationPtr    app;
 
    if (FAILED(retcode=GetXRApplicationInstance(&app)))
        return retcode;
 
    if (FAILED(retcode=app->CreateObject(IID_IXRRotateTransform,&rotatetransform)))
        return retcode;
 
 
    if (FAILED(retcode=rotatetransform->SetAngle(angle)))
        return retcode;
 
    if (FAILED(retcode=rotatetransform->SetCenterX(0.0)))
        return retcode;
 
    float height;
 
    if (FAILED(retcode==Hand->GetActualHeight(&height)))
        return retcode;
 
    if (FAILED(retcode=rotatetransform->SetCenterY(height/2)))
        return retcode;
    
    if (FAILED(retcode=Hand->SetRenderTransform(rotatetransform)))
        return retcode;
 
    return S_OK;
}

It creates a IXRotateTransform object, set its rotation angle and origin (the default origin is at the top-left corner of our Grid panel, we move it in the vertical center to keep the hand rotating around a single point in a more “clock like” way.

Then we can apply the transform to our UI object using SetRenderTransform.

Every UI element (derived from IXRFrameworkElement) can be rotated! And using different subclasses of IXRTransform also moved, scaled, skewed and distorted in many ways. You can also concatenate multiple transforms and apply them at once suing a IXRTransformGroup object.

The XAML engine uses vector graphics and object will not look “pixelated” when they are rotated or scaled.

As usual you can download the code here: http://cid-9b7b0aefe3514dc5.skydrive.live.com/self.aspx/.Public/Clock.zip

If you read up to (down to?) this point you seem to be interested in Silverlight for Windows Embedded. If you want me to discuss some specific topic, please feel free to point it out in the comments!

posted on Thursday, March 25, 2010 12:42 AM Print
Comments
Gravatar
# re: Silverlight for Windows Embedded tutorial (step 6)
Shailesh Lohia
4/4/2010 7:59 AM
Is it possible to use silverlight toolkit controls especially charting tools in windows embedded ce 6.0 R3. Desperately need a charting tool for my med based application.
Gravatar
# re: Silverlight for Windows Embedded tutorial (step 6)
Valter Minute
4/5/2010 7:12 AM
Silverlight for Windows Embedded is based on a XAML engine that can process the same XAML code that Silverlight 2.0 handles, but the underlying technology is different. The Embedded version is based on native code and out-of-browser apps, the "desktop" version is in-browser and based on managed code, so you can't port controls and other code easily.
You may port the XAML code, but not the "businness" logic behind it.
Gravatar
# re: Silverlight for Windows Embedded tutorial (step 6)
Shailesh Lohia
4/8/2010 4:43 AM
i wanted to make an application with two features viz charting data real time and streaming web cam images ... can u plz give me some idea about how to go about this.
Gravatar
# re: Silverlight for Windows Embedded tutorial (step 6)
Valter Minute
4/28/2010 11:21 PM
Ciao Shailesh,
it's not easy to give direction knowing so few about you application requirements and platform.
I think that video streaming may be implemented using a Win32 custom control inside your XAML code or, if your hardware supports that, using a video overlay (that will leave most of the work to the hardware).
For the charts you may create them in Silverlight for Windows Embedded by creating objects at runtime, like I did in the listbox sample in one of the previous tutorial step. For a bar chart, for example, you can create rectangle objects or load a XAML file defining the ones you need and then use transforms to stretch and resize them to reflect the associated data.
Gravatar
# re: Silverlight for Windows Embedded tutorial (step 6)
annaertd
5/20/2010 3:34 AM
Hi Shailesh,

I am also interested in charting possibilities of Silverlight on WinCE6.0 r3. (especially realtime update polygon lines)
Have you find any useful references in the mean time ?

Greetings,

annaertd
Gravatar
# re: Silverlight for Windows Embedded tutorial (step 6)
annaertd
5/20/2010 11:20 PM
Can you indicate how one could draw a realtime chart at runtime (eg temperature versus time), by connecting line-pieces from one data sample to another ?
Do you think smooth curves can be drawn with IXRPolyline::SetPoints() ?
Gravatar
# re: Silverlight for Windows Embedded tutorial (step 6)
Valter Minute
5/27/2010 11:02 PM
You can use IXRShape derived classes to draw your own chart and modify them at runtime. The "smoothness" of this effect depends on your hardware. On an accelerated implementation of Silverlight for Windows Embedded this should not be too CPU intensive.
Gravatar
# re: Silverlight for Windows Embedded tutorial (step 6)
Velraja
7/22/2010 5:15 PM
Is it possible to use styles created in <app.resources>?If possible how?
Gravatar
# re: Silverlight for Windows Embedded tutorial (step 6)
Valter Minute
8/9/2010 7:35 PM
@Velraja
You should be able to do that in Expression Blend.
Here you may find some useful information about this topic:
http://www.scribd.com/doc/16875489/silverlight-2-expression-blend-styles-and-templates
Remember that not all the "desktop" Silverlight features are supported on the embedded version, so you may need to adjust some of the samples you find in tutorials and articles about silverlight.

Post Comment

Title *
Name *
Email
Comment *  
Verification
Toradex logo

Cookies in Use
Tag Cloud