The sample demostrates how to create and use a persistent hierarchical data structure. The serializer object (instance of the wxXmlSerializer class) holds data objects (instances of xsSerializable class ) reflecting a current structure of the tree contols' items. The serializer's content is stored in a configuration file so the tree control's content can be reconstructed from this information at the application's start up.
Screenshot of the application:
A content of the output XML file corresponding to the tree structure on the screenshot above:
<?xml version="1.0" encoding="utf-8"?> <tree owner="TreeSampleApp" version="1.0.0"> <object type="SerializableObject"> <property name="id" type="long">1</property> <property name="item_name" type="string">Item 1</property> <object type="SerializableObject"> <property name="id" type="long">2</property> <property name="item_name" type="string">Item 2</property> </object> <object type="SerializableObject"> <property name="id" type="long">3</property> <property name="item_name" type="string">Item 3</property> <object type="SerializableObject"> <property name="id" type="long">6</property> <property name="item_name" type="string">Item 6</property> </object> </object> </object> <object type="SerializableObject"> <property name="id" type="long">4</property> <property name="item_name" type="string">Item 4</property> <object type="SerializableObject"> <property name="id" type="long">5</property> <property name="item_name" type="string">Item 5</property> <object type="SerializableObject"> <property name="id" type="long">7</property> <property name="item_name" type="string">Item 7</property> </object> <object type="SerializableObject"> <property name="id" type="long">9</property> <property name="item_name" type="string">Item 9</property> </object> </object> <object type="SerializableObject"> <property name="id" type="long">8</property> <property name="item_name" type="string">Item 8</property> </object> </object> </tree>
The sample consists of two classes:
wxString
data type member m_sTreeItemName
.Relationships between classes in this sample:
Declaration and implementation of these classes (taken from source files TreeSample.h and TreeSample.cpp):
// SerializableObject class declaration ///////////////////////////////////////////// class SerializableObject : public xsSerializable { public: // RTTI and xsSerializable::Clone() function must be provided XS_DECLARE_CLONABLE_CLASS(SerializableObject); // constructor SerializableObject(); // copy onstructor needed by default implementation of xsSerializable::Clone() function SerializableObject(SerializableObject &obj); // destructor virtual ~SerializableObject(); // protected data members wxString m_sTreeItemName; private: // private data members static int m_nItemCounter; }; // SerializableObject class implementation ////////////////////////////////////////// XS_IMPLEMENT_CLONABLE_CLASS(SerializableObject, xsSerializable); SerializableObject::SerializableObject() { // initialize member data m_sTreeItemName.Printf(wxT("Item %d"), ++m_nItemCounter); // mark the data members which should be serialized XS_SERIALIZE(m_sTreeItemName, wxT("item_name")); } SerializableObject::SerializableObject(SerializableObject &obj) { // initialize member data m_sTreeItemName = obj.m_sTreeItemName; // mark the data members which should be serialized XS_SERIALIZE(m_sTreeItemName, wxT("item_name")); } SerializableObject::~SerializableObject() { }
// TreeSampleApp class declaration ////////////////////////////////////////////////// class TreeSampleApp : public wxApp { public: virtual bool OnInit(); virtual int OnExit(); // main serializer object wxXmlSerializer m_XmlIO; }; DECLARE_APP(TreeSampleApp); // TreeSampleApp class implementation /////////////////////////////////////////////// IMPLEMENT_APP(TreeSampleApp); bool TreeSampleApp::OnInit() { // load application settings if the configuration file exists, otherwise create new // settings class object with default values // initialize serializer m_XmlIO.SetSerializerOwner(wxT("TreeSampleApp")); m_XmlIO.SetSerializerRootName(wxT("tree")); m_XmlIO.SetSerializerVersion(wxT("1.0.0")); if( wxFileExists(wxT("data.xml")) ) { // load tree data from configuration file (data class objects will be created at // run-time from the information stored in the configuration file) m_XmlIO.DeserializeFromXml(wxT("data.xml")); } // create and show main application frame MainFrame *frame = new MainFrame(NULL); SetTopWindow(frame); frame->Show(); return true; } int TreeSampleApp::OnExit() { // serialize tree data to given configuration file m_XmlIO.SerializeToXml(wxT("data.xml")); return 0; }
The class MainFrame which is responsible for viewing a main application window has implemented the functions by which the tree structure can be viewed and the new objects can be added/removed to/from this tree structure.
Implementation of these functions:
MainFrame::MainFrame( wxWindow* parent ) : _MainFrame( parent ) { SetIcon(wxIcon(wx_xpm)); // initialize root node CreateTreeRoot(); // build the tree control content BuildTreeFromData(); } void MainFrame::CreateTreeRoot() { // initialize root node treeCtrl->AddRoot(wxT("Root")); // Every tree control's item holds user data container encapsulating an ID of the corespondent serializable // data object. Note that the root data object is created by the serializer and shouldn't be manipulated // direcly (but it is possible as well). treeCtrl->SetItemData(treeCtrl->GetRootItem(), new TreeData(wxGetApp().m_XmlIO.GetRootItem()->GetId())); } void MainFrame::BuildTreeFromData() { // create a content of the tree control in accordance to the structure of the data objects stored in // the serializer _BuildChildren(treeCtrl->GetRootItem()); treeCtrl->ExpandAll(); } void MainFrame::_BuildChildren(wxTreeItemId parent) { // get ID of a serializable object associated with relevant tree node long nId = ((TreeData*)treeCtrl->GetItemData(parent))->m_nDataId; // get pointer to serializable object xsSerializable *parentObject = wxGetApp().m_XmlIO.GetItem(nId); if(parentObject) { wxTreeItemId treeId; SerializableObject *pChild; // iterate through all the parent data object's items and create relevant tree control nodes SerializableList::compatibility_iterator node = parentObject->GetFirstChildNode(); while(node) { pChild = (SerializableObject*)node->GetData(); // create new tree control node treeId = treeCtrl->AppendItem(parent, pChild->m_sTreeItemName, -1, -1, new TreeData(pChild->GetId())); // create next tree level items recursively _BuildChildren(treeId); node = node->GetNext(); } } } void MainFrame::OnAddClick( wxCommandEvent& WXUNUSED(event) ) { // This function creates new tree node as a child of currently selected tree node. // Hierarchical structure of data objects stored in the serializer copies the tree control's // nodes structure. // get selected tree node wxTreeItemId treeNode = treeCtrl->GetSelection(); // create new serializable data object SerializableObject *data = new SerializableObject(); // add new data object to the serializer at a position given by the tree structure // (selected tree node is a parent) long parentId = ((TreeData*)treeCtrl->GetItemData(treeNode))->m_nDataId; wxGetApp().m_XmlIO.AddItem(parentId, data); // create new tree node containing an ID of new data object and set it visible treeCtrl->EnsureVisible(treeCtrl->AppendItem(treeNode, data->m_sTreeItemName, -1, -1, new TreeData(data->GetId()))); } void MainFrame::OnUpdateAdd( wxUpdateUIEvent& event ) { event.Enable(treeCtrl->GetSelection().IsOk()); } void MainFrame::OnRemoveClick( wxCommandEvent& event ) { wxUnusedVar(event); // This function removes currently selected tree node. Also relevant data object // in the serializer is removed. // get selected tree node wxTreeItemId treeNode = treeCtrl->GetSelection(); if(treeNode == treeCtrl->GetRootItem()) { // remove all data objects from the serializer wxGetApp().m_XmlIO.RemoveAll(); // clear the tree control's content treeCtrl->DeleteAllItems(); // re-create tree root CreateTreeRoot(); } else { // get ID of a serializable object associated with relevant tree node long nId = ((TreeData*)treeCtrl->GetItemData(treeNode))->m_nDataId; // remove the data object stored in the serializer wxGetApp().m_XmlIO.RemoveItem(nId); // remove tree control node treeCtrl->Delete(treeNode); } } void MainFrame::OnUpdateRemove( wxUpdateUIEvent& event ) { event.Enable(treeCtrl->GetSelection().IsOk()); } void MainFrame::OnOk( wxCommandEvent& event ) { wxUnusedVar(event); Destroy(); }