0

I wanted to load an XML and have it available to all events. In the application below, the Button1 and Button3 events use the loaded XML, whereas Button2 won't and I had to load it within the event. I am assuming that every time I load the file it is taking up more resources, which I am trying to avoid. My questions are: - Do I have to find a different way to populate the Datagridview ? - Do I need to somehow unload the XML file if I need to load it somewhere else to save system resources.

I am new to programming and self taught so apologize in advance if terminology is not correct.

It is an application in a Windows form with:

  • Button1 generating a listBox in ListBox1;
  • Button2 populating dataGridView1 with 2 columns;
  • Button3 populating comboBox1 list

XML is as Follows:

<?xml version="1.0" encoding="utf-8" ?>
<Config>  
  <Categories>
    <Category Name="OneChar">
      <Entry>
        <Name>a</Name>
        <id>1</id>
      </Entry>
      <Entry>
        <Name>b</Name>
        <id>2</id>
      </Entry>
      <Entry>
        <Name>c</Name>
        <id>3</id>
      </Entry>
    </Category>
    <Category Name="TwoChar">
      <Entry>
        <Name>aa</Name>
        <id>11</id>
      </Entry>
      <Entry>
        <Name>bb</Name>
        <id>22</id>
      </Entry>
      <Entry>
        <Name>cc</Name>
        <id>33</id>
      </Entry>      
    </Category>   
  </Categories>
  <Schemes>
  </Schemes>
</Config>

Code as Follows:

using System;
using System.Windows.Forms;
using System.Xml.Linq;
using System.Xml;
using System.Xml.XPath;

namespace List_box_multiple_query
{
    public partial class Form1 : Form
    {
        XDocument xdoc = XDocument.Load("Config\\TestFile.xml");
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            listBox1.Items.Clear();
            var result = xdoc.XPathSelectElements("/Config/Categories/Category [@Name='TwoChar']/Entry/Name");
            foreach (string entry in result)
            {
                listBox1.Items.Add(entry);
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {

            dataGridView1.Rows.Clear();
            dataGridView1.Refresh();

            XmlDocument doc = new XmlDocument();
            doc.Load("Config\\testfile.xml");
            XmlNodeList nodeList;
            XmlNode root = doc.DocumentElement;
            nodeList = root.SelectNodes("/Config/Categories/Category[@Name='OneChar']/Entry");

            foreach (XmlNode entry in nodeList)
            {               
                int n = dataGridView1.Rows.Add();
                dataGridView1.Rows[n].Cells[0].Value = entry["Name"].InnerText.ToString();
                dataGridView1.Rows[n].Cells[1].Value = entry["id"].InnerText.ToString();                
            }

        }

        private void button3_Click(object sender, EventArgs e)
        {
            var result = xdoc.XPathSelectElements("/Config/Categories/Category [@Name='TwoChar']/Entry/Name");

            foreach (string entry in result)
            {
                comboBox1.Items.Add(entry);
            }
        }
    }
}
Anil_M
  • 10,893
  • 6
  • 47
  • 74
Big Vil
  • 7
  • 6

1 Answers1

1

First, you need to be consistently using XDocument or XmlDocument.

You can define a

 private Lazy<XmlDocument> docLazy = 
         new Lazy<XmlDocument>(() =>
             { 
                 XmlDocument doc = new XmlDocument();
                 doc.Load("Config\\TestFile.xml");
                 return doc;
             }
         );

and then use it in all the handlers

  var doc = docLazy.Value;

In this way it will be loaded from file only for the first call and then cached in memory.

My previous answer was similar but for XDocument.

reply to comments

Is there an easy way to select nodes in an XML and use their contents...?

Yes, for example

var test_nodeList  = xdoc.Descendants("Category")
    .Where(x => x.Attribute("Name").Value.Equals("OneChar"))
       .Descendants("Entry");

instead of

nodeList = root.SelectNodes("/Config/Categories/Category[@Name='OneChar']/Entry");
Community
  • 1
  • 1
  • Thank you for the quick reply. I have tried the fix, but the problem is the following line fails -- XmlNode root = doc.DocumentElement; -- which is the same problem as when I don't load the file within the Button click event. The intellisense message indicates " 'XDocument' does not contain a definition for 'DocumentElement' and no extension method ..." – Big Vil Sep 08 '16 at 06:41
  • Fixed. But doc is like xdoc: you can still do doc = xdocLazy.Value; –  Sep 08 '16 at 07:09
  • Apologies but I did not note your first comment about choosing XMLDocument vs XDocument. Indeed I have found the "DocumentElement" definition does not work with XDocument whereas "XPathSelectElements" does not work with XMLDocument. I quite like navigating XML with Xpath so I think XDocument it is. The code above works but as you have mentioned, uses a mixture of XDocument and XMLDocument. Is there an easy way to select nodes in an XML and use their contents to populate a Datagrid ? – Big Vil Sep 08 '16 at 11:38
  • @BigVil updated my answer to reply to your last comment: pls consider accepting it, thanks :-) –  Sep 08 '16 at 12:33
  • Appreciate the help. As an aside, there may be a closing parentheses missing from initial definition and may be worth editing in case anyone uses this case as a reference. Big Vil – Big Vil Sep 08 '16 at 21:21
  • I have accepted the answer however my current foreach loop doesn't work after the statement provided. How do I cycle through the var test_nodelist to extract "name" and "id" and populate the datagridview ? – Big Vil Sep 08 '16 at 23:31
  • Thanks! Have you seen that I proposed test_nodelist instead of nodelist? That should return the same 3 items with your example and you should be able to loop at foreach(var entry in test_nodelist) and extract the entry.Descendants("Name") and entry.Descendants("id"), so you'll get then a .Value for each of those XElement. Of course I'll fix if there is something wrong, but it looked fine when I tried it, let me know...BTW I've seen they've fixed the parentheses, sorry for that. –  Sep 09 '16 at 00:50
  • If you're sure there is always a Name for each entry, then for example entry.Descendants("Name").First().Value and so on... –  Sep 09 '16 at 01:01
  • All good. "entry.Descendants("Name").First().Value" works fine but can't seem to use just "entry.Descendants("Name") .Value" as intellisense doesn't accept ".Value" in this statement – Big Vil Sep 09 '16 at 22:44
  • of course, you need First(), which is the problem? you need it since Descendants returns possible multiple nodes etc.. –  Sep 09 '16 at 22:46
  • I Understand. Thanks again – Big Vil Sep 09 '16 at 22:52
  • Thanks to you, @BigVil ! bye :-) –  Sep 09 '16 at 22:56