Posts
76
Comments
208
Trackbacks
0
March 2010 Entries
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 | Comments (9)
Silverlight for Windows Embedded Tutorial (step 5 and a bit of Windows Phone 7)

Winter markets, Germany

If you haven’t spent the last week in the middle of the Sahara desert or traveling on a sled in the north pole area you should have heard something about the launch of Windows Phone 7 Series (or Windows Phone Series 7, or Windows Series Phone 7 or something like that). Even if you are in the middle of the desert or somewhere around the north pole you may have been reached by the news, since it seems that WP7S (using the full name will kill my available bandwidth!) is generating a lot of buzz in the development and IT communities.

One of the most important aspects of this new platform is that it will be programmed using a new set of tools and frameworks, completely different from the ones used on older releases of Windows Mobile (or SmartPhone, or PocketPC or whatever…).

WP7S applications can be developed using Silverlight or XNA.

If you want to learn something more about WP7S development you can download the preview of Charles Petzold’s book about it: http://www.charlespetzold.com/phone/index.html

Charles Petzold is also the author of “Programming Windows”, the first book I ever read about programming on Windows (it was Windows 3.0 at that time!). The fact that even I was able to learn how to develop Windows application is a proof of the quality of Petzold’s work. This book is up to his standards and the 150pages preview is already rich in technical contents without being boring or complicated to understand. I may be able to become a Windows Phone developer thanks to mr. Petzold.

Mr. Petzold uses some nice samples to introduce the basic concepts of Silverlight development on WP7S. On this new platform you’ll use managed code to develop your application, so those samples can’t be ported on Windows CE R3 as they are, but I would like to take one of the first samples (called “SilverlightTapHello1”) and adapt it to Silverlight for Windows Embedded to show that even plain old native code can be used to develop “cool” user interfaces!

The sample shows the standard WP7S title header and a textbox with an hello world message inside it. When the user touches the textbox, it will change its color. When the user touches the background (Grid) behind it, its default color (plain old White) will be restored.

Let’s see how we can implement the same features on our embedded device!

I took the XAML code of the sample (you can download the book samples here: http://download.microsoft.com/download/1/D/B/1DB49641-3956-41F1-BAFA-A021673C709E/CodeSamples_DRAFTPreview_ProgrammingWindowsPhone7Series.zip) and changed it a little bit to remove references to WP7S or managed runtime.

If you compare the resulting files you will see that I was able to keep all the resources inside the App.xaml files and the structure of  MainPage.XAML almost intact.

This is the Silverlight for Windows Embedded version of MainPage.XAML:

<UserControl
    x:Class="SilverlightTapHello1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phoneNavigation="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Navigation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}" Width="640" Height="480">
 
    <Grid x:Name="LayoutRoot" Background="{StaticResource PhoneBackgroundBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
 
        <!--TitleGrid is the name of the application and page title-->
        <Grid x:Name="TitleGrid" Grid.Row="0">
            <TextBlock Text="SILVERLIGHT TAP HELLO #1" x:Name="textBlockPageTitle" Style="{StaticResource PhoneTextPageTitle1Style}"/>
            <TextBlock Text="main page" x:Name="textBlockListTitle" Style="{StaticResource PhoneTextPageTitle2Style}"/>
        </Grid>
 
        <!--ContentGrid is empty. Place new content here-->
        <Grid x:Name="ContentGrid" Grid.Row="1" MouseLeftButtonDown="ContentGrid_MouseButtonDown" Background="{StaticResource PhoneBackgroundBrush}">
          <TextBlock x:Name="TextBlock" Text="Hello, Silverlight for Windows Embedded!" 
                     HorizontalAlignment="Center"
                     VerticalAlignment="Center"
                     />
        </Grid>
    </Grid>
</UserControl>

If you compare it to the WP7S sample (not reported here to avoid any copyright issue) you’ll notice that I had to replace the original phoneNavigation:PhoneApplicationPage with UserControl as the root node. This make sense because there is not support for phone applications on CE 6. I also had to specify width and height of my main page (on the WP7S device this will be adjusted by the OS) and I had to replace the multi-touch event handler with the MouseLeftButtonDown event (no multitouch support for Windows CE R3, still).

I also changed the hello message, of course.

