Exit Wiki

Using XRC in your Application.

This example has been created to show key concepts about using XRC files to define your GUI, then using those files in a wxWindows C++ program.

This program shows the following concepts:

#1: How to load your XRC objects (and potential gottchas)

#2: How to handle simple view events (enter key down, menu selected)

#3: How to retrieve other XRC specified objects

A fair number of the inline comments were taken from the wxWindows MINIMAL example, and comments created by WD-rpw are marked as such.

This sample assumes that you're familiar with the basics of creating a wxWindows application, and thus only explains the code that interacts with the XRCs.

First, The Code


#include "wx/filefn.h"
#include "xrcdemo.h" //where all our definitions are. Shown at the end.

//-----------------------------------------------------------------------------

#include "wx/xrc/xmlres.h"          // XRC XML resouces

//-----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
// event tables and other macros for wxWindows
// ----------------------------------------------------------------------------

// the event tables connect the wxWindows events with the functions (event
// handlers) which process them. It can be also done at run-time, but for the
// simple menu events like this the static method is much simpler.

BEGIN_EVENT_TABLE(MyFrame, wxFrame) 

        //file menu events
        //these are defined in the XRC file
        EVT_MENU(XRCID("exit_tool_or_menuitem"), MyFrame::OnQuit)
        
        EVT_TEXT_ENTER(XRCID("user_input_textctrl"), MyFrame::OnUserInput)
        //NOTE that the object must be specified with the wxTE_PROCESS_ENTER style,
        //else you will not get this event.
        //WD-rpw 7/18/03
        
END_EVENT_TABLE()


// Create a new application object: this macro will allow wxWindows to create
// the application object during program execution (it's better than using a
// static object for many reasons) and also declares the accessor function
// wxGetApp() which will return the reference of the right type (i.e. MyApp and
// not wxApp)

IMPLEMENT_APP(MyApp)

void cw8FunctionPopupWorkAround(void)
{
        //this only exists to work around a function popup bug in CodeWarrior 8.
        //ignore it - it will (or should be) be ignored by the linker.
        //WD-rpw 07-18-03

}

// 'Main program' equivalent: the program execution "starts" here
bool MyApp::OnInit()
{
         
         MyFrame *frame;
         wxString menuPath;
         wxString dlgPath;
         bool dlgExists, menuExists;
         
        // Initialize all the XRC handlers. Always required (unless you feel like
    // going through and initializing a handler of each control type you will
    // be using (ie initialize the spinctrl handler, initialize the textctrl
    // handler). However, if you are only using a few control types, it will
    // save some space to only initialize the ones you will be using. See
    // wxXRC docs for details.

         wxXmlResource::Get()->InitAllHandlers();
         
         //Load all of the XRC files that will be used
         //first, build a path to where they are stored
         //WD-rpw 07-18-03
         
     menuPath = wxGetCwd() + wxFILE_SEP_PATH + "rc" + wxFILE_SEP_PATH + "menu.xrc";
         dlgPath = wxGetCwd() + wxFILE_SEP_PATH + "rc" + wxFILE_SEP_PATH + "basicdlg.xrc";
         
         menuExists = wxFileExists((const char*)menuPath);
         dlgExists = wxFileExists((const char*) dlgPath);
         
         if (menuExists && dlgExists)
         {
                 //make sure the files exist!
                //WD-rpw 7/18/03
                menuPath = wxFileSystem::FileNameToURL(menuPath);
                dlgPath = wxFileSystem::FileNameToURL(dlgPath);
                
                wxXmlResource::Get()->Load(wxT(menuPath));
                wxXmlResource::Get()->Load(wxT(dlgPath));
                
                frame = new MyFrame();  //construct the frame - it will load itself from an XRC
         
                // Load all of the XRC files that will be used. You can put everything
                // into one giant XRC file if you wanted, but then they become more 
                // diffcult to manage, and harder to reuse in later projects.   
                 
                          
                 // and show it (the frames, unlike simple controls, are not shown when
                 // created initially)
                 frame->Show(TRUE);
        
                 // success: wxApp::OnRun() will be called which will enter the main message
                 // loop and the application will run. If we returned FALSE here, the
                 // application would exit immediately.
                 return TRUE;
        }
        else
        {
                //we can't load our XRCs. I suppose we could put a good error message here, 
                //but we just exit for now by returning false
                //WD-rpw 7/18/03
                return FALSE;
        }
}

