Resource Files

From WxWiki
(Redirected from XRC)
Jump to navigation Jump to search

XRC Overview

See the XML-based resource system overview of the online docs.

You can find many XRC dialog editors on the Tools page.

XRC format specification is included in the manual.

XRC Sourcecode Example

See the Using XML Resources with XRC guide for much more in-depth overview.

Instead of using a GUI, it's probably easier to learn by hand and see how XRC works. There is a step-by-step walkthrough of XRC files and related sample C++ sourcecode in "samples/xrc". The sample is an executable program with 8 dialogs, each dialog loaded from XRC. Each dialog shows a demo of some aspect of XRC, with a text box at the top of the dialog describing step-by-step how to use that feature. The first 4 demo topics are basic topics:

  • A non-derived dialog, as typically used on "About" dialogs.
  • A derived dialog, showing how to hook up custom methods and events to controls on your XRC dialog.
  • A "controls" demo in XRC: a notebook with one control per tab, previewing how each control looks and is used under XRC.
  • A non-centered dialog, demonstrating the "centered" parameter.

The next 4 are advanced topics:

  • Embedding a custom class into an XRC dialog, by using the "unknown" class.
  • Using wxArtProvider to use stock icons from within your XRC resources (new feature in XRC wx2.3.3).
  • Using the platform attribute to selectively show a part of XRC based on the current OS (now works as expected as of wx2.3.3).
  • Runtime variable expansion (demo only. Not implemented at this time).

The application also shows how to make your main wxFrame using XRC, as well as use XRC for the menu and toolbar. All classes are split into 1 class per .cpp/.h/.xrc file. This makes it both easier for a newcomer to follow the code, and also easier to use this as a starting point for a new wxWindow project.

Supported Objects and Properties

Supported objects and their properties are documented in XRC format specification.

Alternatively, running the following command in contrib/src/xrc should reveal all of them:

grep IsOfClass xh_*.cpp

Converting Old RC and WXR Resource Files to XRC

The new xrc format is primarily designed for use with sizers (see WxSizer), however it also supports absolute positioning which is currently the only format to which you can convert old resource files.

To convert you can use convertrc which can be found in contrib/utils/convertrc. You will have to build the xrc library first which can be found in contrib/src/xrc.

In the long run it is best to redesign using the sizer based layout, which is much more portable.

Gotchas

Custom controls with OnSize() events

If you are embedding a custom control that has an OnSize event(), be aware that an OnSize() event will be called when the custom control is embedded into its "unknown" XRC placeholder node. For example if you have a listctrl that resizes its 4 columns on an OnSize() event, take into account that an OnSize() will be called upon a grafting, so either insert the 4 columns in the constructor, or rewrite the OnSize() somehow to not execute until after it has been inserted.

choosing xml-id's

The IDs ("name" properties, not "id"!) are free-form, you can use anything you wish.

Some Need to know about this (IMHO) : You have the choice, for button to use (for example) wxID_OK, to get things working "automatically". But if you want this to work, you should NEVER use XRCID() against this name. If you do so, the event ID will not be the same, and this will kill the mechanism. This is true even if you use an XRCID for one dialog box, and in another you want the old behavior ... Sounds logical when Thinking, but take time to think :).

Using derived classes

Note for version >= 2.4.2 (from another contributor) The Constructor of your class is not necessarily matching the signature (at least with default statement). so instead of

     MyPanel(wxWindow * parent, wxWindowID id = XRCID("ID_CustomPanel"),
             const wxPoint & pos = wxDefaultPosition,
             const wxSize & size = wxDefaultSize,
             long style = wxTAB_TRAVERSAL)

you can use :

   MyPanel(wxWindow * parent)

at least it seems working here after these little thing ... More in a few moment