I used XAML2CPP to generate the boring part of our application and then added the initialization code to WinMain:

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;
    
    XRXamlSource dictsrc;
 
    dictsrc.SetResource(hInstance,TEXT("XAML"),IDR_XAML_App);
 
    if (FAILED(retcode=app->LoadResourceDictionary(&dictsrc,NULL)))
        return -1;
 
    MainPage page;
 
    if (FAILED(page.Init(hInstance,app)))
        return -1;
 
    UINT exitcode;
 
    if (FAILED(page.GetVisualHost()->StartDialog(&exitcode)))
        return -1;
 
    return exitcode;
}

 

You may have noticed that there is something different from the previous samples.

I added the code to load a resource dictionary. Resources are an important feature of XAML that allows you to define some values that could be replaced inside any XAML file loaded by the runtime. You can use resources to define custom styles for your fonts, backgrounds, controls etc. and to support internationalization, by providing different strings for different languages.

The rest of our WinMain isn’t that different. It creates an instances of our MainPage object and displays it.

The MainPage class implements an event handler for the MouseLeftButtonDown event of the ContentGrid:

class MainPage : public TMainPage<MainPage>
{
public:
 
    HRESULT ContentGrid_MouseButtonDown(IXRDependencyObject* source,XRMouseButtonEventArgs* args)
    {
        HRESULT retcode;
        IXRSolidColorBrushPtr brush;
        IXRApplicationPtr app;
 
        if (FAILED(retcode=GetXRApplicationInstance(&app)))
            return retcode;
 
        if (FAILED(retcode=app->CreateObject(IID_IXRSolidColorBrush,&brush)))
            return retcode;
 
        COLORREF color=RGBA(0xff,0xff,0xff,0xff);
 
        if (args->pOriginalSource==TextBlock)
            color=RGBA(rand()&0xFF,rand()&0xFF,rand()&0xFF,0xFF);
 
        if (FAILED(retcode=brush->SetColor(color)))
            return retcode;
 
        if (FAILED(retcode=TextBlock->SetForeground(brush)))
            return retcode;
        return S_OK;
    }
};

As you can see this event is generated when a used clicks inside the grid or inside one of the objects it contains. Since our TextBlock is inside the grid, we don’t need to provide an event handler for its MouseLeftButtonDown event. We can just use the pOriginalSource member of the event arguments to check if the event was generated inside the textblock.

If the event was generated inside the grid we create a white brush,if it’s inside the textblock we create some randomly colored brush. Notice that we need to use the RGBA macro to create colors, specifying also a transparency value for them. If we use the RGB macro the resulting color will have its Alpha channel set to zero and will be transparent.

Using the SetForeground method we can change the color of our control.

You can compare this to the managed code that you can find at page 40-41 of Petzold’s preview book and you’ll see that the native version isn’t much more complex than the managed one.

As usual you can download the full code of the sample here:

http://cid-9b7b0aefe3514dc5.skydrive.live.com/self.aspx/.Public/SilverlightTapHello1.zip

And remember to pre-order Charles Petzold’s “Programming Windows Phone 7 series”, I bet it will be a best-seller!

Posted On Friday, March 19, 2010 5:34 AM | Comments (0)
XAML2CPP 1.0.2.0

Val Veddasca (Italy)

A new updated release of everybody favourite XAML to CPP conversion tool (at least because it’s the only one available!).

New features:

- support for resource dictionaries (app.xaml if you use Blend to generate your XAML)

Bugfixes:

- the parameters for the mouseleftbuttondown and up events were incorrect

As usual you can download the new release here:

http://cid-9b7b0aefe3514dc5.skydrive.live.com/self.aspx/.Public/XAML2CPP.zip

Posted On Friday, March 19, 2010 4:49 AM | Comments (3)
Windows CE training in Italy

Se volete approfondire le vostre conoscenze su Windows CE (anche relativamente alle novità introdotte con la versione R3), o desiderate acquisire le basi per cominciare a lavorare con questo sistema operativo, questa è un'occasione da non perdere.

Dal 12 al 16 Aprile si terrà presso gli uffici di Fortech Embedded Labs di Saronno (VA) il corso "Building Solutions with Windows Embedded CE 6.0", tenuto dal sottoscritto.

Per maggiori informazioni sui contenuti e i costi: http://www.fortechembeddedlabs.it/node/27

