Build a Maintenance Application with the XmlTextWriter/XmlTextReader Classes

The past several articles have demonstrated how to use both the XmlTextWriter and XmlTextReader classes to perform basic XML file, or document, tasks. This article takes what you've seen thus far to the next logical step—creating a real application that reads and writes data using these classes.

Remember that both the XmlTextWriter and XmlTextReader classes provide for only sequential, forward-only access to an XML file. Therefore, use these classes only in situations where your application design allows for processing every node in the XML file. Examples of such scenarios are XML parsers and applications that maintain single-entity files.

Building the Demo

This article's demo application is a C++ mixed-mode (native and managed code) example of maintaining a single-entity file, where the information for each movie video is stored in an individual file. If the application were storing the information for all videos in the same file, using the .NET XML DOM (Document Object Model) classes would be best, because they allow direct access to specific nodes without having to process each of the file's nodes from beginning to end. (Upcoming articles will explore the .NET implementation of the XML DOM.)

The following steps walk you through creating the demo application:

  1. Create an MFC dialog-based application called VideoInfo.
  2. From the Project Settings, specify the option Use Managed Extensions.
  3. Open the stdafx.h file and add the following to the end of the file:
    #using <mscorlib.dll>
    #using <System.Windows.Forms.dll>
    #using <System.dll>
    #using <System.xml.dll>
    #using <Microsoft.VisualBasic.dll>
    using namespace System;
    using namespace System::Windows::Forms;
    using namespace System::Xml;
    using namespace System::IO;
    using namespace Microsoft::VisualBasic;
    #undef MessageBox
    (You'll see shortly the purpose for the Microsoft.VisualBasic assembly.)
  4. Open the dialog template resource and add the controls as you see them in Figure 1.

    Figure 1: Dialog Template Resource Layout for the Demo Application

  5. Hard code two entries into the Format combobox control's Data property: "DVD;VHS" (without the quotes).
  6. Set the Actors listbox Sort property to True.
  7. Set up the DDX variables as listed in Table 1.

    Table 1: DDX Variables to Be Used in Demo

    ControlVariable TypeVariable Name
    Format editCStringm_strFormat
    Title editCStringm_strTitle
    Director editCStringm_strDirector
    Actors listboxCListBoxm_lbxActors
    Studio editCStringm_strStudio
    Release editintm_iReleaseYear
    Runtime editintm_iRuntime
  8. Add a dialog member variable that will hold the value of the application folder:
    class CVideoInfoDlg : public CDialog
      CString m_strWorkingDir;
  9. Add the following code to set the m_strWorkingDir member just before the end of the dialog's OnInitDialog function:
    TCHAR buff[MAX_PATH];
    GetModuleFileName(NULL, buff, MAX_PATH);
    m_strWorkingDir = Path::GetDirectoryName(buff);
  10. As the dialog box's listbox and Add button indicate, the application allows for the entry of multiple actors to each video definition. If you're a veteran MFC developer, you're accustomed to having to create a dialog box any time a value is needed from the user. However, because this is a mixed-mode application (giving access to the full .NET class library) and Microsoft ported the VB 6 InputBox function for backwards compatibility between VB 6 and VB.NET, you also have access to it from C++. Therefore, you can obtain a single value from the user with one line of code.

    Add an event handler for the dialog box's Add button and code it as follows:

    void CVideoInfoDlg::OnBnClickedAddActor()
      CRect rect;
      String* actor = 
        Interaction::InputBox(S"Enter the Actor name and click the
                                OK button:",
                              S"New Actor",
                              rect.left + (rect.Width() / 2),
                     + (rect.Height() / 2));
      if (actor->Length)
        if (LB_ERR ==
          m_lbxActors.FindStringExact(-1, (CString)actor))
          MessageBox::Show(S"That actor is already listed");
  11. Now, add the following function to allow the removal of the currently selected actor in the listbox.
    Note:The Add/Remove Actor functions have nothing to do with XML, but they're small bits of code that help to create a more practical application from which to learn XML.
    void CVideoInfoDlg::OnBnClickedRemoveActor()
      int iCurrSel;
      if (LB_ERR == (iCurrSel = m_lbxActors.GetCurSel()))
        MessageBox::Show(S"Please select an actor to remove");

Build a Maintenance Application with the XmlTextWriter/XmlTextReader Classes

  1. Add an event handler for the dialog box's Save button. In a real-world application, you would include much more error handling and data validation. However, this example focuses on the XML.

    After calling UpdateData, the function builds the file name using the video title and format. An XMLTextWriter object then is instantiated and several of its methods are used to perform such tasks as starting the document and writing elements and attributes. Note how in some cases the WriteElementString is used and in other situations where more information is needed—such as when writing nested elements or elements with attributes—the WriteStartElement is first called, the addition information is written, and then the WriteEndElement function is called to terminate that element:

    void CVideoInfoDlg::OnBnClickedSave()
    #pragma push_macro("new")
    #undef new
      if (UpdateData())
        XmlTextWriter* xmlwriter;
          String* fileName = String::Format(S"{0}-{1}.xml",
          fileName = Path::Combine(m_strWorkingDir,
          xmlwriter = new XmlTextWriter(fileName,
          xmlwriter->WriteAttributeString(S"Format", m_strFormat);
          xmlwriter->WriteElementString(S"Title", m_strTitle);
          xmlwriter->WriteElementString(S"Director", m_strDirector);
          for (int i = 0; i < m_lbxActors.GetCount(); i++)
            CString strActor;
            m_lbxActors.GetText(i, strActor);
            xmlwriter->WriteElementString(S"Actor", strActor);
          xmlwriter->WriteEndElement();    // end of Actors
          xmlwriter->WriteElementString(S"Studio", m_strStudio);
          xmlwriter->WriteEndElement();    // end of VideoInfo
          MessageBox::Show(S"Video information successfully saved");
        catch(Exception* e)
    #pragma pop_macro("new")
  2. Now, you add the function to open an existing video-information file. Add an event handler for the Open button and code it as follows.

    The function begins by displaying a dialog box for the user to specify a file to open. An XMLTextReader object is instantiated to read that file's nodes. A basic while loop is used, where the XMLTextReader::Read method is called until it returns false (indicating end of file).

    Within the loop, the XmlTextReader::NodeType property is inspected to ensure that only elements nodes (XmlNodeType::Element) are processed. If the node type is an element, the function then determines whether the current element is one whose value needs to be retrieved for display on the dialog box. If it is, the value is retrieved using the XmlTextReader::ReadString method. Because the Format value is stored as an attribute of the VideoInfo element, the XmlTextReader::GetAttribute method is used to obtain that value if the current element's Name value is equal to "VideoInfo":

    void CVideoInfoDlg::OnBnClickedOpen()
    #pragma push_macro("new")
    #undef new
      CFileDialog dlg(TRUE);
      dlg.m_pOFN->lpstrInitialDir = m_strWorkingDir;
      dlg.m_pOFN->lpstrTitle = _T("Open a VideoInfo XML File");
      dlg.m_pOFN->lpstrFilter = _T("XML Files (*.xml)\0*.xml\0");
      if (IDOK == dlg.DoModal())
        XmlTextReader* xmlreader;
          xmlreader  = new XmlTextReader(dlg.GetPathName());
          while (xmlreader->Read())
            if (XmlNodeType::Element == xmlreader->NodeType)
              CString name = (CString)xmlreader->Name;
              if (0 == name.CompareNoCase(_T("VideoInfo")))
                m_strFormat = (CString)xmlreader->
              else if (0 == name.CompareNoCase(_T("Title")))
                m_strTitle = (CString)xmlreader->ReadString();
              else if (0 == name.CompareNoCase(_T("Director")))
                m_strDirector = (CString)xmlreader->ReadString();
              else if (0 == name.CompareNoCase(_T("Actor")))
              else if (0 == name.CompareNoCase(_T("Studio")))
                m_strStudio = (CString)xmlreader->ReadString();
              else if (0 == name.CompareNoCase(_T("ReleaseYear")))
                m_iReleaseYear = Convert::ToInt32(xmlreader->
              else if (0 == name.CompareNoCase(_T("Runtime")))
                m_iRuntime = Convert::ToInt32(xmlreader->
        catch(Exception* e)
    #pragma pop_macro("new")
  3. Finally, build and test the application. You should have a fully functional application that uses the .NET XML classes to store and retrieve data. Figure 2 shows an example of running this application with information from the famous Reservoir Dogs movie displayed.


    Figure 2: Example Running of the Demo Application

    Figure 3 shows the same video file's information as displayed in Internet Explorer.


    Figure 3: You Can Open the File Using Any XML Parser (including Internet Explorer)

Looking Ahead

As previously mentioned, the XmlTextWriter/XmlTextReader classes are used in situations where your application's design allows for the writing or reading of an XML file's nodes in sequential order. Although this works fine for certain scenarios, there are many situations where your application design or data schema require that you be able to randomly read or update a specific node. An obvious example is if your data is being stored in a large file where writing or reading every node would be impractical and inefficient. For those scenarios, there is the DOM. The .NET class library implements the DOM via the XmlDocument class that upcoming articles will cover.

About the Author

Tom Archer - MSFT

I am a Program Manager and Content Strategist for the Microsoft MSDN Online team managing the Windows Vista and Visual C++ developer centers. Before being employed at Microsoft, I was awarded MVP status for the Visual C++ product. A 20+ year veteran of programming with various languages - C++, C, Assembler, RPG III/400, PL/I, etc. - I've also written many technical books (Inside C#, Extending MFC Applications with the .NET Framework, Visual C++.NET Bible, etc.) and 100+ online articles.



  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds