2

My XML file looks like this

<?xml version="1.0" encoding="utf-8" ?>
<root>
  <mob name="mob1" lvl="5" hp="30" mp="25"/>
  <mob name="mob2" lvl="6" hp="50" mp="55"/>
  <mob name="mob3" lvl="9" hp="80" mp="85"/>
  <mob name="mob4" lvl="11" hp="130" mp="125"/>
</root>

I'm fairly new to programming and working on a program where I can list and keep track of data. What I want to do is have a Combo-box populated with the entries from the "name" attribute, and when you select entry in the combo-box it would pull the Lvl,hp,mp attributes from the same node and put them into some variables to use and display.

The way I had working was using an XML file like this

    <?xml version="1.0" encoding="utf-8" ?>
<root>
  <moblist list="mob1,mob2,mob3,mob4"/>
  <mob0 lvl="5" hp="30" mp="25"/>
  <mob1 lvl="6" hp="50" mp="55"/>
  <mob2 lvl="9" hp="80" mp="85"/>
  <mob3 lvl="11" hp="130" mp="125"/>
</root>

Turning the moblist node list attribute into a list, and using that to populate the combo box. And then having doing something like this :

    newindex = cmb_mobs.SelectedIndex
    index = "/root/mob" & newindex
    Dim doc As XmlDocument = New XmlDocument()
    doc.Load("C:/test.xml")
    Dim root As XmlNode = doc.DocumentElement
    Dim mobs As XmlNode
    mobs = root.SelectSingleNode(index)
    Dim shw_lvl As String = mobs.Attributes.ItemOf("lvl").InnerText
    Dim shw_hp As String = mobs.Attributes.ItemOf("hp").InnerText
    Dim shw_mp As String = mobs.Attributes.ItemOf("mp").InnerText

And I would have that go off every time the Combo box had its selected index change.

Question 1:Is there a better way to achieve these results? I am open to changing the xml structure if it can be done better.

  • On a general note: You really should not have "numbered" XML elements. If there are several instances of mob, they all ought to be ``, not `` through ``. Have an `id` attribute instead. (Im unsure what the purpose of `` is, too. It's in the structure of the XML, there is no need to keep an explicit list, or is there?) – Tomalak Oct 23 '14 at 20:09
  • Also, without seeing the rest of your code, it's not easy to answer question 2. One way would be to load the XML file once at application start and use the in-memory instance every time you need it. – Tomalak Oct 23 '14 at 20:12
  • It was the only way I could figure out to get it to do what I wanted, how would I populate say a ComboBox with the 'id' attribute and then when I select an item in the ComboBox have it grab all the other attributes from the same node as the selected id and put them into variables? – Kaleb Davis Oct 23 '14 at 20:17
  • A ComboBox can contain a list of arbitrary objects, they don't have to be strings. You could have a ComboBox over XmlNode objects, for example. Compare the MSDN article on the [ComboBox.Items Property](http://msdn.microsoft.com/en-us/library/system.windows.forms.combobox.items%28v=vs.110%29.aspx). You could also use custom Mob objects, like Neolisk's answer suggests. Or you use [XML-to-object de-serialization](http://stackoverflow.com/questions/364253/how-to-deserialize-xml-document) to build custom objects right from your XML. – Tomalak Oct 23 '14 at 20:22

2 Answers2

2

You can use xml serialization.

Using this xml file,

<?xml version="1.0" encoding="utf-8" ?>
<root>
  <mob name="mob1" lvl="5" hp="30" mp="25"/>
  <mob name="mob2" lvl="6" hp="50" mp="55"/>
  <mob name="mob3" lvl="9" hp="80" mp="85"/>
  <mob name="mob4" lvl="11" hp="130" mp="125"/>
</root>

Define the xml model like this in VB

Imports System.Xml.Serialization
Imports System.IO
<XmlRoot("root")>
Public Class XmlModel
    <XmlElement("mob")>
    Public Property Mobs As List(Of XmlMob)
End Class

<Serializable>
Public Class XmlMob
    <XmlAttribute("name")>
    Public Property Name As String
    <XmlAttribute("lvl")>
    Public Property Level As Integer
    <XmlAttribute("hp")>
    Public Property HitPoints As Integer
    <XmlAttribute("mp")>
    Public Property MagicPoints As Integer
End Class

Then deserialize the xml file into an instance of the XmlModel class

Private xmlMobs As XmlModel

Private Sub LoadXmlFile()
    Dim s As New XmlSerializer(GetType(XmlModel))
    Using sr As New StreamReader("c:/test.xml")
        xmlMobs = s.Deserialize(sr)
    End Using
End Sub

Now you have a variable, xmlMobs, which holds all your mobs info. You can then modify this variable (change name, stats, etc.), and serialize back into the file.

Private Sub SaveXmlFile()
    Dim s As New XmlSerializer(GetType(XmlModel))
    Using sw As New StreamWriter("c:/test.xml")
        s.Serialize(sw, xmlMobs)
    End Using
End Sub
djv
  • 15,168
  • 7
  • 48
  • 72
  • I think you'd have to include an XML sample that works with this approach. (And a way to bind this to a ComboBox, while you're at it.) – Tomalak Oct 23 '14 at 20:29
  • 1
    @Tomalak I used the example in OP's question. I'll add it here for clarity. – djv Oct 23 '14 at 20:29
1

You can cache your XML in memory, like this:

Dim xml = <?xml version="1.0" encoding="utf-8"?>
          <root>
            <mob name="mob1" lvl="5" hp="30" mp="25"/>
            <mob name="mob2" lvl="6" hp="50" mp="55"/>
            <mob name="mob3" lvl="9" hp="80" mp="85"/>
            <mob name="mob4" lvl="11" hp="130" mp="125"/>
          </root>

Dim mobs As New List(Of Mob)
For Each e As XElement In xml.Root.Elements
  Dim m As New Mob
  m.name = e.Attribute("name").Value
  m.lvl = e.Attribute("lvl").Value
  m.hp = e.Attribute("hp").Value
  m.mp = e.Attribute("mp").Value
  mobs.Add(m)
Next

That's assuming the following class Mob:

Class Mob
  Public Property name As String
  Public Property lvl As String
  Public Property hp As String
  Public Property mp As String

  Public Overrides Function ToString() As String
    Return Me.name
  End Function
End Class

Then use data binding to populate your combobox:

comboBox1.DataSource = mobs

Then on SelectedIndexChanged event, extract and show the data your need, from a selected Mob:

Dim m As Mob = DirectCast(comboBox1.SelectedItem, Mob)
Dim shw_lvl As String = m.lvl
Dim shw_hp As String = m.hp
Dim shw_mp As String = m.mp
Victor Zakharov
  • 25,801
  • 18
  • 85
  • 151