Posted On Thursday, March 11, 2010 10:53 PM | Comments (2)
RSS blues

It seems that the RSS feed is not updating.
If you missed the last post, here's a list:
Silverlight for Windows Embedded tutorial (step 4):
http://geekswithblogs.net/WindowsEmbeddedCookbook/archive/2010/03/09/silverlight-for-windows-embedded-tutorial-step-3-again.aspx
XAML2CPP 1.0.1.0:
http://geekswithblogs.net/WindowsEmbeddedCookbook/archive/2010/03/08/xaml2cpp-1.0.1.0.aspx

Posted On Thursday, March 11, 2010 8:22 PM | Comments (0)
Silverlight for Windows Embedded tutorial (step 4)

Val Veddasca (Italy)

I’m back with my Silverlight for Windows Embedded tutorial. Sorry for the long delay between step 3 and step 4, the MVP summit and some work related issue prevented me from working on the tutorial during the last weeks.

In our first,  second and third tutorial steps we implemented some very simple applications, just to understand the basic structure of a Silverlight for Windows Embedded application, learn how to handle events and how to operate on images.
In this third step our sample application will be slightly more complicated, to introduce two new topics: list boxes and custom control. We will also learn how to create controls at runtime.

I choose to explain those topics together and provide a sample a bit more complicated than usual just to start to give the feeling of how a “real” Silverlight for Windows Embedded application is organized.

As usual we can start using Expression Blend to define our main page. In this case we will have a listbox and a textblock.

Here’s the XAML code:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="ListDemo.Page"
    Width="640" Height="480" x:Name="ListPage" xmlns:ListDemo="clr-namespace:ListDemo">
 
    <Grid x:Name="LayoutRoot" Background="White">
        <ListBox Margin="19,57,19,66" x:Name="FileList" SelectionChanged="Filelist_SelectionChanged"/>
        <TextBlock Height="35" Margin="19,8,19,0" VerticalAlignment="Top" TextWrapping="Wrap" x:Name="CurrentDir" Text="TextBlock" FontSize="20"/>
    </Grid>
</UserControl>

 

 

In our listbox we will load a list of directories, starting from the filesystem root (there are no drives in Windows CE, the filesystem has a single root named “\”). When the user clicks on an item inside the list, the corresponding directory path will be displayed in the TextBlock object and the subdirectories of the selected branch will be shown inside the list.

As you can see we declared an event handler for the SelectionChanged event of our listbox.

We also used a different font size for the TextBlock, to make it more readable. XAML and Expression Blend allow you to customize your UI pretty heavily, experiment with the tools and discover how you can completely change the aspect of your application without changing a single line of code!

Inside our ListBox we want to insert the directory presenting a nice icon and their name, just like you are used to see them inside Windows 7 file explorer, for example. To get this we will define a user control. This is a custom object that will behave like “regular” Silverlight for Windows Embedded objects inside our application.

