Introduction
For this tutorial, you'll need basic knowledge of the XML format.
A while ago, I had to come up with a solution on how to serialize a CTreeCtrl - or a derived class from it - with XML, e.g. loading and saving its content in an XML file without destroying the tree structure of the items.
Browsing the web did not give me any hints on how to do this, so I decided to write my own routines for storing CTreeCtrl contents into a file: the class CTreeCtrlXML was born.
As my program had to be fairly small and without big overheads and dependencies, I chose TinyXML to do all the dirty XML parsing and saving stuff. This library is - you maybe suspected it already - very small, easy to integrate in existing projects and more or less platform independent. Also, it's open source under the Zlib license.
You can get TinyXML here.
Implementation
To stay on focus, I developed a small program which demonstrates the functionality of the following two functions:
bool CTreeCtrlXML::LoadFromXML( const CString& a_strFile )
{
TiXmlNode* pXML = NULL;
TiXmlDocument xmlDoc;
char szBuf[ _MAX_PATH + 1 ];
CString strTemp = a_strFile;
getcwd( szBuf, sizeof( szBuf ) );
strcat( szBuf, "\\" );
strcat( szBuf, strTemp.GetBuffer( 1 ) );
if( xmlDoc.LoadFile( szBuf ) )
{
// XML root
pXML = xmlDoc.FirstChild( "XML" );
if( NULL == pXML )
return false;
// Load our tree control
Load( pXML );
// Expand all entries
Expand( GetRootItem(), TVE_EXPAND );
return true;
}
return false;
}
bool CTreeCtrlXML::SaveToXML( const CString& a_strFile )
{
TiXmlNode* pXML = NULL;
TiXmlDocument xmlDoc;
xmlDoc.InsertEndChild( TiXmlDeclaration( "1.0", "UTF-8", "yes" ) );
pXML = xmlDoc.InsertEndChild( TiXmlElement( "XML" ) );
Save( pXML );
CString strFile = a_strFile;
return xmlDoc.SaveFile( strFile.GetBuffer( 1 ) );
}
To make the class a bit more useful for your own projects, I also added two functions which need a pointer to an existing XML node. These functions use the node pointers as their XML root, so this makes it extremely easy to immigrate the CtrlTreeXML serialization into existing XML files:
void CTreeCtrlXML::Load( TiXmlNode* a_pNode )
{
ASSERT( NULL != a_pNode );
TiXmlNode* pItems = a_pNode->FirstChild( "Items" );
TiXmlNode* pItem = NULL;
if( NULL == pItems )
return;
pItem = pItems->FirstChild( "Item" );
while( NULL != pItem )
{
LoadItem( pItem, NULL );
pItem = pItem->NextSibling( "Item" );
}
}
void CTreeCtrlXML::Save( TiXmlNode* a_pNode )
{
ASSERT( NULL != a_pNode );
TiXmlNode* pItems = a_pNode->InsertEndChild( TiXmlElement( "Items" ) );
TiXmlNode* pParent = pItems;
TiXmlNode* pNewNode = NULL;
HTREEITEM hItem = GetRootItem();
int iIndent = 0; int iLastIndent = 0;
while( hItem )
{
iIndent = GetIndentLevel( hItem );
int iDiff = iIndent - iLastIndent;
if( iDiff > 0 )
{
ASSERT( NULL != pNewNode );
while( iDiff-- )
pNewNode = pNewNode->InsertEndChild( TiXmlElement( "Item" ) );
ASSERT( NULL != pNewNode );
pParent = pNewNode->Parent();
}
else if( iDiff < 0 )
{
iDiff--; pParent = pNewNode;
while( iDiff++ < 0 )
pParent = pParent->Parent();
ASSERT( NULL != pParent );
pNewNode = pParent->InsertEndChild( TiXmlElement( "Item" ) );
}
else
{
ASSERT( NULL != pParent );
pNewNode = pParent->InsertEndChild( TiXmlElement( "Item" ) );
}
iLastIndent = iIndent;
TiXmlElement* pElement = pNewNode->ToElement();
ASSERT( NULL != pElement );
pElement->SetValue( "Item" );
pElement->SetAttribute( "Title", GetItemText( hItem ) );
hItem = GetNextItem( hItem );
}
}
Since XML is very extensible, you could store all your configuration data into an XML file, the tree control then would be only a small part of it.
The demo application shows you how to load and save CTreeCtrl's content into two different files: Loading is done from "tree.xml", the data is then saved to "save.xml".
History
- 3rd May, 2006: Initial post