Posts
76
Comments
208
Trackbacks
0
November 2009 Entries
Silverlight for Windows Embedded tutorial (step 3)

After the first two tutorial steps were published on this blog I received many requests about using images inside a Silverlight for Windows Embedded application. This is the topic of this post.
To be able to load and use image files (jpegs,bmps,gifs) inside your application you should include the imaging library components in your OSDesign.
Those component are not included automatically when you add the XAML runtime (the runtime can run also without the imaging components, it will simply not load your images!).
To display an image inside your application user interface you have to use the image control of Silverlight.
This is a very simple XAML file that includes just an Image object and a button:


<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="ImgTest.Page"
    Width="640" Height="480" x:Name="ImagePage">
    <Grid x:Name="LayoutRoot" Background="White">
        <Image Margin="17,25,25,103" x:Name="MyImage" Source="\Windows\img01.JPG"/>
        <Button Height="49" Margin="259,0,253,28" VerticalAlignment="Bottom" Content="Button" x:Name="MyButton" Click="OnClick"/>
    </Grid>
</UserControl>

The image object is named "MyImage" and the button is named "MyButton".

We also have an OnClick event handler for the Click event of the button.

We can create a new Win32 application inside platform builder.

Using the XAML2CPP we can generate some code for us.

We will just have to include "XAML2CPP.h" inside your main C++ source file to use the code that XAML2CPP created for us:

#include "XAML2CPP.h" 

We also have to include "XAML2CPP.rc" inside the rc file of our application to include the XAML code as a resource inside our application.

XAML2CPP has created a base class that we can use to implement our own class and handle the OnClick event:

class ImagePage : public TImagePage<ImagePage>  {  ...  }


In this sample we will just swap two images inside the image control each time you click on the button.
We have to declare a state flag (to be able to swap images) and two IXRBitmapImagePtr objects to store our bitmaps.

    bool state;          IXRBitmapImagePtr    img01;     IXRBitmapImagePtr    img02;   

A very simple constructor will reset the state:

    ImagePage()     {          state=false;          }  

The main initialization will be performed inside our Init method (it's not a good idea to put this kind of initialization code inside the constructor because some API calls may fail and you don't have a way to return an error code from a C++ constructor).

    virtual HRESULT Init(HINSTANCE hinstance,IXRApplication* app)          {             ...     } 

Inside our Init method we have to call the Init method of our base class (declared by XAML2CPP), and check its return code for errors:

        HRESULT retcode;                    if (FAILED(retcode=TImagePage<ImagePage>::Init(hinstance,app)))              return retcode;

We declared two IXRBitmapImagePtr objects but we still haven't initialized them.
To create a Silverlight for Windows Embedded object we should use the CreateObject method of the IXRApplication object:

        if (FAILED(retcode=app->CreateObject(IID_IXRBitmapImage,&img01)))              return retcode;         if (FAILED(retcode=app->CreateObject(IID_IXRBitmapImage,&img02)))              return retcode;

The we can load the bitmaps and store them inside the IXRBitmapImagePtr objects:

        if (FAILED(retcode=img01->SetUriSource(TEXT("\\Windows\\img01.jpg"))))                           return retcode;         if (FAILED(retcode=img02->SetUriSource(TEXT("\\Windows\\img02.jpg"))))              return retcode;

In the OnClick event handler we just have to swap the image displayed by the MyImage object and change the state flag:

    HRESULT OnClick(IXRDependencyObject* source,XRMouseButtonEventArgs* args)      {                    HRESULT retcode;                    if (FAILED(retcode=MyImage->SetSource(state?img01:img02)))                          return retcode;                   state=state?false:true;                  return S_OK;           }

The main function of this sample is quite simple and it's not much different for the WinMain functions of the previous samples (just the class name changes):

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;        ImagePage imagepage;        if (FAILED(imagepage.Init(hInstance,app)))          return -1;        UINT exitcode;        if (FAILED(imagepage.GetVisualHost()->StartDialog(&exitcode)))          return -1;        return 0;   }