// ----------------------------------------------------------------------------
// main frame
// ----------------------------------------------------------------------------

MyFrame::MyFrame(wxWindow* parent)
{

        wxXmlResource::Get()->LoadFrame(this, parent, wxT("main_frame"));
        //load our data from a XRC file (as opposed to specifying it staticly using C++ methods)
        //WD-rpw 7/18/03
#if wxUSE_MENUS

        wxMenuBar* theBar;
        
        theBar = wxXmlResource::Get()->LoadMenuBar(wxT("menu"));        
        //if we care about menu bars, load ours from the resource
        //WD-rpw 7/18/03
        SetMenuBar(theBar);
        
    
#endif // wxUSE_MENUS
}


//event handlers

void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
     // TRUE is to force the frame to close
     Close(TRUE);
}

void MyFrame::OnUserInput(wxCommandEvent& event)
{
        wxCommandEvent localEvent = event;
        wxString myStr = event.GetString();
        
        wxTextCtrl* outputText = XRCCTRL(*this, "output_textctrl", wxTextCtrl);
        //XRCCTRL - from the data we have loaded deep inside 'this', pull out the control named 'output_textctrl'
        //and return it as type wxTextCtrl
        //WD-rpw 7/18/03
        
        outputText->AppendText(myStr);
        outputText->AppendText("n");
        //internally wxTextCtrls use newlines to represent line endings.
        //WD-rpw 7/18/03

}

Several things to notice about this code:

wxGetCwd() and friends explained


