This is an old revision of the document!


Using XML

Editing XML

Normally, binding a ListView to a datasource (the xml) and let WPF databinding handle the update of the XML data automatically. To do this you could create a DataSet from your XML and then bind that using ListView.ItemsSource. You would then create a DataTemplate to define the visual representation of a record in your xml. This could be input controls that'd allow you do directly edit the record within your listview. If you prefer a master-detail style view you would bing the detail view to the current item of your listview (e.g. UserControl.DataContext={Binding CurrentItem, ElementName=myListView}). The rest will be handled by WPF. UPDATE: Here is an example how you could even bind direcly to your XDocument, so so you do not necessarily have to use DataSets. (Source: bitbonk on StackOverflow.com)

Parsing XML

Example (Source: Lukas Šalkauskas):

XmlDocument xmlDoc= new XmlDocument(); //* create an xml document object.
xmlDoc.Load("yourXMLFile.xml"); //* load the XML document from the specified file.
 
//* Get elements.
XmlNodeList girlAddress = xmlDoc.GetElementsByTagName("gAddress");
XmlNodeList girlAge = xmlDoc.GetElementsByTagName("gAge"); 
XmlNodeList girlCellPhoneNumber = xmlDoc.GetElementsByTagName("gPhone");
 
//* Display the results.
Console.WriteLine("Address: " + girlAddress[0].InnerText);
Console.WriteLine("Age: " + girlAge[0].InnerText);
Console.WriteLine("Phone Number: " + girlCellPhoneNumber[0].InnerText);