First of all we have to define the look of our custom control, named DirectoryItem, using XAML:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    x:Class="ListDemo.DirectoryItem" Width="500" Height="80">
 
    <StackPanel x:Name="LayoutRoot" Orientation="Horizontal">
        <Canvas Width="31.6667" Height="45.9583" Margin="10,10,10,10" RenderTransformOrigin="0.5,0.5">
            <Canvas.RenderTransform>
                <TransformGroup>
                    <ScaleTransform/>
                    <SkewTransform/>
                    <RotateTransform Angle="-31.27"/>
                    <TranslateTransform/>
                </TransformGroup>
            </Canvas.RenderTransform>
            <Rectangle Width="31.6667" Height="45.8414" Canvas.Left="0" Canvas.Top="0.116943" Stretch="Fill">
                <Rectangle.Fill>
                    <LinearGradientBrush StartPoint="0.142631,0.75344" EndPoint="1.01886,0.75344">
                        <LinearGradientBrush.RelativeTransform>
                            <TransformGroup>
                                <SkewTransform CenterX="0.142631" CenterY="0.75344" AngleX="19.3128" AngleY="0"/>
                                <RotateTransform CenterX="0.142631" CenterY="0.75344" Angle="-35.3436"/>
                            </TransformGroup>
                        </LinearGradientBrush.RelativeTransform>
                        <LinearGradientBrush.GradientStops>
                            <GradientStop Color="#FF7B6802" Offset="0"/>
                            <GradientStop Color="#FFF3D42C" Offset="1"/>
                        </LinearGradientBrush.GradientStops>
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle Width="29.8441" Height="43.1517" Canvas.Left="0.569519" Canvas.Top="1.05249" Stretch="Fill">
                <Rectangle.Fill>
                    <LinearGradientBrush StartPoint="0.142632,0.753441" EndPoint="1.01886,0.753441">
                        <LinearGradientBrush.RelativeTransform>
                            <TransformGroup>
                                <SkewTransform CenterX="0.142632" CenterY="0.753441" AngleX="19.3127" AngleY="0"/>
                                <RotateTransform CenterX="0.142632" CenterY="0.753441" Angle="-35.3437"/>
                            </TransformGroup>
                        </LinearGradientBrush.RelativeTransform>
                        <LinearGradientBrush.GradientStops>
                            <GradientStop Color="#FFCDCDCD" Offset="0.0833333"/>
                            <GradientStop Color="#FFFFFFFF" Offset="1"/>
                        </LinearGradientBrush.GradientStops>
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle Width="29.8441" Height="43.1517" Canvas.Left="0.455627" Canvas.Top="2.28036" Stretch="Fill">
                <Rectangle.Fill>
                    <LinearGradientBrush StartPoint="0.142631,0.75344" EndPoint="1.01886,0.75344">
                        <LinearGradientBrush.RelativeTransform>
                            <TransformGroup>
                                <SkewTransform CenterX="0.142631" CenterY="0.75344" AngleX="19.3128" AngleY="0"/>
                                <RotateTransform CenterX="0.142631" CenterY="0.75344" Angle="-35.3436"/>
                            </TransformGroup>
                        </LinearGradientBrush.RelativeTransform>
                        <LinearGradientBrush.GradientStops>
                            <GradientStop Color="#FFCDCDCD" Offset="0.0833333"/>
                            <GradientStop Color="#FFFFFFFF" Offset="1"/>
                        </LinearGradientBrush.GradientStops>
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle Width="29.8441" Height="43.1517" Canvas.Left="0.455627" Canvas.Top="1.34485" Stretch="Fill">
                <Rectangle.Fill>
                    <LinearGradientBrush StartPoint="0.142631,0.75344" EndPoint="1.01886,0.75344">
                        <LinearGradientBrush.RelativeTransform>
                            <TransformGroup>
                                <SkewTransform CenterX="0.142631" CenterY="0.75344" AngleX="19.3128" AngleY="0"/>
                                <RotateTransform CenterX="0.142631" CenterY="0.75344" Angle="-35.3436"/>
                            </TransformGroup>
                        </LinearGradientBrush.RelativeTransform>
                        <LinearGradientBrush.GradientStops>
                            <GradientStop Color="#FFCDCDCD" Offset="0.0833333"/>
                            <GradientStop Color="#FFFFFFFF" Offset="1"/>
                        </LinearGradientBrush.GradientStops>
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle Width="26.4269" Height="45.8414" Canvas.Left="0.227798" Canvas.Top="0" Stretch="Fill">
                <Rectangle.Fill>
                    <LinearGradientBrush StartPoint="0.142631,0.75344" EndPoint="1.01886,0.75344">
                        <LinearGradientBrush.RelativeTransform>
                            <TransformGroup>
                                <SkewTransform CenterX="0.142631" CenterY="0.75344" AngleX="19.3127" AngleY="0"/>
                                <RotateTransform CenterX="0.142631" CenterY="0.75344" Angle="-35.3436"/>
                            </TransformGroup>
                        </LinearGradientBrush.RelativeTransform>
                        <LinearGradientBrush.GradientStops>
                            <GradientStop Color="#FF7B6802" Offset="0"/>
                            <GradientStop Color="#FFF3D42C" Offset="1"/>
                        </LinearGradientBrush.GradientStops>
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle Width="1.25301" Height="45.8414" Canvas.Left="1.70862" Canvas.Top="0.116943" Stretch="Fill" Fill="#FFEBFF07"/>
        </Canvas>        
        <TextBlock Height="80" x:Name="Name" Width="448" TextWrapping="Wrap" VerticalAlignment="Center" FontSize="24" Text="Directory"/>
    </StackPanel>
