Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I read values from an XML document to build a ComboBox?

Tags:

c#

xml

winforms

I'm trying to read an xml file which I want to make for my mom. So basically this is what I want to do:

  1. A ComboBox which will show all the vegetable names in the XML.
  2. After selecting a vegetable, the second ComboBox will show the recipe names in the XML that could use the vegetable selected in the first ComboBox for cooking.
  3. Last, with a OK Button, the selected recipe will read the file path which leads to the recipe.

XML I wrote

<Vegetables>
    <vegetable name="Carrot">
        <recipe name="ABCrecipe">
            <FilePath>C:\\</FilePath>
        </recipe>
        <recipe name="DEFrecipe">
            <FilePath>D:\\</FilePath>
        </recipe>   
    </vegetable>
    <vegetable name="Potato">
        <recipe name="CBArecipe">
            <FilePath>E:\\</FilePath>
        </recipe>
            <recipe name"FEDrecipe">
            <FilePath>F:\\</FilePath>
        </recipe>
    </vegetable>
</Vegetables>

C# code

private void Form1_Load(object sender, EventArgs e)
{
    XmlDocument xDoc = new XmlDocument();
    xDoc.Load("Recipe_List.xml");
    XmlNodeList vegetables = xDoc.GetElementsByTagName("Vegetable");
    for (int i = 0; i < vegetables.Count; i++)
    {
        comboBox1.Items.Add(vegetables[i].Attributes["name"].InnerText);
    }
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    //I'm lost at this place.
}

The first ComboBox is now able to display the vegetable names, but how do I make the 2nd ComboBox to read the recipes?

like image 877
MeowMeow Avatar asked May 24 '13 08:05

MeowMeow


3 Answers

Your xml should be restructured, as you are mixing data when putting recipe names and file path node as the value of the recipe node

Here is a better approach:

<Vegetables>
    <vegetable name="Carrot">
        <recipe name="ABCrecipe">
            <FilePath>C:\\</FilePath>
        </recipe>
        <recipe name="DEFrecipe">
            <FilePath>D:\\</FilePath>
        </recipe>   
    </vegetable>
    <vegetable name="Potato">
        <recipe name="CBArecipe">
            <FilePath>E:\\</FilePath>
        </recipe>
        <recipe name="FEDrecipe">
            <FilePath>F:\\</FilePath>
        </recipe>
    </vegetable>
</Vegetables>

So to display the recipes you need to extract the attribute of the recipe node. How to do this is explained here: How to read attribute value from XmlNode in C#?

Edit: Corrected xml structure due to comments. Thanks

like image 97
Samuel Avatar answered Nov 03 '22 08:11

Samuel


If you want to be able to get the name of a vegetable out of the document, the easiest thing to do is to define it as its own separate piece of data. There's nothing invalid about what you've done, but it's made it quite a lot harder to get to the data you want.

If you could can, change the structure to something like this, so that it makes your life easier:

<vegetables>
    <vegetable>
        <name>Carrot</name>
        <recipe>
             <name>ABCrecipe</name>
             <path>C:\\</path>
        </recipe>
        <recipe>
            <name>DEFrecipe</name>
            <path>D:\\</path>
        </recipe>   
    </vegetable>
</vegetables>

Assuming you're using .NET 3.5 or newer, you will have access to the LINQ-to-XML APIs. These provide a simplified way to to read values from an XML document and should make solving this problem a little easier.

You create a document using:

var document = XDocument.Load("Recipe_List.xml");

Then you can write a query to get the vegetable elements like this:

var vegetables = document
    .Element(XName.Get("vegetables"))
    .Elements(XName.Get("vegetable"));

Once you have these elements, you can get their names, like this:

var vegNames = vegetables.Select(ele => ele.Element(XName.Get("name")).Value);

You can then plug this information into your combo-box really easily:

foreach (string name in vegNames)
{
    comboBox1.Items.Add(name);
}
like image 42
Paul Turner Avatar answered Nov 03 '22 08:11

Paul Turner


I assume, you're using C# in .Net 4.0 framework

You can format your xml like this:

<Vegetables>
    <vegetable>
        <name>Carrot</name>
        <recipe>
            <name>ABCrecipe</name>
            <FilePath>C:\\</FilePath>
        </recipe>
        <recipe>
            <name>DEFrecipe</name>
            <FilePath>D:\\</FilePath>
        </recipe>   
    </vegetable>
    <vegetable>
        <name>Potato</name>
        <recipe>
            <name>CBArecipe</name>
            <FilePath>E:\\</FilePath>
        </recipe>
        <recipe>
            <name>FEDrecipe</name>
            <FilePath>F:\\</FilePath>
        </recipe>
    </vegetable>
</Vegetables>

Then just use this query to select those items:

var vegiesList = (from veg in xDoc.Descendants("vegetable")
                  select new Vegetable()
                  {
                       Name = veg.Element("name").Value,
                       Recipes = (from re in veg.Elements("recipe")
                                  select new Recipe(re.Element("name").Value, re.Element("FilePath").Value)).ToList()
                  })
                  .ToList();

Then for your Class Structure:

class Vegetable
{
    public string Name { get; set; }
    public List<Recipe> Recipes { get; set; }
}

class Recipe
{
    public Recipe(string name, string path)
    {
       Name = name;     Path = path;
    }
    public string Name { get; set; }
    public string Path { get; set; } 
}

vegiesList.ForEach(veg => comboBox1.Items.Add(veg.Name));
//You can select its property here depending on what property you want to add on your `ComboBox`
like image 31
roybalderama Avatar answered Nov 03 '22 08:11

roybalderama