XmlDocument: If you are after one specific element, you can access child elements with the indexer: xmlDoc[“Root”], and these can be chained: xmlDoc[“Root”][“Folder”][“Item”] to dig down the hierarchy (although it's sensible to validate that these elements actually exist) – (Source: Jason Williams)

If you're processing a large amount of data (many megabytes) then you want to be using XmlReader to stream parse the XML. Anything else (XPathNavigator, XElement, XmlDocument and even XmlSerializer if you keep the full generated object graph) will result in high memory usage and also a very slow load time. Of course, if you need all the data in memory anyway, then you may not have much choice. (Source: Simon Steele)

Other technologies and libraries to manipulate XML in .NET:

Using LINQ with XML

Example: (Source: Will

XDocument xml = XDocument.Load(@"D:\devel\VS\pchart\charts.xml");
 
var query = from p in xml.Elements("charts").Elements("chart")
select p;
foreach (var record in query)
{
  chartListView.Items.Add(new { 
    Name = record.Attribute("Name").Value, 
    Type = record.Attribute("Type").Value, 
    defaultFontName = record.Attribute("defaultFontName").Value, 
    defaultFontSize = record.Attribute("defaultFontSize").Value, 
    ID = record.Attribute("ID").Value 
    }
  );
}

Converting between List<T> and IEnumerable<T>: (Source: DrJokepu)

List<string> myList = new List<string>();
IEnumerable<string> myEnumerable = myList;
List<string> listAgain = myEnumerable.ToList();

Beware however that myEnumarable is the same object instance as myList, but listAgain is not the same object instance as myEnumerable. Depending on what you want to do and what myEnumerable is, “List<string> listAgain = myEnumerable as List<string>;” might be better. – ChrisW

Chrisw: Oh yes, you're right of course, but the IEnumerable<T> interface is immutable so it will not cause problems in that direction and casting back just feels dirty when you have a function that will take care of type safety. – DrJokepu

You also could just use the original myList object. (I guess I don't really get the question) – sth

It's just that if he edits myList then he would be editing myEnumrable, but if he edits listAgain then he wouldn't be editing myEnumerable.

A List<T> is an IEnumerable<T>, so actually, there's no need to 'convert' a List<T> to an IEnumerable<T>. Since a List<T> is an IEnumerable<T>, you can simply assign a List<T> to a variable of type IEnumerable<T>.

The other way around, not every IEnumerable<T> is a List<T> of course, so then you'll have to call the ToList() member method of the IEnumerable<T>. Source: David B, Frederik Gheysels

Examples

Loading and Parsing an XML File into a Data Structure

For an XML file like this:

<?xml version="1.0"?>
<Workshops xmlns="">
  <Workshop>
    <Name>Audina's 12th Annual Continuing Education Workshop</Name>
    <RecID>12</RecID>
    <WorkshopID>AudinaWorkshop12</WorkshopID>
    <VenueInfo>
      <Organization>Orlando Marriott Lake Mary</Organization>
      <AddressLn1>1501 International Parkway</AddressLn1>
      <City>Lake Mary</City>
      <StateProv>FL</StateProv>
      <PostalCode>32746</PostalCode>
      <PhoneHome>407.995.1100</PhoneHome>
      <PhoneWork>800.380.7724</PhoneWork>
      <Fax>407.995.1101</Fax>
      <Website>www.marriotthotels.com</Website>
    </VenueInfo>
    <Days>
      <DateTime>2008-06-11</DateTime>
      <DateTime>2008-06-12</DateTime>
    </Days>
    <PricingDailyPackages>
      <PricingPackage>
        <Name>Premium</Name>
        <Pricing>100</Pricing>
      </PricingPackage>
      <PricingPackage>
        <Name>Standard</Name>
        <Pricing>200</Pricing>
      </PricingPackage>
      <PricingPackage>
        <Name>Other</Name>
        <Pricing>200</Pricing>
      </PricingPackage>
    </PricingDailyPackages>
    <CancellationPolicy>
      If you are unable to attend, please let us know immediately. You may
      receive a complete refund or send a substitute up to 7 days before the workshop date. If
      canceling within 7 days of the workshop date, your registration fee will be refunded minus a
      $50.00 service fee.
    </CancellationPolicy>
    <Instructions></Instructions>
    <HotelAccommodations></HotelAccommodations>
  </Workshop>
  <Workshop>
    <Name>Audina's 13th Annual Continuing Education Workshop</Name>
    <RecID>13</RecID>
    <WorkshopID>AudinaWorkshop13</WorkshopID>
    <VenueInfo>
      <Organization>Orlando Marriott Lake Mary</Organization>
      <AddressLn1>1501 International Parkway</AddressLn1>
      <City>Lake Mary</City>
      <StateProv>FL</StateProv>
      <PostalCode>32746</PostalCode>
      <PhoneHome>407.995.1100</PhoneHome>
      <PhoneWork>800.380.7724</PhoneWork>
      <Fax>407.995.1101</Fax>
      <Website>www.marriotthotels.com</Website>
    </VenueInfo>
    <Days>
      <DateTime>2009-06-15</DateTime>
      <DateTime>2009-06-16</DateTime>
    </Days>
    <PricingDailyPackages>
      <PricingPackage>
        <Name>Premium</Name>
        <Pricing>100</Pricing>
      </PricingPackage>
      <PricingPackage>
        <Name>Standard</Name>
        <Pricing>200</Pricing>
      </PricingPackage>
      <PricingPackage>
        <Name>Other</Name>
        <Pricing>200</Pricing>
      </PricingPackage>
    </PricingDailyPackages>
    <CancellationPolicy>
      If you are unable to attend, please let us know immediately. You may
      receive a complete refund or send a substitute up to 7 days before the workshop date. If
      canceling within 7 days of the workshop date, your registration fee will be refunded minus a
      $50.00 service fee.
    </CancellationPolicy>
    <Instructions></Instructions>
    <HotelAccommodations></HotelAccommodations>
  </Workshop>
</Workshops>

To format XML content to fit in a data structure like this:

    //========================================================================
    // Class TWorkshop
    //========================================================================
    public class TWorkshop
    {
        public int RecID                          { get; set; }
        public string WorkshopID                  { get; set; }
        public string Name                        { get; set; }
        public TContactInfo VenueInfo             { get; set; }
        public List<DateTime> Days                { get; set; }
        public List<TPricingPackage> PricingDailyPackages  { get; set; }
        public string CancellationPolicy          { get; set; }
        public string Instructions                { get; set; }
        public string HotelAccommodations         { get; set; }
  }

We can implement the logic in a function like this:

//-----------------------------------------------------------------------------------------
/// <summary>
/// Load data from an XML file.
/// </summary>
/// <param name="AFilename">Filename of XML file to load</param>
/// <returns>true if success, false if not</returns>
//-----------------------------------------------------------------------------------------
public bool LoadFromXmlFile(string AFilename)
{
	if (System.IO.File.Exists(AFilename))
	{
		xmldoc = XDocument.Load(AFilename);
		if (xmldoc != null)
		{
			// parse xml data and load into TWorkshopList object
			List<XElement> xmldata = new List<XElement>();
			xmldata = xmldoc.Root.Elements("Workshop").ToList<XElement>();
 
			foreach (XElement el in xmldata)
			{
				// create TWorkshop record
				TWorkshop currec = new TWorkshop();
 
				// Parse xml record, and add it
				currec.VenueInfo.Clear();
				currec.Name                         = el.Element("Name").Value;
				currec.WorkshopID                   = el.Element("WorkshopID").Value;
				currec.RecID                        = Convert.ToInt32(el.Element("RecID").Value);
				currec.VenueInfo.Organization       = el.Element("VenueInfo").Element("Organization").Value;
				currec.VenueInfo.Address.AddressLn1 = el.Element("VenueInfo").Element("AddressLn1").Value;
				currec.VenueInfo.Address.City       = el.Element("VenueInfo").Element("City").Value;
				currec.VenueInfo.Address.StateProv  = el.Element("VenueInfo").Element("StateProv").Value;
				currec.VenueInfo.Address.PostalCode = el.Element("VenueInfo").Element("PostalCode").Value;
				currec.VenueInfo.PhoneHome          = el.Element("VenueInfo").Element("PhoneHome").Value;
				currec.VenueInfo.PhoneWork          = el.Element("VenueInfo").Element("PhoneWork").Value;
				currec.VenueInfo.Fax                = el.Element("VenueInfo").Element("Fax").Value;
				currec.VenueInfo.Website            = el.Element("VenueInfo").Element("Website").Value;
				currec.CancellationPolicy           = el.Element("CancellationPolicy").Value;
				currec.Instructions                 = el.Element("Instructions").Value;
				currec.HotelAccommodations          = el.Element("HotelAccommodations").Value;
 
				// Days: extra processing
				currec.Days.Clear();
				List<XElement> elDays = el.Element("Days").Elements("DateTime").ToList<XElement>();
				foreach (XElement elDay in elDays)
				{
					string DateInInvariantCultureStrFmt = elDay.Value;
					DateTime DateLocalized = new DateTime();
					try
					{
						DateLocalized = DateTime.Parse(DateInInvariantCultureStrFmt, 
						System.Globalization.CultureInfo.InvariantCulture, 
						System.Globalization.DateTimeStyles.None);
					}
					catch(FormatException exc)
					{
						throw new FormatException(string.Format("'{0}' is not in an acceptable format. [{1}]", DateInInvariantCultureStrFmt, exc.Message));
					}
					currec.Days.Add(DateLocalized);
				}
 
				// PricingDailyPackages: extra processing
				currec.PricingDailyPackages.Clear();
				List<XElement> elPkgs = el.Element("PricingDailyPackages").Elements("PricingPackage").ToList<XElement>();
				foreach (XElement elPkg in elPkgs)
				{
					TPricingPackage pkg = new TPricingPackage();
					pkg.Name    = elPkg.Element("Name").Value;
					pkg.Pricing = Convert.ToInt32(elPkg.Element("Pricing").Value);
					currec.PricingDailyPackages.Add(pkg);
				}
 
				// add TWorkshop record
				this.Add(currec);
			}
 
			//_Data = new ObservableCollection<XElement>(xmldoc.Root.Elements("Workshop"));
			XNode node = xmldoc.Root.Element("Workshop");
			XDocument curxmldoc = node.Document;
 
			//_Data = new ObservableCollection<XElement>(curxmldoc.Root.Element("WorkshopID"));
			_Data.Add(curxmldoc.Root.Element("Workshop").Element("WorkshopID"));
 
			// Success
			return true;
		}
		else 
		{
			// Failed to load data from file
			return false;
		}
 
	}
	else
	{
		// Failed. File not found.
		return false;
	}
}  
References