</UserControl>

 

 

As you can see, this XAML contains many graphic elements. Those elements are used to design the folder icon. The original drawing has been designed in Expression Design and then exported as XAML.

In Silverlight for Windows Embedded you can use vector images. This means that your images will look good even when scaled or rotated.

In our DirectoryItem custom control we have a TextBlock named Name, that will be used to display….(suspense)…. the directory name (I’m too lazy to invent fancy names for controls, and using “boring” intuitive names will make code more readable, I hope!).

Now that we have some XAML code, we may execute XAML2CPP to generate part of the aplication code for us.

We should then add references to our XAML2CPP generated resource file and include in our code and add a reference to the XAML runtime library to our sources file (you can follow the instruction of the first tutorial step to do that),

To generate the code used in this tutorial you need XAML2CPP ver 1.0.1.0, that is downloadable here:
http://geekswithblogs.net/WindowsEmbeddedCookbook/archive/2010/03/08/xaml2cpp-1.0.1.0.aspx

We can now create our usual simple Win32 application inside Platform Builder, using the same step described in the first chapter of this tutorial (http://geekswithblogs.net/WindowsEmbeddedCookbook/archive/2009/10/01/silverlight-for-embedded-tutorial.aspx).
We can declare a class for our main page, deriving it from the template that XAML2CPP generated for us:

class ListPage : public TListPage<ListPage>
{
...
}

We will see the ListPage class code in a short time, but before we will see the code of our DirectoryItem user control. This object will be used to populate our list, one item for each directory.

To declare a user control things are a bit more complicated (but also in this case XAML2CPP will write most of the “boilerplate” code for use.

To interact with a user control you should declare an interface. An interface defines the functions of a user control that can be called inside the application code. Our custom control is currently quite simple and we just need some member functions to store and retrieve a full pathname inside our control. The control will display just the last part of the path inside the control.

An interface is declared as a C++ class that has only abstract virtual members. It should also have an UUID associated with it. UUID means Universal Unique IDentifier and it’s a 128 bit number that will identify our interface without the need of specifying its fully qualified name. UUIDs are used to identify COM interfaces and, as we discovered in chapter one, Silverlight for Windows Embedded is based on COM or, at least, provides a COM-like Application Programming Interface (API).

Here’s the declaration of the DirectoryItem interface:

class __declspec(novtable,uuid("{D38C66E5-2725-4111-B422-D75B32AA8702}")) IDirectoryItem : public IXRCustomUserControl
{
public:
 
    virtual HRESULT SetFullPath(BSTR fullpath) = 0;
    virtual HRESULT GetFullPath(BSTR* retval) = 0;
};

The interface is derived from IXRCustomControl, this will allow us to add our object to a XAML tree.
It declares the two functions needed to set and get the full path, but don’t implement them. Implementation will be done inside the control class. The interface only defines the functions of our control class that are accessible from the outside. It’s a sort of “contract” between our control and the applications that will use it. We must support what’s inside the contract and the application code should know nothing else about our own control.

To reference our interface we will use the UUID, to make code more readable we can declare a #define in this way:

#define IID_IDirectoryItem    __uuidof(IDirectoryItem)

Silverlight for Windows Embedded objects (like COM objects) use a reference counting mechanism to handle object destruction. Every time you store a pointer to an object you should call its AddRef function and every time you no longer need that pointer you should call Release. The object keeps an internal counter, incremented for each AddRef and decremented on Release. When the counter reaches 0, the object is destroyed.
Managing reference counting in our code can be quite complicated and, since we are lazy (I am, at least!), we will use a great feature of Silverlight for Windows Embedded: smart pointers.A smart pointer can be connected to a Silverlight for Windows Embedded object and manages its reference counting.
To declare a smart pointer we must use the XRPtr template:

typedef XRPtr<IDirectoryItem> IDirectoryItemPtr;

Now that we have defined our interface, it’s time to implement our user control class.
XAML2CPP has implemented a class for us, and we have only to derive our class from it, defining the main class and interface of our new custom control:

class DirectoryItem : public DirectoryItemUserControlRegister<DirectoryItem,IDirectoryItem>
{
...
}

XAML2CPP has generated some code for us to support the user control, we don’t have to mind too much about that code, since it will be generated (or written by hand, if you like) always in the same way, for every user control. But knowing how does this works “under the hood” is still useful to understand the architecture of Silverlight for Windows Embedded.

Our base class declaration is a bit more complex than the one we used for a simple page in the previous chapters:

template <class A,class B>
class DirectoryItemUserControlRegister : public XRCustomUserControlImpl<A,B>,public TDirectoryItem<A,XAML2CPPUserControl>
{
...
}

This class derives from the XAML2CPP generated template class, like the ListPage class, but it uses XAML2CPPUserControl for the implementation of some features. This class shares the same ancestor of XAML2CPPPage (base class for “regular” XAML pages), XAML2CPPBase, implements binding of member variables and event handlers but, instead of loading and creating its own XAML tree, it attaches to an existing one.

The XAML tree (and UI) of our custom control is created and loaded by the XRCustomUserControlImpl class. This class is part of the Silverlight for Windows Embedded framework and implements most of the functions needed to build-up a custom control in Silverlight (the guys that developed Silverlight for Windows Embedded seem to care about lazy programmers!). We have just to initialize it, providing our class (DirectoryItem) and interface (IDirectoryItem).

Our user control class has also a static member:

protected:
 
  static HINSTANCE hInstance;
  

This is used to store the HINSTANCE of the modules that contain our user control class. I don’t like this implementation, but I can’t find a better one, so if somebody has good ideas about how to handle the HINSTANCE object, I’ll be happy to hear suggestions!

It also implements two static members required by XRCustomUserControlImpl.

The first one is used to load the XAML UI of our custom control:

static HRESULT GetXamlSource(XRXamlSource* pXamlSource)
  {
    pXamlSource->SetResource(hInstance,TEXT("XAML"),IDR_XAML_DirectoryItem);
    return S_OK;
  }
 

It initializes a XRXamlSource object, connecting it to the XAML resource that XAML2CPP has included in our resource script.

The other method is used to register our custom control, allowing Silverlight for Windows Embedded to create it when it load some XAML or when an application creates a new control at runtime (more about this later):

static HRESULT Register()
{
  return XRCustomUserControlImpl<A,B>::Register(__uuidof(B), L"DirectoryItem", L"clr-namespace:DirectoryItemNamespace");
}

To register our control we should provide its interface UUID, the name of the corresponding element in the XAML tree and its current namespace (namespaces compatible with Silverlight must use the “clr-namespace” prefix.

We may also register additional properties for our objects, allowing them to be loaded and saved inside XAML. In this case we have no permanent properties and the Register method will just register our control.

An additional static method is implemented to allow easy registration of our custom control inside our application WinMain function:

static HRESULT RegisterUserControl(HINSTANCE hInstance)
{
  DirectoryItemUserControlRegister::hInstance=hInstance;
  return DirectoryItemUserControlRegister<A,B>::Register();
} 

Now our control is registered and we will be able to create it using the Silverlight for Windows Embedded runtime functions.

But we need to bind our members and event handlers to have them available like we are used to do for other XAML2CPP generated objects. To bind events and members we need to implement the On_Loaded function:

virtual HRESULT OnLoaded(__in IXRDependencyObject* pRoot)
{
    HRESULT retcode;
    IXRApplicationPtr app;
    
    if (FAILED(retcode=GetXRApplicationInstance(&app)))
        return retcode;
    
    return ((A*)this)->Init(pRoot,hInstance,app);
}  

This function will call the XAML2CPPUserControl::Init member that will connect the “root” member with the XAML sub tree that has been created for our control and then calls BindObjects and BindEvents to bind members and events to our code.

Now we can go back to our application code (the code that you’ll have to actually write) to see the contents of our DirectoryItem class:

class DirectoryItem : public DirectoryItemUserControlRegister<DirectoryItem,IDirectoryItem>
{
protected:
 
    WCHAR    fullpath[_MAX_PATH+1];
 
public:
 
    DirectoryItem() 
    {
        *fullpath=0;
    }
 
    virtual HRESULT SetFullPath(BSTR fullpath)
    {
        wcscpy_s(this->fullpath,fullpath);
 
        WCHAR* p=fullpath;
 
        for(WCHAR*q=wcsstr(p,L"\\");q;p=q+1,q=wcsstr(p,L"\\"))
            ;
 
        Name->SetText(p);
        return S_OK;
    }
 
    virtual HRESULT GetFullPath(BSTR* retval)
    {
        *retval=SysAllocString(fullpath);
        return S_OK;
    }
};

It’s pretty easy and contains a fullpath member (used to store that path of the directory connected with the user control) and the implementation of the two interface members that can be used to set and retrieve the path.

The SetFullPath member parses the full path and displays just the last branch directory name inside the “Name” TextBlock object.

As you can see, implementing a user control in Silverlight for Windows Embedded is not too complex and using XAML also for the UI of the control allows us to re-use the same mechanisms that we learnt and used in the previous steps of our tutorial.

Now let’s see how the main page is managed by the ListPage class.

class ListPage : public TListPage<ListPage>
{
protected:
 
    // current path
    TCHAR curpath[_MAX_PATH+1];

It has a member named “curpath” that is used to store the current directory.

It’s initialized inside the constructor:

ListPage()
{
    *curpath=0;
}

And it’s value is displayed inside the “CurrentDir” TextBlock inside the initialization function:

virtual HRESULT Init(HINSTANCE hInstance,IXRApplication* app)
{
    HRESULT retcode;
 
    if (FAILED(retcode=TListPage<ListPage>::Init(hInstance,app)))
        return retcode;
 
    CurrentDir->SetText(L"\\");
    return S_OK;
}

The FillFileList function is used to enumerate subdirectories of the current dir and add entries for each one inside the list box that fills most of the client area of our main page:

HRESULT FillFileList()
{
    HRESULT retcode;
    IXRItemCollectionPtr items;
    IXRApplicationPtr app;
 
    if (FAILED(retcode=GetXRApplicationInstance(&app)))
        return retcode;
    
    // retrieves the items contained in the listbox
    if (FAILED(retcode=FileList->GetItems(&items)))
        return retcode;
 
    // clears the list
    if (FAILED(retcode=items->Clear()))
        return retcode;
 
    // enumerates files and directory in the current path
    WCHAR filemask[_MAX_PATH+1];
 
    wcscpy_s(filemask,curpath);
    wcscat_s(filemask,L"\\*.*");
 
    WIN32_FIND_DATA finddata;
    HANDLE            findhandle;
 
    findhandle=FindFirstFile(filemask,&finddata);
 
    // the directory is empty?
    if (findhandle==INVALID_HANDLE_VALUE)
        return S_OK;
 
    do
    {
        if (finddata.dwFileAttributes&=FILE_ATTRIBUTE_DIRECTORY)
        {
            IXRListBoxItemPtr    listboxitem;
 
            // add a new item to the listbox
            if (FAILED(retcode=app->CreateObject(IID_IXRListBoxItem,&listboxitem)))
            {
                FindClose(findhandle);
                return retcode;
            }
 
            if (FAILED(retcode=items->Add(listboxitem,NULL)))
            {
                FindClose(findhandle);
                return retcode;
            }
 
            IDirectoryItemPtr directoryitem;
 
            if (FAILED(retcode=app->CreateObject(IID_IDirectoryItem,&directoryitem)))
            {
                FindClose(findhandle);
                return retcode;
            }
 
            WCHAR    fullpath[_MAX_PATH+1];
 
            wcscpy_s(fullpath,curpath);
            wcscat_s(fullpath,L"\\");
            wcscat_s(fullpath,finddata.cFileName);
 
            if (FAILED(retcode=directoryitem->SetFullPath(fullpath)))
            {
                FindClose(findhandle);
                return retcode;
            }
 
            XAML2CPPXRValue value((IXRDependencyObject*)directoryitem);
 
            if (FAILED(retcode=listboxitem->SetContent(&value)))
            {
                FindClose(findhandle);
                return retcode;
            }
        }
    }
    while (FindNextFile(findhandle,&finddata));
 
    FindClose(findhandle);
    return S_OK;
}

This functions retrieve a pointer to the collection of the items contained in the directory listbox. The IXRItemCollection interface is used by listboxes and comboboxes and allow you to clear the list (using Clear(), as our function does at the beginning) and change its contents by adding and removing elements.

This function uses the FindFirstFile/FindNextFile functions to enumerate all the objects inside our current directory and for each subdirectory creates a IXRListBoxItem object. You can insert any kind of control inside a list box, you don’t need a IXRListBoxItem, but using it will allow you to handle the selected state of an item, highlighting it inside the list.

The function creates a list box item using the CreateObject function of XRApplication. The same function is then used to create an instance of our custom control. The function returns a pointer to the control IDirectoryItem interface and we can use it to store the directory full path inside the object and add it as content of the IXRListBox item object, adding it to the listbox contents.

The listbox generates an event (SelectionChanged) each time the user clicks on one of the items contained in the listbox.
We implement an event handler for that event and use it to change our current directory and repopulate the listbox. The current directory full path will be displayed in the TextBlock:

HRESULT Filelist_SelectionChanged(IXRDependencyObject* source,XRSelectionChangedEventArgs* args)
{
    HRESULT    retcode;
 
    IXRListBoxItemPtr    listboxitem;
 
    if (!args->pAddedItem)
        return S_OK;
 
    if (FAILED(retcode=args->pAddedItem->QueryInterface(IID_IXRListBoxItem,(void**)&listboxitem)))
        return retcode;
 
    XRValue content;
    
    if (FAILED(retcode=listboxitem->GetContent(&content)))
        return retcode;
 
    if (content.vType!=VTYPE_OBJECT)
        return E_FAIL;
 
    IDirectoryItemPtr    directoryitem;
 
    if (FAILED(retcode=content.pObjectVal->QueryInterface(IID_IDirectoryItem,(void**)&directoryitem)))
        return retcode;
 
    content.pObjectVal->Release();
    content.pObjectVal=NULL;
 
    BSTR fullpath=NULL;
 
    if (FAILED(retcode=directoryitem->GetFullPath(&fullpath)))
        return retcode;
 
    CurrentDir->SetText(fullpath);
 
    wcscpy_s(curpath,fullpath);
    FillFileList();
    SysFreeString(fullpath);
 
 
    return S_OK;
}
};

The function uses the pAddedItem member of the XRSelectionChangedEventArgs object to retrieve the currently selected item, converts it to a IXRListBoxItem interface using QueryInterface, and then retrives its contents (IDirectoryItem object). Using the GetFullPath method we can get the full path of our selected directory and assing it to the curdir member. A call to FillFileList will update the listbox contents, displaying the list of subdirectories of the selected folder.

To build our sample we just need to add code to our WinMain function:

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;
 
    if (FAILED(retcode=DirectoryItem::RegisterUserControl(hInstance)))
        return retcode;
 
    ListPage page;
 
    if (FAILED(page.Init(hInstance,app)))
        return -1;
 
    page.FillFileList();
 
    UINT exitcode;
 
    if (FAILED(page.GetVisualHost()->StartDialog(&exitcode)))
        return -1;
 
    return 0;
}

This code is very similar to the one of the WinMains of our previous samples. The main differences are that we register our custom control (you should do that as soon as you have initialized the XAML runtime) and call FillFileList after the initialization of our ListPage object to load the contents of the root folder of our device inside the listbox.

As usual you can download the full sample source code from here:
http://cid-9b7b0aefe3514dc5.skydrive.live.com/self.aspx/.Public/ListBoxTest.zip

Posted On Tuesday, March 9, 2010 1:00 AM | Comments (11)
XAML2CPP 1.0.1.0

DSC02057 (Small)

I upgraded XAML2CPP to fix some bugs and support some new features that will be used in my next tutorial step (that I hope to publish very soon!).

Bug fixed:

  • the listbox selection changed event was generated with an incorrect prototype
  • events generated by the XAML tree root element (UserControl) were not handled.

New features:

  • XAML2CPP generates panels (dynamically generated XAML subtrees that may be added to a page) and user controls (those will be the object of my next post, stay tuned!).
  • XAML2CPPXRValue class has been added to XAML2CPPbase.h. This class will allow single-line declaration of XRValue objects (I’m lazy!) and destroys automatically objects and string XRValues when the object itself is destroyed.

You can download the latest XAML2CPP release here:
http://cid-9b7b0aefe3514dc5.skydrive.live.com/self.aspx/.Public/XAML2CPP.zip

Posted On Monday, March 8, 2010 12:57 AM | Comments (0)
Toradex logo

Tag Cloud