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