Introduction
This article shows a short way to dynamically fill a list or combobox with
xml-data.
Background
I didn't wanted to write an article about what you read now at this very
moment. I wanted to play around with the xerces parser to learn its interface.
After the first look I decided to write a simple base class for reading data
from xml files easily with xerces. The result is the class
CXMLBase
. And know that you are reading my first article for
The Code Project. I've used the resources here exhaustingly. This is my first try to
give back my experience. If there are any more questions feel free to contact
me!
Used techniques
- XML (xerces-parser)
- STL (maps and list) a very easy, fast and efficient way to store/manage your
stuff in memory
- MFC (owner drawing) fastest possibility to get ur xml data drawn from the
"std-datastore"
Using the code
class CXMLBase
is the base class for all different kinds of
controls or services. class CXMLGrid
is derived from class
CXMLBase
. My idea behind this inheritance was to get one more baseclass
for list like controls. i.e. comboboxes, lists, grids, maybee trees, but I
think this could be done more easy in an extra derivation from class
CXMLBase
like class CXMLTree
1. Step (Creating the xml file!)
My datasource has a grid like look and feel, so you have to divide it into
rows and cols. The body tag is <XMLGRID>
. You have to assign
an id
in order to get the bound to the right control.
label
and icons
are extracted to their according
member variables, but the MFC controls do not use it. I'm going to implement
this features later. <header>
will define the look an feel of
the single columns.
First col (index 0) will be used to get the specific icon name. I'm going to
use the grid in virtual mode, so u can get an unlimited amount of rows and an
other rowsource (i.e. recordset from a db query). Got anyone a good idea how to
implement this better? Second col (index 1) shows the according string which will
be show in the control. Third col is a user defined value. In derived controls
you are able to define as much cols as you want in order to get all values in
your app. All data is written to a stl::map
of CXMLGridData m_rows
.
="1.0" ="ISO-8859-1"
<XMLGRID id="maps" label="Map-Typen" icons="../test/data">
<header>
<col id="1">label</col>
<col id="2">filter</col>
<col id="0">icon</col>
</header>
<row >
<col id="1">Defuse (DE)</col>
<col id="2">de_</col>
<col id="0">de.ico</col>
</row>
<row >
<col id="1">Rescue (CS)</col>
<col id="2">cs_</col>
<col id="0">cs.ico</col>
</row>
<row >
<col id="1">Aim (AIM)</col>
<col id="2">aim</col>
<col id="0">aim.ico</col>
</row>
<row >
<col id="1">Defuse and Rescue (CSDE)</col>
<col id="2">csde_</col>
<col id="0">csde.ico</col>
</row>
</XMLGRID>
2. Step (Creating the member)
Use the class wizard to create a member
i.e. m_cboXMLList
of type CXMLListBox
Don't forget to
include the header files!! XMLListBox.h or XMLComboBox.h Now you are ready to
initialize the control and use it. Simply call Init()
from the
OnInitDialog()
(or where ever you want it to be initialized)
m_cboXMLGrid.Init( "data.xml", "maps");
m_lstXMLGrid.Init( "data.xml", "maps");
3. Step (Have fun!!)
That's all. Data will be shown in the control.
Code definition for the base classes (included in the sources!!) Data maps used for storing the xml stuff in rows and cols
typedef std::map CColsMap;
typedef std::pair CColsPair;
typedef std::map CXMLGridData;
typedef std::pair CXMLGridDataPair;
class CXMLGridRow
{
public:
long m_lIndex;
CColsMap *m_pCols;
CXMLGridRow()
{
m_pCols = new CColsMap();
m_lIndex = -1;
};
CXMLGridRow(const CXMLGridRow &src)
{
m_lIndex = src.m_lIndex;
m_pCols = new CColsMap( *(src.m_pCols));
};
~CXMLGridRow()
{
if( m_pCols )
{
m_pCols->clear();
delete m_pCols;
}
m_pCols = NULL;
};
void Insert( const char *lpcszEntry)
{
(*m_pCols)[m_pCols->size()] = _T(lpcszEntry);
};
void Insert( int iIdx, const char *lpcszEntry)
{
(*m_pCols)[iIdx] = _T(lpcszEntry);
};
};
The base class for all of our weird xml data. Parse
sets up the
xerces and gives you the document pointer.
class CXMLBase
{
public:
bool Init(const char* lpcszXMLFile, const char* lpcszID);
protected:
virtual void Parse( DOMDocument *doc, const char* lpcszID)=0;
};
This is the funky class for all list like controls. Parse()
calls ParseRow()
if a <row>
tag is found, parses
its Attributes and its Header -> ParseRow()
looks up for
cols. So if you want to write your own service or control simply derive a
class
of class CXMLGrid
and define the functions
needed to add new features. Don't forget to call its adequate virtual to provide
the base functions. Only in case of redefining the tags used for base classes
define Parse()
. Otherwise Parse()
will call
OnUnknownAttribute()
to provide user implementations.
WARNING:
Redefining could lead to an unknown behavior of my provided code. So please
check carefully. I suggest to use other tags instead of redefining them or first
simply call the base class Parse()
function and then do your own
parsing. This will lead to an overhead but ensures full functionality of the
base classes.
class CXMLGrid : public CXMLBase
{
public:
CXMLGrid(){ m_lRowCount = 0; };
~CXMLGrid(){};
virtual void Parse ( DOMDocument *doc, const char* lpcszID );
virtual void ParseRow ( DOMNode *node );
virtual void ParseCol ( DOMNode *node, CXMLGridRow &row );
virtual void ParseAttributes ( DOMNode *node );
virtual void ParseHeader ( DOMNode *node );
virtual void OnUnknownAttribute( DOMNode *node );
CXMLGridData m_rows;
protected:
CXMLGridRow m_header;
long m_lRowCount;
CString m_strLabel;
CString m_strID;
CString m_strIcons;
};
Conclusion
Playing around with xerces is weird stuff and fun fun fun. It is a very
powerful tool. Getting started with it is no problem. Sample code is included
in the distribution. The documentation is great. If you have a basic
understanding of reading a manual, you'll get started very fast. My biggest
problem was to prepare the code for parsing the file itself. I have used the
copy-paste wizard (included in every good IDE ;-) ) to Parse()
the
document. All other kind of parsing code is just like playing around! Good job
by the devteam from xerces. Number 1!!! And the best, you know, all free :-)
Points of Interest
If anyone got a good idea for features, please contact me!!
History
- 2005-03-10 1.000 Initial release
- 2005-03-11 1.001 Added icon drawing, modified some code (still
searching for good and small code to load and draw icons from a file (no
resource!!)
- 2005-03-11 1.002 Added
OnUnknownAttribute()
to provide
user defined tags without to rewrite the parse function. Added some more
documentation to the article.
Thanks to