You can download the full source code of this sample here:

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

 

Posted On Wednesday, November 18, 2009 9:54 AM | Comments (21)
XAML2CPP ver. 1.0.0.1

I just found (and fixed) some bugs inside the XAML2CPP tool.

You can download the new release from here:

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

Changes history:

ver 1.0.0.1

- the application version is inserted in the generate files and written on the console

- the x:Name tag of the user control is used as class name instead of the XAML file name. If no x:Name attribute is specified the XAML file name will be used.

 

Posted On Wednesday, November 18, 2009 9:37 AM | Comments (0)
XAML2CPP


Before I start to explain the topic of this post I should confess one of my many defects: I'm very lazy.
Someone may have noticed that from the update rate of this blog, but I really like to avoid as much work as I can.
I also really like to experiment new technologies and embedded devices, and that's bad for a lazy guy because that means having to write some code...
While experimenting with Silverlight for Windows Embedded I found myself trying to write some more complex samples to continue my own tutorial (it's idle, but I told you that I'm lazy...) but I hate to write all the "boilerplate" code required to connect C++ objects to XAML code and register event handlers on those objects.
Being as lazy as I am I decided that writing a simple tool once it's better than re-writing more or less the same code over and over again, so I wrote a small tool that without much fantasy (I'm lazy also about coming out with fancy names for tools!) I called XAML2CPP.
XAML2CPP is a very simple command line utility that parses a XAML file and generates some C++ code for you.
It generate a class for each XAML file (I suppose that you have one user control per file so that makes sense) with all the code needed to access the objects inside your XAML and to invoke event handlers when an event is generated by the runtime.
It also generates a "cumulative" include file (to avoid to include each class file in my sources, you know...I'm lazy) and resource definitions (to avoid to define your own XAML resources).
It generates code only for objects and event handlers that have a name (memory is not an infinite resource, as our colleagues programming desktop application may believe, and declaring objects and event handler for everything looks like a waste to me).
Let's start with an easy sample (I'm too lazy to write a complex one!):

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="SimpleApp.Page" 
Width="640" Height="480">
    <Grid x:Name="LayoutRoot" Background="White">
        <Button Height="87" Margin="189,106,209,0" VerticalAlignment="Top" Content="Button" 
x:Name="MyButton" Click="OnClick"/>
    </Grid>
</UserControl>


