]>
Commit | Line | Data |
---|---|---|
15b6757b | 1 | ///////////////////////////////////////////////////////////////////////////// |
e244be15 | 2 | // Name: xrc.h |
15b6757b FM |
3 | // Purpose: topic overview |
4 | // Author: wxWidgets team | |
5 | // RCS-ID: $Id$ | |
526954c5 | 6 | // Licence: wxWindows licence |
15b6757b FM |
7 | ///////////////////////////////////////////////////////////////////////////// |
8 | ||
880efa2a | 9 | /** |
36c9828f | 10 | |
032e27aa | 11 | @page overview_xrc XML Based Resource System (XRC) |
3863c5eb | 12 | |
831e1028 | 13 | @tableofcontents |
3863c5eb BP |
14 | |
15 | The XML-based resource system, known as XRC, allows user interface elements | |
16 | such as dialogs, menu bars and toolbars, to be stored in text files and loaded | |
17 | into the application at run-time. XRC files can also be compiled into binary | |
18 | XRS files or C++ code (the former makes it possible to store all resources in a | |
19 | single file and the latter is useful when you want to embed the resources into | |
20 | the executable). | |
21 | ||
22 | There are several advantages to using XRC resources: | |
23 | ||
24 | @li Recompiling and linking an application is not necessary if the resources | |
25 | change. | |
26 | @li If you use a dialog designer that generates C++ code, it can be hard to | |
27 | reintegrate this into existing C++ code. Separation of resources and code | |
28 | is a more elegant solution. | |
29 | @li You can choose between different alternative resource files at run time, if | |
30 | necessary. | |
31 | @li The XRC format uses sizers for flexibility, allowing dialogs to be | |
32 | resizable and highly portable. | |
33 | @li The XRC format is a wxWidgets standard, and can be generated or | |
d9384bfb | 34 | postprocessed by any program that understands it. As it is based on the XML |
3863c5eb BP |
35 | standard, existing XML editors can be used for simple editing purposes. |
36 | ||
37 | XRC was written by Vaclav Slavik. | |
38 | ||
831e1028 | 39 | @see wxXmlResource, wxXmlResourceHandler, @ref overview_xrcformat |
41e69d79 | 40 | |
3863c5eb | 41 | |
3863c5eb | 42 | |
5fcef184 | 43 | @section overview_xrc_gettingstarted Getting Started with XRC |
3863c5eb | 44 | |
5fcef184 | 45 | <b> Creating an XRC file </b> |
3863c5eb | 46 | |
5fcef184 VZ |
47 | You will need to write an XRC file. Though this @e can be done by hand in a |
48 | text editor, for all but the smallest files it is advisable to use a | |
49 | specialised tool. Examples of these include: | |
3863c5eb | 50 | |
5fcef184 VZ |
51 | @e Non-free: |
52 | @li wxDesigner <http://www.wxdesigner-software.de/>, a commercial dialog | |
53 | designer/RAD tool. | |
54 | @li DialogBlocks <http://www.anthemion.co.uk/dialogblocks/>, a commercial | |
3863c5eb | 55 | dialog editor. |
5fcef184 VZ |
56 | |
57 | @e Free: | |
58 | @li XRCed <http://xrced.sf.net/>, a wxPython-based dialog editor that you | |
3863c5eb | 59 | can find in the wxPython/tools subdirectory of the wxWidgets SVN archive. |
5fcef184 VZ |
60 | @li wxFormBuilder <http://wxformbuilder.org/>, a C++-based dialog editor that |
61 | can output C++, XRC or python. | |
62 | ||
63 | There's a more complete list at <http://www.wxwidgets.org/wiki/index.php/Tools> | |
64 | ||
65 | This small demonstration XRC file contains a simple dialog: | |
66 | @code | |
67 | <?xml version="1.0" ?> | |
68 | <resource version="2.3.0.1"> | |
69 | <object class="wxDialog" name="SimpleDialog"> | |
70 | <title>Simple dialog</title> | |
71 | <object class="wxBoxSizer"> | |
72 | <orient>wxVERTICAL</orient> | |
73 | <object class="sizeritem"> | |
74 | <object class="wxTextCtrl" name="text"/> | |
75 | <option>1</option> | |
76 | <flag>wxALL|wxEXPAND</flag> | |
77 | <border>10</border> | |
78 | </object> | |
79 | <object class="sizeritem"> | |
80 | <object class="wxBoxSizer"> | |
81 | <object class="sizeritem"> | |
82 | <object class="wxButton" name="clickme_btn"> | |
83 | <label>Click</label> | |
84 | </object> | |
85 | <flag>wxRIGHT</flag> | |
86 | <border>10</border> | |
87 | </object> | |
88 | <object class="sizeritem"> | |
89 | <object class="wxButton" name="wxID_OK"> | |
90 | <label>OK</label> | |
91 | </object> | |
92 | <flag>wxLEFT</flag> | |
93 | <border>10</border> | |
94 | </object> | |
95 | <orient>wxHORIZONTAL</orient> | |
96 | </object> | |
97 | <flag>wxALL|wxALIGN_CENTRE</flag> | |
98 | <border>10</border> | |
99 | </object> | |
100 | </object> | |
101 | </object> | |
102 | </resource> | |
103 | @endcode | |
104 | ||
105 | You can keep all your XRC elements together in one file, or split them between | |
106 | several. | |
107 | ||
108 | <b> Loading XRC files </b> | |
109 | ||
110 | Before you can use XRC in an app, it must first be loaded. This code fragment | |
111 | shows how to load a single XRC file "resource.xrc" from the current working | |
112 | directory, plus all the *.xrc files contained in the subdirectory "rc". | |
113 | ||
114 | @code | |
115 | #include "wx/xrc/xmlres.h" | |
116 | ||
117 | bool MyApp::OnInit() | |
118 | { | |
119 | ... | |
120 | wxXmlResource::Get()->InitAllHandlers(); | |
121 | ||
122 | wxXmlResource::Get()->Load("resource.xrc"); | |
123 | wxXmlResource::Get()->LoadAllFiles("rc"); | |
124 | ... | |
125 | } | |
126 | @endcode | |
127 | ||
128 | It's normal to load any XRC files at the beginning of an app. Though it is | |
129 | possible to unload a file later, it's seldom necessary. | |
130 | ||
131 | ||
132 | <b> Using an XRC item </b> | |
133 | ||
134 | The XRC file(s) are now loaded into the app's virtual filesystem. From there, | |
135 | you must do another sort of load when you want to use an individual object. | |
136 | Yes, it's confusingly named, but you first Load() the file, and later load each | |
137 | top-level object when its needed. | |
3863c5eb | 138 | |
5fcef184 | 139 | This is how you would use the above simple dialog in your code. |
3863c5eb | 140 | |
5fcef184 VZ |
141 | @code |
142 | void MyClass::ShowDialog() | |
143 | { | |
144 | wxDialog dlg; | |
145 | if (wxXmlResource::Get()->LoadDialog(&dlg, NULL, "SimpleDialog")) | |
146 | dlg.ShowModal(); | |
147 | } | |
148 | @endcode | |
149 | ||
150 | See how simple the code is. All the instantiation is done invisibly by the XRC | |
151 | system. | |
152 | ||
153 | Though you'll most often use wxXmlResource::LoadDialog, there are also | |
154 | equivalents that load a frame, a menu etc; and the generic | |
155 | wxXmlResource::LoadObject. See wxXmlResource for more details. | |
3863c5eb | 156 | |
5fcef184 | 157 | <b> Accessing XRC child controls </b> |
3863c5eb | 158 | |
5fcef184 VZ |
159 | The last section showed how to load top-level windows like dialogs, but what |
160 | about child windows like the wxTextCtrl named "text" that the dialog contains? | |
161 | You can't 'load' an individual child control in the same way. Instead you use | |
162 | the XRCCTRL macro to get a pointer to the child. To expand the previous code: | |
3863c5eb | 163 | |
5fcef184 VZ |
164 | @code |
165 | void MyClass::ShowDialog() | |
166 | { | |
167 | wxDialog dlg; | |
168 | if (!wxXmlResource::Get()->LoadDialog(&dlg, NULL, "SimpleDialog")) | |
169 | return; | |
170 | ||
171 | wxTextCtrl* pText = XRCCTRL(dlg, "text", wxTextCtrl); | |
172 | if (pText) | |
173 | pText->ChangeValue("This is a simple dialog"); | |
174 | ||
175 | dlg.ShowModal(); | |
176 | } | |
177 | @endcode | |
178 | ||
179 | XRCCTRL takes a reference to the parent container and uses wxWindow::FindWindow | |
180 | to search inside it for a wxWindow with the supplied name (here "text"). It | |
181 | returns a pointer to that control, cast to the type in the third parameter; so | |
182 | a similar effect could be obtained by writing: | |
183 | ||
184 | @code | |
185 | pText = (wxTextCtrl*)(dlg.FindWindowByName("text")); | |
186 | @endcode | |
187 | ||
188 | <b> XRC and IDs </b> | |
189 | ||
190 | The ID of a control is often needed, e.g. for use in an event table | |
191 | or with wxEvtHandler::Bind. It can easily be found by passing the name of the | |
192 | control to the XRCID macro: | |
193 | ||
194 | @code | |
195 | void MyClass::ShowDialog() | |
196 | { | |
197 | wxDialog dlg; | |
198 | if (!wxXmlResource::Get()->LoadDialog(&dlg, NULL, "SimpleDialog")) | |
199 | return; | |
3863c5eb | 200 | |
5fcef184 VZ |
201 | XRCCTRL(dlg, "text", wxTextCtrl)->Bind(wxEVT_COMMAND_TEXT_UPDATED, |
202 | wxTextEventHandler(MyClass::OnTextEntered), this, XRCID("text")); | |
203 | ||
204 | XRCCTRL(dlg, "clickme_btn", wxButton)->Bind(wxEVT_COMMAND_BUTTON_CLICKED, | |
205 | wxCommandEventHandler(MyClass::OnClickme), this, XRCID("clickme_btn")); | |
206 | ||
207 | dlg.ShowModal(); | |
208 | } | |
209 | @endcode | |
210 | ||
211 | A few points to note: | |
212 | @li The value of the int returned by XRCID("foo") is guaranteed to be unique | |
213 | within an app. | |
214 | @li However that value isn't predictable, and you shouldn't rely on it being | |
215 | consistent between runs. It certainly won't be the same in different apps. | |
216 | @li @ref page_stockitems such as wxID_OK work correctly without requiring XRCID | |
217 | (because, internally, XRCID("wxID_OK") is mapped to wxID_OK). | |
218 | @li Both XRCID and XRCCTRL use the 'name' of the control (as in | |
219 | wxWindow::GetName). This is different from the label that the user sees on | |
220 | e.g. a wxButton. | |
221 | ||
222 | <b> Subclassing in XRC </b> | |
223 | ||
224 | You will often want to use subclassed wx controls in your code. There are three | |
225 | ways to do this from XRC: | |
226 | @li Very rarely you might need to | |
227 | @ref overview_xrcformat_extending_custom "create your own wxXmlResourceHandler" | |
228 | @li Occasionally wxXmlResource::AttachUnknownControl may be best. See | |
229 | @ref overview_xrcformat_extending_unknown | |
230 | @li Usually though, the simple 'subclass' keyword will suffice. | |
231 | ||
232 | Suppose you wanted the wxTextCtrl named "text" to be created as your derived | |
233 | class MyTextCtrl. The only change needed in the XRC file would be in this line: | |
234 | ||
235 | @code | |
236 | <object class="wxTextCtrl" name="text" subclass="MyTextCtrl"/> | |
237 | @endcode | |
238 | ||
239 | The only change in your code would be to use MyTextCtrl in XRCCTRL. However for | |
240 | the subclass to be created successfully, it's important to ensure that it uses | |
241 | wxWidget's RTTI mechanism: see @ref overview_xrcformat_extending_subclass for | |
242 | the details. | |
243 | ||
244 | ||
245 | ||
246 | @section overview_xrc_xrcsample The XRC sample | |
247 | ||
248 | A major resource for learning how to use XRC is the @sample{xrc}. This | |
249 | demonstrates all of the standard uses of XRC, and some of the less common ones. | |
250 | It is strongly suggested that you run it, and look at the well-commented | |
251 | source code to see how it works. | |
252 | ||
253 | ||
254 | @section overview_xrc_binaryresourcefiles Binary Resource Files | |
3863c5eb BP |
255 | |
256 | To compile binary resource files, use the command-line @c wxrc utility. It | |
257 | takes one or more file parameters (the input XRC files) and the following | |
258 | switches and options: | |
259 | ||
260 | @li -h (--help): Show a help message. | |
261 | @li -v (--verbose): Show verbose logging information. | |
262 | @li -c (--cpp-code): Write C++ source rather than a XRS file. | |
263 | @li -e (--extra-cpp-code): If used together with -c, generates C++ header file | |
264 | containing class definitions for the windows defined by the XRC file (see | |
265 | special subsection). | |
266 | @li -u (--uncompressed): Do not compress XML files (C++ only). | |
267 | @li -g (--gettext): Output underscore-wrapped strings that poEdit or gettext | |
268 | can scan. Outputs to stdout, or a file if -o is used. | |
269 | @li -n (--function) @<name@>: Specify C++ function name (use with -c). | |
270 | @li -o (--output) @<filename@>: Specify the output file, such as resource.xrs | |
271 | or resource.cpp. | |
272 | @li -l (--list-of-handlers) @<filename@>: Output a list of necessary handlers | |
273 | to this file. | |
274 | ||
275 | For example: | |
276 | ||
277 | @code | |
278 | $ wxrc resource.xrc | |
279 | $ wxrc resource.xrc -o resource.xrs | |
280 | $ wxrc resource.xrc -v -c -o resource.cpp | |
281 | @endcode | |
282 | ||
283 | @note XRS file is essentially a renamed ZIP archive which means that you can | |
284 | manipulate it with standard ZIP tools. Note that if you are using XRS files, | |
285 | you have to initialize the wxFileSystem archive handler first! It is a simple | |
286 | thing to do: | |
287 | ||
288 | @code | |
289 | #include <wx/filesys.h> | |
290 | #include <wx/fs_arc.h> | |
291 | ... | |
292 | wxFileSystem::AddHandler(new wxArchiveFSHandler); | |
293 | @endcode | |
294 | ||
295 | ||
296 | @section overview_xrc_embeddedresource Using Embedded Resources | |
297 | ||
298 | It is sometimes useful to embed resources in the executable itself instead of | |
299 | loading an external file (e.g. when your app is small and consists only of one | |
300 | exe file). XRC provides means to convert resources into regular C++ file that | |
301 | can be compiled and included in the executable. | |
302 | ||
303 | Use the @c -c switch to @c wxrc utility to produce C++ file with embedded | |
304 | resources. This file will contain a function called @c InitXmlResource (unless | |
305 | you override this with a command line switch). Use it to load the resource: | |
306 | ||
307 | @code | |
308 | extern void InitXmlResource(); // defined in generated file | |
309 | ... | |
310 | wxXmlResource::Get()->InitAllHandlers(); | |
311 | InitXmlResource(); | |
312 | ... | |
313 | @endcode | |
314 | ||
315 | ||
3863c5eb BP |
316 | @section overview_xrc_cppheader C++ header file generation |
317 | ||
318 | Using the @c -e switch together with @c -c, a C++ header file is written | |
319 | containing class definitions for the GUI windows defined in the XRC file. This | |
320 | code generation can make it easier to use XRC and automate program development. | |
321 | The classes can be used as basis for development, freeing the programmer from | |
322 | dealing with most of the XRC specifics (e.g. @c XRCCTRL). | |
323 | ||
324 | For each top level window defined in the XRC file a C++ class definition is | |
325 | generated, containing as class members the named widgets of the window. A | |
326 | default constructor for each class is also generated. Inside the constructor | |
327 | all XRC loading is done and all class members representing widgets are | |
328 | initialized. | |
329 | ||
330 | A simple example will help understand how the scheme works. Suppose you have a | |
331 | XRC file defining a top level window @c TestWnd_Base, which subclasses wxFrame | |
3c4f71cc | 332 | (any other class like @c wxDialog will do also), and has subwidgets wxTextCtrl A |
3863c5eb BP |
333 | and wxButton B. |
334 | ||
335 | The XRC file and corresponding class definition in the header file will be | |
336 | something like: | |
337 | ||
338 | @code | |
339 | <?xml version="1.0"?> | |
340 | <resource version="2.3.0.1"> | |
341 | <object class="wxFrame" name="TestWnd_Base"> | |
342 | <size>-1,-1</size> | |
343 | <title>Test</title> | |
344 | <object class="wxBoxSizer"> | |
345 | <orient>wxHORIZONTAL</orient> | |
346 | <object class="sizeritem"> | |
347 | <object class="wxTextCtrl" name="A"> | |
348 | <label>Test label</label> | |
349 | </object> | |
350 | </object> | |
351 | <object class="sizeritem"> | |
352 | <object class="wxButton" name="B"> | |
353 | <label>Test button</label> | |
354 | </object> | |
355 | </object> | |
356 | </object> | |
357 | </object> | |
358 | </resource> | |
359 | ||
360 | ||
361 | class TestWnd_Base : public wxFrame | |
362 | { | |
363 | protected: | |
364 | wxTextCtrl* A; | |
365 | wxButton* B; | |
366 | ||
367 | private: | |
368 | void InitWidgetsFromXRC() | |
369 | { | |
370 | wxXmlResource::Get()->LoadObject(this, NULL, "TestWnd", "wxFrame"); | |
371 | A = XRCCTRL(*this, "A", wxTextCtrl); | |
372 | B = XRCCTRL(*this, "B", wxButton); | |
373 | } | |
374 | public: | |
375 | TestWnd::TestWnd() | |
376 | { | |
377 | InitWidgetsFromXRC(); | |
378 | } | |
379 | }; | |
380 | @endcode | |
381 | ||
382 | The generated window class can be used as basis for the full window class. The | |
383 | class members which represent widgets may be accessed by name instead of using | |
384 | @c XRCCTRL every time you wish to reference them (note that they are | |
385 | @c protected class members), though you must still use @c XRCID to refer to | |
386 | widget IDs in the event table. | |
387 | ||
388 | Example: | |
389 | ||
390 | @code | |
391 | #include "resource.h" | |
392 | ||
393 | class TestWnd : public TestWnd_Base | |
394 | { | |
395 | public: | |
396 | TestWnd() | |
397 | { | |
398 | // A, B already initialised at this point | |
399 | A->SetValue("Updated in TestWnd::TestWnd"); | |
400 | B->SetValue("Nice :)"); | |
401 | } | |
402 | void OnBPressed(wxEvent& event) | |
403 | { | |
404 | Close(); | |
405 | } | |
406 | DECLARE_EVENT_TABLE(); | |
407 | }; | |
408 | ||
409 | BEGIN_EVENT_TABLE(TestWnd,TestWnd_Base) | |
410 | EVT_BUTTON(XRCID("B"), TestWnd::OnBPressed) | |
411 | END_EVENT_TABLE() | |
412 | @endcode | |
413 | ||
414 | It is also possible to access the wxSizerItem of a sizer that is part of a | |
415 | resource. This can be done using @c XRCSIZERITEM as shown. | |
416 | ||
417 | The resource file can have something like this for a sizer item. | |
418 | ||
419 | @code | |
420 | <object class="spacer" name="area"> | |
421 | <size>400, 300</size> | |
422 | </object> | |
423 | @endcode | |
424 | ||
425 | The code can then access the sizer item by using @c XRCSIZERITEM and @c XRCID | |
426 | together. | |
427 | ||
428 | @code | |
429 | wxSizerItem* item = XRCSIZERITEM(*this, "area"); | |
430 | @endcode | |
431 | ||
432 | ||
433 | @section overview_xrc_newresourcehandlers Adding New Resource Handlers | |
434 | ||
435 | Adding a new resource handler is pretty easy. | |
436 | ||
437 | Typically, to add an handler for the @c MyControl class, you'll want to create | |
438 | the @c xh_mycontrol.h and @c xh_mycontrol.cpp files. | |
439 | ||
440 | The header needs to contains the @c MyControlXmlHandler class definition: | |
441 | ||
442 | @code | |
443 | class MyControlXmlHandler : public wxXmlResourceHandler | |
444 | { | |
445 | public: | |
446 | // Constructor. | |
447 | MyControlXmlHandler(); | |
448 | ||
449 | // Creates the control and returns a pointer to it. | |
450 | virtual wxObject *DoCreateResource(); | |
451 | ||
452 | // Returns true if we know how to create a control for the given node. | |
453 | virtual bool CanHandle(wxXmlNode *node); | |
454 | ||
455 | // Register with wxWidgets' dynamic class subsystem. | |
456 | DECLARE_DYNAMIC_CLASS(MyControlXmlHandler) | |
457 | }; | |
458 | @endcode | |
459 | ||
460 | The implementation of your custom XML handler will typically look as: | |
461 | ||
462 | @code | |
463 | // Register with wxWidgets' dynamic class subsystem. | |
464 | IMPLEMENT_DYNAMIC_CLASS(MyControlXmlHandler, wxXmlResourceHandler) | |
465 | ||
466 | MyControlXmlHandler::MyControlXmlHandler() | |
467 | { | |
468 | // this call adds support for all wxWindows class styles | |
469 | // (e.g. wxBORDER_SIMPLE, wxBORDER_SUNKEN, wxWS_EX_* etc etc) | |
470 | AddWindowStyles(); | |
471 | ||
472 | // if MyControl class supports e.g. MYCONTROL_DEFAULT_STYLE | |
473 | // you should use: | |
474 | // XRC_ADD_STYLE(MYCONTROL_DEFAULT_STYLE); | |
475 | } | |
476 | ||
477 | wxObject *MyControlXmlHandler::DoCreateResource() | |
478 | { | |
479 | // the following macro will init a pointer named "control" | |
480 | // with a new instance of the MyControl class, but will NOT | |
481 | // Create() it! | |
482 | XRC_MAKE_INSTANCE(control, MyControl) | |
483 | ||
484 | // this is the point where you'll typically need to do the most | |
485 | // important changes: here the control is created and initialized. | |
486 | // You'll want to use the wxXmlResourceHandler's getters to | |
487 | // do most of your work. | |
488 | // If e.g. the MyControl::Create function looks like: | |
489 | // | |
3c4f71cc | 490 | // bool MyControl::Create(wxWindow *parent, int id, |
3863c5eb BP |
491 | // const wxBitmap &first, const wxPoint &posFirst, |
492 | // const wxBitmap &second, const wxPoint &posSecond, | |
493 | // const wxString &theTitle, const wxFont &titleFont, | |
494 | // const wxPoint &pos, const wxSize &size, | |
495 | // long style = MYCONTROL_DEFAULT_STYLE, | |
496 | // const wxString &name = wxT("MyControl")); | |
497 | // | |
498 | // Then the XRC for your component should look like: | |
499 | // | |
500 | // <object class="MyControl" name="some_name"> | |
501 | // <first-bitmap>first.xpm</first-bitmap> | |
502 | // <second-bitmap>text.xpm</second-bitmap> | |
503 | // <first-pos>3,3</first-pos> | |
504 | // <second-pos>4,4</second-pos> | |
505 | // <the-title>a title</the-title> | |
506 | // <title-font> | |
507 | // <!-- Standard XRC tags for a font: <size>, <style>, <weight>, etc --> | |
508 | // </title-font> | |
509 | // <!-- XRC also accepts other usual tags for wxWindow-derived classes: | |
510 | // like e.g. <name>, <style>, <size>, <position>, etc --> | |
511 | // </object> | |
512 | // | |
513 | // And the code to read your custom tags from the XRC file is just: | |
514 | control->Create(m_parentAsWindow, GetID(), | |
515 | GetBitmap(wxT("first-bitmap")), | |
516 | GetPosition(wxT("first-pos")), | |
517 | GetBitmap(wxT("second-bitmap")), | |
518 | GetPosition(wxT("second-pos")), | |
519 | GetText(wxT("the-title")), | |
520 | GetFont(wxT("title-font")), | |
521 | GetPosition(), GetSize(), GetStyle(), GetName()); | |
522 | ||
523 | SetupWindow(control); | |
524 | ||
525 | return control; | |
526 | } | |
527 | ||
528 | bool MyControlXmlHandler::CanHandle(wxXmlNode *node) | |
529 | { | |
530 | // this function tells XRC system that this handler can parse | |
531 | // the <object class="MyControl"> tags | |
532 | return IsOfClass(node, wxT("MyControl")); | |
533 | } | |
534 | @endcode | |
535 | ||
536 | You may want to check the wxXmlResourceHandler documentation to see how many | |
537 | built-in getters it contains. It's very easy to retrieve also complex | |
538 | structures out of XRC files using them. | |
36c9828f | 539 | |
e244be15 | 540 | */ |