bool MyApp::OnInit()
{
        ...

        menuPath = wxGetCwd() + wxFILE_SEP_PATH + "rc" + wxFILE_SEP_PATH + "menu.xrc";

        ...
        

With some operating systems (Mac) you can't be guaranteed what your working directory is. Make sure you use wxGetCwd(), which will always return the path to your application. Then you can use wxString operators to add things to that path.

XRCCTRL Explained


void MyFrame::OnUserInput(wxCommandEvent& event)
{

        ...
        
        wxTextCtrl* outputText = XRCCTRL(*this, "output_textctrl", wxTextCtrl);

        ...
        
}

The XRCCTRL macro is defined as:


 #ifdef __WXDEBUG__
#define XRCCTRL(window, id, type) 
    (wxDynamicCast((window).FindWindow(XRCID(id)), type))
#else
#define XRCCTRL(window, id, type) 
    ((type*)((window).FindWindow(XRCID(id))))


How do I read this?

It's easier to read it (IMHO) if the macro is expanded.

Read it like


((wxTextCtrl*)((this).FindWindow(XRCID("output_textctrl"))));

Find the item "output_textctrl" in this, and cast that result into a wxTextCtrl.

The explaination of FindWindow says this:

So now we have a pointer to the wxTextCtrl object, and can use it just like any other C++ pointer.

Because all the controls in wxWindows inherit from the wxWindow class!

GetString Explained


void MyFrame::OnUserInput(wxCommandEvent& event)
{
        wxCommandEvent localEvent = event;
        wxString myStr = event.GetString();

        ....
}

In this case, GetString() will return the text the user entered.

Now, the XRC view used (basicdlg.xrc)

In the interest of full disclosure, I'm including the XRC I used here:


<?xml version="1.0" encoding="ISO-8859-1"?>

<resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1">

<object class="wxFrame" name="main_frame">
    <title>wxEcho Client</title>
    <centered>1</centered>
    <object class="wxFlexGridSizer">
        <cols>1</cols>
        <rows>0</rows>
        <vgap>0</vgap>
        <hgap>0</hgap>
        <growablecols>0</growablecols>
        <growablerows>0</growablerows>
        <object class="sizeritem">
            <flag>wxGROW|wxALIGN_CENTER_VERTICAL|wxALL</flag>
            <border>5</border>
            <object class="wxTextCtrl" name="output_textctrl">
                                <size>500,260</size>
                                <style>wxTE_MULTILINE|wxTE_READONLY</style>
                                <value>Welcome to the example!</value>
                        </object>               
        </object>
        <object class="sizeritem">
            <flag>wxGROW|wxALIGN_LEFT|wxALL</flag>
            <border>5</border>
                        <orient>wxHORIZONTAL</orient>
                        <object class="wxTextCtrl" name="user_input_textctrl">
                                <size>500,24</size>
                                <style>wxTE_PROCESS_ENTER|wxTE_RIGHT</style>
                                <value>Your Command, melord</value>
                        </object>
        </object>
    </object>
</object>

</resource>

If you're writing an XRC by hand, make sure to keep it as simple as possible. It looks like one sizer an object tends to be the rule.

Two things to notice about this XRC:

#1: wxTE_MULTILINE and wxTE_READONLY. This makes the first text edit control a multi-line, read only object - the user can't type into it.

#2: wxTE_PROCESS_ENTER. This text edit field will send a wxCommandEvent when the user presses return in it.

More interesting things to note about XRCs in general:

Style Flags


  <object class="wxTextCtrl" name="output_textctrl">
                                <size>500,260</size>
                                <style>wxTE_MULTILINE|wxTE_READONLY</style>
    
    .....

  </object>

Most of these flags are self-explanatory - except the style flags.

Objects in wxWindows usually have a style parameter in their constructor. This style parameter defines characteristics of the control (is it read only, does it have a close box, etc). The flags an object accepts can be found in the documentation for that class.

Items specified in the <style> parameter are passed into the style parameter of the constructor for that object.

The Header (xrcDemo.h)

David Hart mentioned that I didn't have declarations for MyFrame and MyApp. Here they are:

//xrcDemo.h

#include "wx/wx.h"

class MyApp: public wxApp
{
        virtual bool OnInit();
};


// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
     // ctor(s)
     MyFrame(wxWindow* parent=(NULL));
     
     // event handlers (these functions should _not_ be virtual)

                //file menu
        void OnQuit(wxCommandEvent& event);
        void OnUserInput(wxCommandEvent& event);
        
private:
     // any class wishing to process wxWindows events must use this macro
     DECLARE_EVENT_TABLE()
};

Conclusion

That code, its comments, and the future explanations of those key routines should help you to build apps with wxWindows and XRC.

Check back here for updates, or further articles.

Questions? Comments?

You can comment on this article by registering for this site, and leaving a comment on this page, or you can send an email.

We also provide wxWindows consulting services, specializing in the wxMac side of things. Contact us for further info!

Comments

wxGetCwd() will not always return the path to the applications. When the application is executed from a shell (Unix) or command prompt (Win2K), then the path to the application may be explicitly specified and it may not be the current directory. For example:

cd temp MyAppMyApp.exe

In this case, wxGetCwd() will return temp, but the application is in MyApp.

Another way to get the path to the application is from argv0. This is more reliable than wxGetCwd(). -- snappy at 11:53:37 07/24/03


It would be good to have an explanation of how to attach one's own derived control to a control defined in XRC using AttachUnknownControl(). In particular, it is tricky to get the unknown control to size correctly. The following web page in wxPyWiki has some details under the sections 6.0.2, 7.0 and 8.0.

http://wiki.wxpython.org/index.cgi/UsingXmlResources?action=highlight&value=xrc -- snappy at 11:54:41 07/24/03


There really needs to get a GetPath() wxFunction.

Thanks for the wxGetCwd() info though!

Perhaps at some point I'll make an Advanced XRC tutorial page.. that's an idea.

Thanks for the feedback -- Ryan Wilcox at 12:51:07 07/24/03

Using_XRC (last edited 2006-10-13 15:32:37 by RyanWilcox)