It seems that you need to be carefull with the subclassing system. In my case I get some little surprise, when trying to destroy the main Frame, the application was not living. That was caused by subclassing a not directly derived class from wx (in my case I'm not deriving from a wxFrame, but from a CCustomFrame which derive from a wxFrame ...). I guess that doing a little with the SetTopWindow and SetOnExitWindow functions could give a litle help, But I don't get the time to look at it.

[First contributor]

I had a bit of a headache over this, and I don't think it's explained in the docs. What we're talking about here is not the class="unknown" feature of XRC. Using this you can add a completely unrelated class to your XRC file, and sorta "slip it in" during construction. I had a case, where I wanted to use a custom wxPanel class for some notebook pages, since they needed to maintain some information about the controls they contained. My first thought was using the Foreign Control as described in the overview, but XRC has a different solution.

This all assumes that you want to use a subclass of an existing wxClass, e.g. wxPanel, wxTextCtrl, or some other class. You will probably want to use all the base class features, but add a few features such as extended information about child controls or specialized event handling.

To do this you derive your class from the class you want to re-implement, and declare it to be a dynamic class. This is very important! In your implementation file you implement the class as normal; remember to implement it as a dynamic class. You MUST match the signature of the base class' constructors exactly and include a Create() function that matches the signature of the base class Create(). Here is an example based on the above mentioned situation.

[Note : This is silly, but be carefull that this is not the actual signature of wxPanel (at least 2.4.3 and later) (Caeies)]

In mypanel.h

 class MyPanel : public wxPanel
 {
   public:
     MyPanel(){}
     /*! The constructor must match the signature from wxPanel. */
     MyPanel(wxWindow * parent, wxWindowID id = XRCID("ID_CustomPanel"),
             const wxPoint & pos = wxDefaultPosition,
             const wxSize & size = wxDefaultSize,
             long style = wxTAB_TRAVERSAL)
       {
          Create(parent, id, pos, size, style);
       }
     
     /*! The Create() function must also match the base class' signature. */
     bool Create(wxWindow * parent, wxWindowID id = XRCID("ID_CustomPanel"),
                 const wxPoint & pos = wxDefaultPosition,
                 const wxSize & size = wxDefaultSize,
                 long style = wxTAB_TRAVERSAL);
   
   private:
     /*! This part is ESSENTIAL! */
     DECLARE_DYNAMIC_CLASS(MyPanel)
     DECLARE_EVENT_TABLE()
 };

In MyPanel.cc

 IMPLEMENT_DYNAMIC_CLASS(MyPanel, wxPanel)
 
 BEGIN_EVENT_TABLE(MyPanel, wxPanel)
 // Add event tables here
 END_EVENT_TABLE()
 
 bool MyPanel::Create(wxWindow * parent, wxWindowID id,
                 const wxPoint & pos,
                 const wxSize & size,
                 long style)
 {
   SetParent(parent);
   wxXmlResource::Get()->LoadPanel(this, GetParent(), "ID_CustomPanel");
   GetSizer()->Fit(this);
   
   return true;
 }

Now you can include your new panel in your XRC files as:

 <object class="wxPanel" name="ID_CustomPanel" subclass="MyPanel">
 </object>

Note the "subclass" attribute of the object. The class is still a wxPanel, but now the XRC system knows it has to search for a derived class called MyPanel and instantiate that instead.

[Note : Perhaps this is clear for a C++ expert but I lost a lot of time on this so I'd like to add it here: If you did everything right and nevertheless the library won't find your subclass make sure that the linker didn't discard it. I subclassed wxPanel but didn't create any instances of it by myself. So the linker didn't include my subclass into the application and I got a lot of error messages when loading the frame (megli)]

[Note : It looks like that it's enough to have only default constructor and no 'Create' method. Custom control's full constructor and Create method is never called when creating object from XRC by wxWidgets 2.6.3. Don't know was it changed in later versions (milyin)]

setup.h

Do not be confused by the mentions of wxUSE_PROLOGIO and wxUSE_WX_RESOURCES in setup.h, these refer to the old resource system. The new resource system can be found in the contrib directory, as described above.

How to hide part of a dialog using XRC

You'll need a function like:

 wxWindow *myDlg::ShowHideChild(const wxString &name)
 {
   wxWindow *p = FindWindowByName(name, this);
   if (!p) return NULL;
   
   // now, modify the sizer which contain that window
   wxSizer *sizer = p->GetContainingSizer();
   wxSizerItem *item = sizer->GetItem(p);
   if (!item) return NULL;
   
   // invert the show state
   if (p->IsShown())
     item->Show(FALSE);
   else
     item->Show(TRUE);
   return p;
 }

and then an event handler for the button you use to show/hide the panel:

 void myDlg::OnShowHideAdv(wxCommandEvent &)
 {
   wxWindow *p = ShowHideChild(wxT("ID_WINDOW_TO_HIDE"));
   wxASSERT_MSG(p, wxT("Invalid XRC file !"));
   
   // if you are hiding a control which is contained using a <unknown> tag, then you must
   // hide also the container of that item which is automatically generated by XRC and has
   // the "_container" suffix added at the end of the ID name:
   p = ShowHideChild(wxT("ID_WINDOW_TO_HIDE_container"));
   wxASSERT_MSG(p, wxT("Invalid XRC file !"));
   
   // resize this dialog to reflect the change
   GetSizer()->Layout();
   GetSizer()->SetSizeHints(this);
   this->SetSize(-1, -1, 0, 0);
   this->CenterOnScreen();
   
   if (!p->IsShown())
     m_myShowHideBtn->SetLabel(wxT("Show >>"));
   else
     m_myShowHideBtn->SetLabel(wxT("Hide <<"));
 }

also note that your <unknown> tag must be a child of the *first* sizer of the dialog if you want to get the things working correctly.