Posts
80
Comments
214
Trackbacks
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 Print
Comments
Gravatar
# re: XAML2CPP
Andy Wigley
12/10/2009 11:15 PM
Great work Valter!

However, I ran version 1.0.0.1 against a simple XAML page likethe one you created in Tutorial 1, and it created no code to add the Click event handler in the BindEventHandlers method.
Any idea why this didn't work?

My XAML:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SimpleSLapp.Page"
Width="640" Height="480" Background="#FFC42929">
<UserControl.OpacityMask>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF000000"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>
</UserControl.OpacityMask>

<Grid x:Name="LayoutRoot">
<Button Height="99" Margin="176,0,216,130" VerticalAlignment="Bottom" Content="Button" Background="#FF32A625" x:Name="MyButton"/>
</Grid>
</UserControl>

- Andy
Gravatar
# re: XAML2CPP
Valter Minute
12/15/2009 12:22 AM
Hi Andy,
to create an event handler for the Click event of your button you should specify a name for it using the events "tab" of the object properties page in Expression Blend or by adding the "Click" attribute to your button in the XAML file.
XAML2CPP "translates" to objects and functions only object and event that are named inside the XAML, to reduce the amount of generated code and the memory used to run your application.
Gravatar
# re: XAML2CPP
Marie
3/29/2010 9:18 AM
I used xaml2cpp with a pretty simple app that plays a video. I only have an emulator for testing right now; the video does not play. Is there something more to the C++ code after xaml2cpp to enable video?
Gravatar
# re: XAML2CPP
Valter Minute
3/29/2010 6:54 PM
Ciao Marie,
the video object is not supported by Silverlight for Windows Embedded.
To include a video you need to create a win32 control and "host" Windows Media Player inside it, build your own DirectShow pipeline to play a specific kind of video or build your own video player (the first solution is the simplest one, of course).
Gravatar
# re: video object is not supported by Silverlight for Windows Embedded
Marie
3/30/2010 8:03 AM
Thank you for confirming something I have been trying to prototype for days! Regarding your suggestion to create a win32 control and host Windows Media Player inside it - can you provide alittle more detail. Use VS2005 project? Use Blend 2? Any help with a sample?
Gravatar
# re: XAML2CPP
Valter Minute
3/31/2010 9:03 PM
Ciao Marie,
I hope to write something on Win32 control integration with Silverlight for Windows Embedded in my next tutorial step (I needed some ideas about new topics and your question provided an interesting one).
Gravatar
# re: XAML2CPP
Marie
5/11/2010 10:33 AM
Looking forward to some information on WIN32 control integration in a future posting. Thanks for the assistance.
Gravatar
# re: XAML2CPP
Chris
7/21/2010 1:06 PM
Hi Valter,
I see in the T_Page.h class a "..UserControlRegister" class...how is that used if I want to add a UserControl to a page dynamically?
Gravatar
# re: XAML2CPP
Valter Minute
8/9/2010 7:37 PM
@Chris
You can find a sample here:
http://geekswithblogs.net/WindowsEmbeddedCookbook/archive/2010/03/09/silverlight-for-windows-embedded-tutorial-step-3-again.aspx
Gravatar
# re: XAML2CPP
Ayman
8/13/2010 2:44 AM
Hi Valter,

You said in this tutorial that we should not change or modify the code resultant from XAML2CPP, but I noticed the following:
1) The file called XAML2CPPBase.h is always the same no matter what the input XAML file was.
2) The file generated XAML2CPP_res.h file only differs in the name of the defined resource, so I add different resource defined names inside it, rather than having a different file for each XAML file, I think there is no need for that.
3) XAML2CPP.h also contains the different includes for different generated files from XAML, so also no need for separate different files, I add the includes here for different generated files from XAML.
4) The file XAML2CPP.rc also have the same issue, I just add different generated names inside it.

So are my comments here correct or what?

Regards,

Ayman
Gravatar
# re: XAML2CPP
Ayman
8/13/2010 5:43 AM
The other thing I want to talk about Valter is that working with Silverlight is not as intuitive as working with regular windows stuff, I want to refer here to our conversation about the dialog based application, I tried to do as you advised, the first approach, closing the main dialog and showing the other one, didn't work, because the other dialog is a child of the main dialog, so how can I close the main and show the child, the second approach, hiding the main dialog and showing the child also didn't work, the main dialog got hidden but the child never been displayed, it only appears as a minimized dialog on the task bar.

I know Valter that I am missing something here, but as I was reading around about Silverlight, I think it handles such issues in a whole different way, which I still don't understand.
Gravatar
# re: XAML2CPP
Valter Minute
8/16/2010 12:15 AM
@Ayman
I said to not change that code because XAML2CPP re-generates it every time you run it, overwriting any modification you made to those files.
1) Yes, this file is the same every time you run the same version of XAML2CCP. I may distribute it separately, but if you upgrade the exe you may need a different header, so generating it everytime avoids compatibility issues.
2,3,4) you can mantain those files by hand, but be careful about overwrites when you execute XAML2CPP.

I agree that the approach is different in Silverlight. I told you to open a new dialog, forgetting to tell you to close the previous one before. In this way it should work.
Usually Silverlight (non for embeeded) apps are designed to be hosted inside a browser page and so they don't open dialogs and usually load another custom control inside the main page or show/hide the panes they need.
Gravatar
# re: XAML2CPP
nathaniel
1/10/2011 6:55 PM
Hi Valter,
wonderful work, very very useful, and one can testimony I am a big big lazy man...
Anyway, after, I do all the steps you explained, I get a compilation error:

BUILD: [01:0000000055:ERRORE] c:\wince600\osdesigns\osdesign2\osdesign2\generatetest1\T_Page.h(11) : fatal error C1083: Cannot open include file: 'pwinuser.h': No such file or directory.

The weird is that pwinuser.h is in the C:\WINCE600\PUBLIC\COMMON\OAK\INC, I have also made sure this folder is defined in the project options as a folder for the includes.

Maybe someone can help me, maybe someone has something to propose.

Thank you very much,

Nathaniel

PS: I have recently sent 2 messages on other pages of your blog (step 1) and no one has been published, did you get them?

Post Comment

Title *
Name *
Email
Comment *  
Verification
Toradex logo

Cookies in Use
Tag Cloud