As you can see this is more or less the same XAML code we used in the first tutorial step (being lazy it's easier to modify an existing sample than creating a new one!), I just specified "OnClick" as name for the Click event handler of the MyButton object.
If we run XAML2CPP using this command line syntax:
XAML2CPP Page.XAML
It will generate five source files:
XAML2CPP.rc
XAML2CPP.h
XAML2CPPBase.h
XAML2CPP_res.h
T_Page.h
I'm lazy, but my tools aren't: five files out of a single one!

Never change the code inside those files, if your run XAML2CPP again it will overwrite them and your modifications will be lost forever (and even if you are not lazy like me, rewriting the same code over and over again after it has been overwritten don't look so smart).
I know that modifications are not lost forever because you are a good programmer and you have multiple backups of all your sources... but that doesn't change the concept: never modify XAML2CPP generated code!
And also never use for your own files the same file names that may be generated by XAML2CPP, of course.
Let's see what those files contains and how you can integrate them in your Silverlight for Windows Embedded application.
XAML2CPP.rc includes all the resource definitions that we need to include our XAML inside the executable file resources.
In this case it will include only our Page.XAML file (you can run XAML2CPP with multiple input file names or with wildcards to generate multiple XAML resources).
Those are the contents of XAML2CPP.rc:
XAML_RESOURCE_Page   XAML ".\Page.xaml"
Pretty minimal, isn't it? But this has saved writing at least one row of code. Since I had to write "xaml2cpp page.xaml" on the command line this hasn't saved too much time... let's see what's inside other files.
XAML2CPP_res.h includes the definitions of the resources that XAML2CPP brought inside your executable.

/*
This file has been generated by XAML2CPP tool.
Modifications to this source code may be overwritten without warning when the XAML2CPP tool is executed.
XAML2CPP (c) 2009 by Valter Minute (valter.minute@gmail.com)
This code is provided as is and it's generated automatically. It's up to the developer to check that it works as expected.
*/
/*
This file includes all the resource identifiers of the XAML files embedded inside the application resources
*/
#ifndef XAML2CPP_RES_H
#define XAML2CPP_RES_H
#define IDR_XAML_Page TEXT("XAML_RESOURCE_Page")
#endif //XAML2CPP_RES_H


Four rows of code, not including the copyright. Not bad!
XAML2CPPBase.h will have always the same content, it's the definition of a class (XAML2CPPBase) that is used as base class for all the XAML2CPP generated classes. Even if that's not strictly necessary, the file is generated by XAML2CPP each time it's run. In this way some modifications of the base classes introduced by a new release of the tool will not require re-distribution of a new header and udating it inside all the project using it (sometimes being lazy may prevent some maintenance nightmare).
This will allow a single declaration of some members and methods that are common between all those objects.
After the copyright you'll find the base class declaration:

class XAML2CPPBase
{
...
}

In this class we will find some XAML runtime objects:

    // Pointer to the visual host
    IXRVisualHostPtr vhost;
    
    // Pointer to the root of the XAML visual tree
    IXRFrameworkElementPtr root;

and some methods to access them (they are declared as protected inside our base class):

    // returns the visual host
    IXRVisualHost* GetVisualHost() { return vhost; }
    
    // returns the visual tree root
    IXRFrameworkElement* GetRoot() { return root; }


We also find two string pointers and a constructor to inizialize them:

    // Pointer to the page title
    TCHAR* windowtitle;
    
    // Pointer to the resource name
    TCHAR* xamlresourceid;
    
public:

    XAML2CPPBase(TCHAR* windowtitle,TCHAR* xamlresourceid)
    {
      this->windowtitle=windowtitle;
      this->xamlresourceid=xamlresourceid;
    }

Those pointers will be used to set the window title for our visual host (you may not need to set a title if your window has no title bar) and the resource id of the XAML associated with a specific instance of a XAML2CPPBase derived class.
Those field are used in the two function that setup window creation parameters and XAML loading method:

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

    // Set the XAML source path. By default loads the XAML that is included in the resources script
    virtual void SetXAMLSource(HINSTANCE hInstance,XRXamlSource* xamlsrc)
    {
        xamlsrc->SetResource(hInstance,TEXT("XAML"),xamlresourceid);
    }

 

 

In this way we will create an overlapped (top level) window with the title we passed to the constructor and we will load our XAML from the resource specified by the other constructor parameter.
If we want to change this behavior we may override InitWindowParms or SetXAMLSource method in our derived class. Lazy people love C++ inheritance!

Those method are called by the CreateHost method, the method that initializes our visual host and loads our XAML:

    // create the visual host and loads the XAML
    virtual HRESULT CreateHost(HINSTANCE hInstance,IXRApplication* app)
    {
        HRESULT retcode;
        
        XRWindowCreateParams wp;

        ZeroMemory(&amp;wp, sizeof(XRWindowCreateParams));
        
        InitWindowParms(&amp;wp);

        XRXamlSource xamlsrc;

        SetXAMLSource(hInstance,&amp;xamlsrc);
        
        if (FAILED(retcode=app-&gt;CreateHostFromXaml(&amp;xamlsrc, &amp;wp, &amp;vhost)))
            return retcode;

        if (FAILED(retcode=vhost-&gt;GetRootElement(&amp;root)))
            return retcode;
        
        return S_OK;
    } 

This method is also marked as virtual so you may override it if you need to perform some particular initialization steps inside your own class. Usually overriding InitWindowParms and SetXAMLSource should be enough but a virtual function call during initialization won't impact performances so much, so I preferred to leave more room for customization in this case.
If you override CreateHost remember that the rest of the code expects root and vhost smart pointers to be initialized after this method has been called.

Now let's see what's inside XAML2CPP.h.

/*
This file has been generated by XAML2CPP tool.
Modifications to this source code may be overwritten without warning when the XAML2CPP tool is executed.
XAML2CPP (c) 2009 by Valter Minute (valter.minute@gmail.com)
This code is provided as is and it's generated automatically. It's up to the developer to check that it works as expected.
*/
/*
This header includes all the classes generated by XAML2CPP the last time it was executed.
*/
#ifndef XAML2CPP_H
#define XAML2CPP_H

#include "T_Page.h"

#endif //XAML2CPP_H

In this file you'll find an #include directive for each class that has been generated by XAML2CPP from the XAML files it processed. This will allow you to simply include "XAML2CPP.h" in each source file where you need to access one of the classes generated by the tool. Including it in your pre-compiled header file (usually stdafx.h) could save some build time.
Now let's see in detail what's inside T_Page.h.

#ifndef Page_TEMPLATE_HEADER_FILE_H
#define Page_TEMPLATE_HEADER_FILE_H
#include "windows.h"
#include "pwinuser.h"
#include "xamlruntime.h"
#include "xrdelegate.h"
#include "xrptr.h"
#include "XAML2CPP_res.h"

After an #ifdef used to avoid multiple inclusion of this file you'll find a list of includes for all the header  files needed to build a Silverlight for Windows Embedded  application. This will save some time and some time spent understanding build errors.

Then we find a template declaration:

/*
class generated by XAML2CPP from .\Page.xaml
*/
template <class X>
 class TPage : public XAML2CPPBase
 {
 ...
 }
 


I used templates to be able to link event handlers directly to your own code and not to some code generated by the tool.
I may have used a function invoking a pure virtual method inside my class and let you implement your own method in a derived class but that may have a (small) impact on performances.
We will see how we can derive our own class from the one generated by XAML2CPP in a short time. Right now, let's focus on the TPage class code.
We have a constructor that takes two (optional) parameters and passes them to the XAML2CPPBase constructor. In this way you can change the title of your window and XAML resource id or use the default ones generated by the tool.

public:
    TPage(TCHAR* title=TEXT("Page"),TCHAR* xamlid=IDR_XAML_Page) : XAML2CPPBase(title,xamlid)
    {
    }

The we have two smart pointers:

protected:

    // XAML defined objects (declared as smart pointers)
    IXRGridPtr LayoutRoot;
    IXRButtonPtr MyButton;

The tool generates them using the names you specified in the x:Name attribute of your XAML code (or that you set in the properties window inside Expression Blend, of course).
For each object with an x:Name attribute in your XAML you'll have an object, no need to declare them! Great for lazy people and also great to avoid that a designer changes the name of an object without informing you. In this way you can re-generate code and then you'll have to replace the old name with the new one but you'll discover that at build time, not when you run you application.
Those smart pointer need to be bound to XAML objects and that's done inside the BindObjects method:

    // binds objects smart pointers to objects created by the runtime
    virtual HRESULT BindObjects()
    {    
        HRESULT retcode;
            
            
        if (FAILED(retcode=root->FindName(L"LayoutRoot",&LayoutRoot))) 
            return retcode;
                       
            
        if (FAILED(retcode=root->FindName(L"MyButton",&MyButton))) 
            return retcode;
                       
    
        return S_OK;
    }


The code of this method looks boring and repetitive... but you don't have to write it, XAML2CPP has generated it for you. And no chances to type the wrong name or forget something during cut&paste (I know that you write this kind of code using cut&paste, like I do!).
Now we have to bind our event handlers to the objects, and that's exactly what the BindEventHandlers method does:

// binds event handlers to template class member functions   
    // should be called after BindObjects
    virtual HRESULT BindEventHandlers()
    {
        HRESULT retcode;
        
    
        //declare your own event handler as 
        // HRESULT OnClick(IXRDependencyObject* source,XRMouseButtonEventArgs* args)
        IXRDelegate<XRMouseButtonEventArgs>* OnClickDelegate;

        retcode=CreateDelegate<X,XRMouseButtonEventArgs>((X*)this,&X::OnClick,&OnClickDelegate);
        
        if (FAILED(retcode))        
          return retcode;
            
    
        if (FAILED(retcode=MyButton->AddClickEventHandler(OnClickDelegate)))
            return retcode;
    
        OnClickDelegate->Release();
    
        return S_OK;
    }

As you can see a delegate is created using the OnClick method of the class parameter passed to the template and this is your own class. You'll have to declare the OnClick method inside your class. If you don't you'll get a build error on the CreateDelegate call. Just  above that line you'll find a prototype of that method ready to be cut and pasted inside your code!

Now we have seen how to run XAML2CPP and the code it has generated. We still have to see how we can integrate it in our own code.

You can start by creating an empty Win32 application subproject inside Platform Builder.

You can repeat the steps we did in the first Silverlight for Windows Embedded tutorial here:

http://geekswithblogs.net/WindowsEmbeddedCookbook/archive/2009/10/01/silverlight-for-embedded-tutorial.aspx

Then move your XAML file inside the subproject directory and run XAML2CPP on it. You'll have all the files we described above inside your subproject directory.

Add a resource (.rc) script to your project, as described in the tutorial but don't add the XAML resource to it.

Simply include the .rc script that XAML2CPP has generated for you.

Move to the "resource view" tab, right click on your project .rc file and select "Resource includes...".

Add:

#include "XAML2CPP.rc"

and close the dialog.

In this way you'll include all the resources generated by XAML2CPP, you don't need to add them by hand (and discover that you forgot that when you run your application!).

Now you can edit our main .cpp source file.

Fist of all you can include "XAML2CPP.h" :

#include "XAML2CPP.h"

Then you can declare a class to implement the code that XAML2CPP hasn't (and could not have) generated for you:

class Page : public TPage<Page>
{
public:

    HRESULT OnClick(IXRDependencyObject* source,XRMouseButtonEventArgs* args)
    {
        MessageBox(NULL,TEXT("Click!"),TEXT("Click!"),MB_OK);
        return S_OK;
    }
};

As you can see the class is quite simple. You'll have to write only your own even hander code (and you may copy its prototype from the comment inside TPage.h, a dream for a lazy programmer like me!). No initialization code, no event binding code.

Sounds great?

Let's see what you have to add to your WinMain function to create your own XAML UI.

You should still add the runtime initialization code (you can copy it from our previous sample, so don't be too lazy!):

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;

Now you can create and display your XAML page:

    Page page;

    if (FAILED(page.Init(hInstance,app)))
        return -1;

    UINT exitcode;

    if (FAILED(page.GetVisualHost()->StartDialog(&exitcode)))
        return -1;

    return 0;

You should declare an istance of your class, call Init on it (passing the application HINSTANCE and a pointer to the XAML runtime application object) and then you can show it accessing its visual host methods.

As you can see XAML2CPP has generated only the "boilerplate" code and just a simple wrapper around some features (initialization, mostly). It's not a big framework, I don't like them and I'm too lazy to build one!

You can download XAML2CPP from here:

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

It's still an alpha release, so use it carefully (read that as backup everything you can backup and don't blame me for problems) and let me know if you experience problems or have ideas about ways to improve it.

As I told you, I'm a lazy guy... so don't hold your breath waiting for updateas and bugfixes. But I promise that I'll try to improve this tool and when the code is stable and readable enough, maybe publish it on codeplex or other open-source repositories.

 

Posted On Wednesday, November 11, 2009 12:59 AM | Comments (13)
Toradex logo

Tag Cloud