locked
Read / Parse XML file to controls in groupboxes RRS feed

  • Question

  • I have a very simple basic XML file that I need to read. The structure is below:

    <assets>
     <asset>
      <Category>cat1</Category>
      <Installed>Yes</Installed>
      <Name>bob</Name>
     </asset>
     <asset>
      <Category>cat1</Category>
      <Installed>no</Installed>
      <Name>jack</Name>
     </asset>
     <asset>
      <Category>cat2</Category>
      <Installed>Yes</Installed>
      <Name>julie</Name>
     </asset>
     <asset>
      <Category>cat3</Category>
      <Installed>Yes</Installed>
      <Name>linda</Name>
     </asset>
     <asset>
      <Category>cat4</Category>
      <Installed>No</Installed>
      <Name>jake</Name>
     </asset>
     <asset>
      <Category>cat5</Category>
      <Installed>Yes</Installed>
      <Name>paul</Name>
     </asset>

    I have 5 group boxes on a form. Each groupbox corresponds to a category, information in cat1 will be placed in groupbox1 and cat2 will be placed in groupbox2, etc.. I have a label which is either green for yes and red for no. A textbox is used to display the name information. The label and textbox are created dynamically as the xml file is being read.

    This is what I am attempting which is not working very well as I have never worked with XML files before.

    Dim xmlr = XmlReader.Create("c:\temp\test.xml")
            Do While xmlr.Read()
                If xmlr.NodeType = XmlNodeType.Element AndAlso xmlr.Name = "Category" Then
                    MessageBox.Show(xmlr.ReadElementString)
                    MessageBox.Show(xmlr.ReadElementString)
                Else
                    xmlr.Read()
                End If
            Loop

    The messagebox.show function is there as a test to see if the file is being read.. I plan on replacing them with a series of if … then statements to place the dynamic label and textbox and populate them with the data..

    ie.

    if xmlr.nodetype = element and name of element = "category1" then place label and textbox in groupbox1

    if xmlr.nodetype = element and name of element = "category2" then place label and textbox in groupbox2..

    Where am I going wrong?? Please help..


    Jason

    Friday, November 27, 2020 4:43 PM

Answers

  • Hi

    I think you are confusing the use of the IO.Path.Combine. Also, there are many places much more appropriate to store User Data than the root directory.

    Here is an example of cheking for a path, creating it if not found and then saving some test data to it. Here, I chose the C:\ProgramData directory for the data storage location. More commonly, I use a Directory I keep alongside the application executable, but that is just a preference.

    This example will do the above and also will open the File Explorer at the new file.  If you set the default application for .xml files to Microsoft Visual Studio 2019 then you can view the contents of the file just by clicking the file in File Explorer an Visual Studio will open it.

    Option Strict On
    Option Explicit On
    Public Class Form1
      Dim dt As New DataTable("Asset")
      '  path pointing to C:\ProgramData
      Dim MyPath As String = IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.AllUsersApplicationData, "MyData", "test.xml")
      Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
        ' check if MyPath has already been
        ' Created - if not then create it
        If Not IO.Directory.Exists(IO.Directory.GetParent(MyPath).FullName) Then
          ' no, not founIO.Directory.GetParent(MyPath).FullNamed so create it
          IO.Directory.CreateDirectory(IO.Directory.GetParent(MyPath).FullName)
          ' here, the directory has been created
          ' BUT no data file.
        End If
    
        With dt
          .Columns.Add("Category", GetType(String))
          .Columns.Add("Installed", GetType(Boolean))
          .Columns.Add("Name", GetType(String))
          If IO.File.Exists(MyPath) Then
            .ReadXml(MyPath)
          Else
            ' no file found so create a
            ' new TEST FILE
            With dt.Rows
              .Add("Cat1", True, "Test1")
              .Add("Cat2", True, "Test2")
              .Add("Cat1", False, "Test3")
              .Add("Cat3", True, "Test4")
              .Add("Cat5", False, "Test5")
              .Add("Cat3", True, "Test6")
              .Add("Cat2", False, "Test7")
              .Add("Cat4", True, "Test8")
            End With
            dt.WriteXml(MyPath)
          End If
        End With
    
        ' show you the file exists
        Process.Start(IO.Directory.GetParent(MyPath).FullName)
    
        ' if you set xml files default
        ' Application to Microsoft Visual
        ' Studio 2019 then double click
        ' to view the contents
      End Sub
    End Class


    Regards Les, Livingston, Scotland

    • Marked as answer by x73 Sunday, November 29, 2020 7:00 PM
    Sunday, November 29, 2020 6:17 PM

All replies

  • Hello,

    To be honest I see json as a better way to go if you don't mind looking at the following.

    Let's start with the json file, in the code which follows the file resides in the same folder as the executable.

    json file

    [
      {
        "Category": "cat1",
        "Installed": true,
        "Name": "Bob"
      },
      {
        "Category": "cat1",
        "Installed": false,
        "Name": "Jack"
      },
      {
        "Category": "cat2",
        "Installed": true,
        "Name": "Julie"
      },
      {
        "Category": "cat3",
        "Installed": true,
        "Name": "Linda"
      }
    ]

    Each item will be stored in the following class (note IsInstalled is not stored but read from)

    Container class

    Public Class Asset
        Public Property Category() As String
        Public Property Installed() As Boolean
        <JsonIgnore>
        Public ReadOnly Property IsInstalled() As String
            Get
                Return If(Installed, "Yes", "No")
            End Get
        End Property
        Public Property Name() As String
    
        Public Overrides Function ToString() As String
            Return Name
        End Function
    End Class

    The following class contains a method to create a new json file, read from the json file and update the json file.

    Worker class

    Public Class AssetOperations
        ''' <summary>
        ''' File name to read/write json
        ''' </summary>
        Public Shared FileName As String = "assets.json"
        ''' <summary>
        ''' Used to provide a json file with assets
        ''' </summary>
        Public Shared Sub MockAssets()
    
            Dim assetList As New List(Of Asset) From {
                New Asset() With {.Category = "cat1", .Installed = True, .Name = "Bob"},
                New Asset() With {.Category = "cat1", .Installed = False, .Name = "Jack"},
                New Asset() With {.Category = "cat2", .Installed = True, .Name = "Julie"},
                New Asset() With {.Category = "cat3", .Installed = True, .Name = "Linda"}
            }
    
            Using streamWriter = File.CreateText(FileName)
    
                Dim serializer = New JsonSerializer With {.Formatting = Formatting.Indented}
                serializer.Serialize(streamWriter, assetList)
    
            End Using
    
        End Sub
        ''' <summary>
        ''' Read assets from json file, if it does not exists
        ''' an empty list is returned.
        ''' </summary>
        ''' <returns></returns>
        Public Shared Function ReadAssets() As List(Of Asset)
    
            If File.Exists(FileName) Then
                Using streamReader = New StreamReader(FileName)
                    Dim json = streamReader.ReadToEnd()
                    Return JsonConvert.DeserializeObject(Of List(Of Asset))(json)
                End Using
            Else
                Return New List(Of Asset)
            End If
    
        End Function
        ''' <summary>
        ''' Update list of assets. Since you can't simply update one
        ''' asset you need to modify one or more from the method above ReadAssets
        ''' change stuff then save the entire list back to disk as json
        ''' </summary>
        ''' <param name="assets"></param>
        Public Shared Sub UpdateAsset(assets As List(Of Asset))
    
            Using streamWriter = File.CreateText(FileName)
    
                Dim serializer = New JsonSerializer With {.Formatting = Formatting.Indented}
                serializer.Serialize(streamWriter, assets)
    
            End Using
    
    
        End Sub
    End Class

    To group items read in by category this class is needed.

    Grouping code

    Public Class AssetGrouping
        Public Property Category() As String
        Public Property AssetList() As List(Of Asset)
        Public Sub New()
            AssetList = New List(Of Asset)
        End Sub
    End Class

    Form code examples

    Create the json file

    AssetOperations.MockAssets()

    Read the json file

    Dim assetList As List(Of Asset) = AssetOperations.ReadAssets()

    Group the assets by category and for this example display the results to the IDE output window where you would be assigning values to controls in group box controls.

    Dim assetList As List(Of Asset) = AssetOperations.ReadAssets()
    
    Dim results As List(Of AssetGrouping) = (
            From asset In assetList
            Group By category = asset.Category Into g = Group
            Select New AssetGrouping With {.Category = category, .AssetList = g.ToList()}).ToList()
    
    For Each assetGrouping As AssetGrouping In results
        Console.WriteLine(assetGrouping.Category)
        For Each asset As Asset In assetGrouping.AssetList
            Console.WriteLine($" {asset.Name}, {asset.IsInstalled}")
        Next
    Next

    Here are the results from the code block above

    cat1
     Bob, Yes
     Jack, No
    cat2
     Julie, Yes
    cat3
     Linda, Yes
    

    Ending notes.

    • Using json with classes everything is strongly typed vs xml without a xml schema is loosely typed.
    • I used json.net NuGet package but there is also System.Text.Json if working with .NET 5 or .NET Core 3.1. I've used both and favor json.net for the time being as Sytem.Text.Json requires more work which surely will change later down the road.



    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    My GitHub code samples
    GitHub page

    Check out:  the new Microsoft Q&A forums

    Friday, November 27, 2020 6:49 PM
  • Hi

    I was in the process of preparing this to post so if Karens answer is suitable then just ignore this one.

    My idea is to use a DataTable - this simplifies the saving.reading of the data and present you wilt a datarow for each of the boxes (Catagory, Installed and Name)

    This is a layout example for this.

    	Dim dt As New DataTable("Asset")
    	Dim path As String = IO.Path.Combine(Application.StartupPath, "Data", "data.xml")
    	Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
    		dt.WriteXml(path)
    	End Sub
    	Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    		
    		With dt
    			.Columns.Add("Category", GetType(String))
    			.Columns.Add("Installed", GetType(Boolean))
    			.Columns.Add("Name", GetType(String))
    			If IO.File.Exists(path) Then .ReadXml(path)
    		End With
    		' at this point, if the data file is
    		' found then the DataTable named 'dt'
    		' will contain all the data rows
    		' Catagory, Installed, Name
    
    
    		For Each r As DataRow In dt.Rows
    			Dim catagory As String = r("Category").ToString
    			Dim Installed As Boolean = CBool(r("Installed"))
    			Dim Name As String = r("Name").ToString
    
    			' here, for each row, you have the
    			' fields available to fill all
    			' the Controls
    		Next
    	End Sub

    This is the data file used above (see path variable for location)

    <?xml version="1.0" standalone="yes"?>
    <DocumentElement>
      <Asset>
        <Category>Cat1</Category> 
        <Installed>true</Installed>
        <Name>bob</Name>
      </Asset>
      <Asset>
        <Category>Cat2</Category> 
        <Installed>false</Installed>
        <Name>jack</Name>
      </Asset>
      <Asset>
        <Category>Cat3</Category> 
        <Installed>true</Installed>
        <Name>julie</Name>
      </Asset>
      <Asset>
        <Category>Cat4</Category> 
        <Installed>true</Installed>
        <Name>linda</Name>
      </Asset>
      <Asset>
        <Category>Cat5</Category> 
        <Installed>false</Installed>
        <Name>paul</Name>
      </Asset>
    </DocumentElement>


    Regards Les, Livingston, Scotland



    • Edited by leshay Friday, November 27, 2020 7:12 PM
    Friday, November 27, 2020 7:05 PM
  • hmm, I like that idea. The question I asked here is actually a continuation of the other question I asked regarding groupbox's and dynamically placing controls on them.

    I am definitely down the rabbit hole..

    I had an idea to use a class, but so far, this is what I have for the class. and in my mind, should be complete.. But who knows.

    Class Asset
        Private _category As String
        Public Property category() As String
            Get
                Return _category
            End Get
            Set(value As String)
                _category = value
            End Set
        End Property
        Private _installed As String
        Public Property installed() As String
            Get
                Return _installed
            End Get
            Set(value As String)
                _installed = value
            End Set
        End Property
        Private _name As String
        Public Property name() As String
            Get
                Return _name
            End Get
            Set(value As String)
                _name = value
            End Set
        End Property
    End Class

    for the actual reading of data I was attempting to read through the xml file and as I "find" {cat1, cat2, cat3, cat4, cat5} elements make the control and place them in the corresponding groupbox. I know, you prefer panels; I like groupboxes... Just a preference. Opendata() code is below. I have the code working to make the xml data file which was surprisingly easy for me.. Thought I would have a much harder time..

    I think I can almost see the finish line with this one... But, who knows?!!

     Private Sub OpenData()
    
            Dim xmldata As XElement = XElement.Load("c:\temp\test.xml")
            Dim assets = From asset In xmldata...<Product> Select New Asset With {.category = asset.<category>.Value, .installed = asset.<installed>.Value, .name = asset.<name>.Value}
    
            For Each item In assets
                If item.category = "cat1" Then
                    ' draw the controls in the cat1 groupbox, populate them with the data
                ElseIf item.category = "cat2" Then
                    ' draw the controls in the cat2 groupbox, populate them with the data
                End If
                ' repeat this for all 5 categories until the file is read.
    
    
            Next
    
    
    
        End Sub

    Am I close, or am I way off base??


    Jason

    Friday, November 27, 2020 9:27 PM

  • Am I close, or am I way off base??


    Jason

    Hi

    There really is not a 'right' way to do things.  There are usually many ways to tackle a project.  If you plan a strategy BEFORE any coding, then you stand a good chance of designing the coding in a more straightforward way.

    For example:  if you had mentioned the need to store/retrieve the data in your other thread question, I would probably not have offered the solution I did.  I would have designed it all around a DataTable instead and built the controls from that - going row by row.

    It may well have used similar code structures to my earlier offering but in a different context.

    Personally, I don't see a need for a Class as you are only going to use 5 sets of data, but, your Class code would work and creating 5 instances would do the job also.

    Either way - one of these or any other, you end up with the data available to use in the boxes.


    Regards Les, Livingston, Scotland


    • Edited by leshay Friday, November 27, 2020 9:40 PM typo
    Friday, November 27, 2020 9:39 PM
  • I'm not quiet sure what I did but now when I try and run the project I get an error:

    Build started...
    1>------ Build started: Project: Vehicle Proximity Status - Editor, Configuration: Debug Any CPU ------
    1>vbc : error BC30420: 'Sub Main' was not found in '(None)'.
    1>Done building project "Vehicle Proximity Status - Editor.vbproj" -- FAILED.
    ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

    getting very frustrated with the whole thing.. Not sure what I changed.


    Also, the Class is made wasn't working, so I commented out the class section and reference to it, and was going to use your solution of a DataTable. It looks less confusing than anything else I have seen.

    At this point, I may just copy the code to notepad and save it somewhere, create a new project, and recreate the form elements.. I tried to search the entire solution / project for sub main and any reference to to it..

    • Edited by x73 Saturday, November 28, 2020 9:26 PM added to post.
    Saturday, November 28, 2020 9:08 PM
  • I'm not quiet sure what I did but now when I try and run the project I get an error:

    getting very frustrated with the whole thing.. Not sure what I changed.

    Hi

    Frustration is part of the procees.

    I have no idea hat you have done.

    Does the project open in the editor as normal?

    Does the Designer view open as normal?

    If you can look at the code - do you see the Public Class Form1, and perhaps a Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load line?

    Are all Subs fully formed - ie all have an End Sub?

    Likewise, are all Functions are fully formed with Return statement etc?

    Is there a End Class at bottom of code?

    Try deleting the Debug and Release folders entirely (they will be recreated if you can get the compile done)

    *

    Those are just what comes to mind, probably of little help, but all I can think of.


    Regards Les, Livingston, Scotland


    • Edited by leshay Saturday, November 28, 2020 9:20 PM
    Saturday, November 28, 2020 9:19 PM
  • This much comes down to how much code you "need" to write, and how much you "want" to write.

    Given you simple xml, then I don't think using a big 18 wheeler truck to deliver a pizza makes sense here.

    Given your simple xml? Well, adopting the kiss principal here?

    You can use this code:

     Dim sFile As String = "c:\test4\xml.txt"
    
     Dim rstD As New DataSet
     rstD.ReadXml(sFile)
    
     Dim MyGroup As String = ""
     For Each myrow As DataRow In rstD.Tables(0).Rows
        If MyGroup <> myrow("Category") Then
           MyGroup = myrow("Category")
           Console.WriteLine(MyGroup)
        End If
        Console.WriteLine(vbTab & "-->" & myrow("Name") & vbTab & myrow("Installed"))
    Next
      

    OutPut:

    cat1
    	-->bob	Yes
    	-->jack	no
    cat2
    	-->julie	Yes
    cat3
    	-->linda	Yes
    cat4
    	-->jake	No
    cat5
    	-->paul	Yes
    

    Now of course sending to the console not all that great. 

    If you drag a gridview on to your form, then our code is even LESS!!!

    This code:

            Dim sFile As String = "c:\test4\xml.txt"
    
            Dim rstD As New DataSet
            rstD.ReadXml(sFile)
    
            DataGridView1.DataSource = rstD.Tables(0)

    The result of 4 wee little lines of code is this:

    However, we do need/want some type of grouping, and more so, we AGAIN don't want to write a lot of code.

    Drag + drop a treeview onto the form. We now have this code:

            Dim sFile As String = "c:\test4\xml.txt"
    
            Dim rstD As New DataSet
            rstD.ReadXml(sFile)
    
            DataGridView1.DataSource = rstD.Tables(0)
            Dim MyGroup As String = ""
            Dim MyNode As TreeNode = Nothing
            For Each MyRow As DataRow In rstD.Tables(0).Rows
                If MyGroup <> MyRow("Category") Then
                    MyGroup = MyRow("Category")
                    MyNode = TreeView1.Nodes.Add(MyGroup)
                End If
                Dim OneSet As TreeNode = MyNode.Nodes.Add(MyRow("Name"))
                OneSet.Nodes.Add(MyRow("Installed"))
            Next
    
            TreeView1.ExpandAll()

    And we get this:

    So, i posted 3 examples - one as console, one as a grideview, and one as a tree view.

    So you don't want to assume 5 controls on your form - try to think of using some kind of control that allows repeating data. That way, once you get the data into a table, then in some cases, you can use data bound controls. And looking at what  you kind of looking form? I think a ListView might be the best.

    However, while my work in asp.net and Andriod is near always JSON? Well, in vb land, xml been around the block and been around a long time, and in 9 out of 10 cases, you can't choose your format, and better yet, XML is VERY well supported in vb, and the above total lines of code is LESS then some single examples here. 

    So, for sure use the built in xml reader in .net - this is especially given  your simple and basic XML layout.

    >>Am I close, or am I way off base??

    Well, there is as many flavors of ice cream as there are to do this.

    I will give a few tips:

    Don't start adopting HUGE mac truck frameworks and creating classes that results in buckets of code writing WHEN there is little need.

    Writing code is expensive - so try to write as little as possible!!!

    Until such time the xml (or jason, or csv) becomes complex, then stay with YOUR approach - the kiss principle (in this light - I well think you doing rather well - your attempting to keep this simple and that's good!). 

    And your choice of using XML elements? It not all that bad. However, given the data you have, then I outlined 3 different approaches by adopting a dataset/datatable/datarow.  Depending on your data structites, if you can get data into one of these VERY base and basic .net primiatives? Then you should!

    All in all, as noted, there is not one way to do this, but using a race horse to plow a field?

    Well, there is a very old saying:

    Use the right horse for the right course!

    All in all? I think all of the examples show great promise, and that includes yours.

    My post is already too long, but I would really in place of trying to toot my horn, have written an answer using MSXML.net as you started. I think which road, or which horse comes down to how much xml processing you have "in general" for your existing project. The more complex, then the more complex frameworks are justified as a solution.

    But, do keep in mind the handy dandy built in xml readers we have for datasets, they often save a lot of code and effort.

    Regards,

    Albert D. Kallal (Access MVP 2003-2017)
    Edmonton, Alberta Canada

    Saturday, November 28, 2020 10:20 PM
  • I was able to recover everything. I copied the three main sections of code to a text file and started a new project with a slightly different name. Then just pasted all code in as it was and it runs just fine now.

    Thanks for the suggestions.. Now I can work on the Datatable.. Had some other things to distract me from my coding frustrations. And yes, I agree, its part of the process for sure.


    Jason

    Sunday, November 29, 2020 4:31 AM
  • still a little confused, but i'm getting close. I guess I need to reference each column by column # or name.. Which is what I am try to figure out now.


    Jason

    Sunday, November 29, 2020 5:34 AM
  • still a little confused, but i'm getting close. I guess I need to reference each column by column # or name.. Which is what I am try to figure out now.


    Jason

    Hi

    That was shown in my post a couple of days ago.


    Regards Les, Livingston, Scotland

    Sunday, November 29, 2020 2:18 PM
  • Just having some issues with getting the code to work as expected. The path is good, I am wondering if my XML file is not formatted correctly.

    I'm using the code to test if the xml file is being read, and the message box to display each row is not displaying. I should also mention that for each category there can be multiple entries for each category. so Cat1 can possibly have 1 or many entries or none. But, I'm sure you knew that..

    I did not want to load the xml file on form load as I want that to be up to the user if they want to load existing data from an xml file or just create new entries and overwrite the existing xml data file. so I have an OpenData sub-routine.

    Private Sub OpenData()
            Dim dt As New DataTable("Asset")
            Dim path As String = IO.Path.Combine(Application.StartupPath, "c:\temp\", "test.xml")
            MsgBox(path, MessageBoxButtons.OK)' test path variable
            With dt
                .Columns.Add("Category", GetType(String))
                .Columns.Add("Installed", GetType(Boolean))
                .Columns.Add("Name", GetType(String))
                If IO.File.Exists(path) Then .ReadXml(path)
            End With
            ' at this point, if the data file is
            ' found then the DataTable named 'dt'
            ' will contain all the data rows
            ' Catagory, Installed, Name
            For Each r As DataRow In dt.Rows
                MsgBox((r("Category") & vbCrLf & r("Installed") & vbCrLf & r("Name"), MessageBoxButtons.OK))
            Next
            dt.WriteXml(path)
        End Sub


    Jason

    Sunday, November 29, 2020 5:19 PM
  • Hi

    I think you are confusing the use of the IO.Path.Combine. Also, there are many places much more appropriate to store User Data than the root directory.

    Here is an example of cheking for a path, creating it if not found and then saving some test data to it. Here, I chose the C:\ProgramData directory for the data storage location. More commonly, I use a Directory I keep alongside the application executable, but that is just a preference.

    This example will do the above and also will open the File Explorer at the new file.  If you set the default application for .xml files to Microsoft Visual Studio 2019 then you can view the contents of the file just by clicking the file in File Explorer an Visual Studio will open it.

    Option Strict On
    Option Explicit On
    Public Class Form1
      Dim dt As New DataTable("Asset")
      '  path pointing to C:\ProgramData
      Dim MyPath As String = IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.AllUsersApplicationData, "MyData", "test.xml")
      Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
        ' check if MyPath has already been
        ' Created - if not then create it
        If Not IO.Directory.Exists(IO.Directory.GetParent(MyPath).FullName) Then
          ' no, not founIO.Directory.GetParent(MyPath).FullNamed so create it
          IO.Directory.CreateDirectory(IO.Directory.GetParent(MyPath).FullName)
          ' here, the directory has been created
          ' BUT no data file.
        End If
    
        With dt
          .Columns.Add("Category", GetType(String))
          .Columns.Add("Installed", GetType(Boolean))
          .Columns.Add("Name", GetType(String))
          If IO.File.Exists(MyPath) Then
            .ReadXml(MyPath)
          Else
            ' no file found so create a
            ' new TEST FILE
            With dt.Rows
              .Add("Cat1", True, "Test1")
              .Add("Cat2", True, "Test2")
              .Add("Cat1", False, "Test3")
              .Add("Cat3", True, "Test4")
              .Add("Cat5", False, "Test5")
              .Add("Cat3", True, "Test6")
              .Add("Cat2", False, "Test7")
              .Add("Cat4", True, "Test8")
            End With
            dt.WriteXml(MyPath)
          End If
        End With
    
        ' show you the file exists
        Process.Start(IO.Directory.GetParent(MyPath).FullName)
    
        ' if you set xml files default
        ' Application to Microsoft Visual
        ' Studio 2019 then double click
        ' to view the contents
      End Sub
    End Class


    Regards Les, Livingston, Scotland

    • Marked as answer by x73 Sunday, November 29, 2020 7:00 PM
    Sunday, November 29, 2020 6:17 PM
  • That's awesome, thanks for that.. I found the issue with the file not being read. My xml document was not formatted correctly. I needed to include the standalone=yes and my code was not producing the end element for each <Asset>..

    And I do plan on using a network share location to store the data file. There are two applications I need to write, one is the editor - currently working on, which will be used by me or someone else to update the data as it changes. The second application will be run on a standalone computer with no user input and will periodically open the xml file if there have been changes and update the data displayed. Once I get the "editor" application completed I will use what I have learned from writing the editor and apply it to the "Viewer" application.

    The plan is to have the viewer make sure the XML file is not in use or is in a state that can be read from.. The viewer will open the xml file every hour or so.

    I used to code quiet a lot when I was younger, in my teenage years and in to my 20's. Its been a while getting back in the saddle as I am quickly approaching my 50's. Huge gap in terms of timeframe from coding, so I really do appreciate the help and you have given me a boost of confidence, so thank you. Every time I run in to an issue or problem, I see it as an opportunity to learn, and not see it as a road block but rather a slight speed bump. Always need to make forward progress.

    A little long winded, sorry for that.. back to coding.


    Jason

    Sunday, November 29, 2020 7:00 PM
  • In a strange way?

    The largest challenge?

    Well, it "used" to be simple, and the REASON why was limited choices.

    So, you walk in, and the menu has fries, and a burger.

    Now? It like walking into the largest food emporium you ever seen! The choices are huge, the choices are vast!

    Hallways stretch for miles in each direction. Can't I just get some fries with gravy?

    In fact, finding "simple" bits of code is often what makes coding so much fun. You write a few lines of code - see the result. Today? It seems everything is some huge bus or elephant - and base fundamentals are lost.

    And this loss while a fact of life in this industry? It often takes the fun out of learning! Yes, when I was young - I miss writing some code on that Apple II in BASIC. 

    And even today? Well, when I type in vb.net, I often smile - since I still typing code that reminds of those early days.

    In fact, this is why I posted the most minimal code with the fewest lines of code.

    I can say, that one tip that helps? Try to think of your data as a table. Work with the data that way. And then output the data as csv, as XML, as JSON or whatever format you need, but still "think" of data as tables in your mind.

    Build, think, write code around the concept of a table (or more then one table!).

    Good luck!

    Regards,

    Albert D. Kallal (Access MVP 2003-2017)
    Edmonton, Alberta Canada

    Sunday, November 29, 2020 7:17 PM
  • As promised, the "finished" code. I need to make a few minor tweaks but it works. I'm not 100% satisfied with the repetitive code for the OpenData() sub and the Save to xml sub, but again it works. I'll be adding some error handling and of course I'll try and reduce repetitive code.

    Again, thanks to all who help..

    Imports System.Xml
    Public Class Form1
    
        Private Sub tmrFrmHeading_Tick(sender As Object, e As EventArgs) Handles tmrFrmHeading.Tick
            Text = "Vehicle Proximity Status Editor -- " & DateTime.Now.ToString("dddd MMMM dd yyy hh:mm tt") & " - [" & Environment.UserName & "] [" & Environment.MachineName & "]"
        End Sub
    
        Private Sub OpenData()
            Dim dt As New DataTable("Asset")
            Dim path As String = IO.Path.Combine(Application.StartupPath, "c:\temp\", "test.xml")
            'MsgBox(path, MessageBoxButtons.OK)
            With dt
                .Columns.Add("Category", GetType(String))
                .Columns.Add("Installed", GetType(String))
                .Columns.Add("Name", GetType(String))
                If IO.File.Exists(path) Then .ReadXml(path)
            End With
    
            For Each r As DataRow In dt.Rows
                'MessageBox.Show(r("Category") & vbCrLf & r("Installed") & vbCrLf & r("Name"))
                If r("Category") = "PrimeMovers" Then
    
                    Dim TextBox As TextBox = New TextBox
                    Dim label As Label = New Label
                    Dim count As Integer = PrimeMoversGroupBox.Controls.OfType(Of TextBox).ToList.Count
    
                    With TextBox
                        .Location = New System.Drawing.Point(30, (35 * count) + 60)
                        .Size = New System.Drawing.Size(260, 20)
                        .Name = "txt_" & count + 1
                        .Text = r("Name")
                    End With
    
                    With label
                        .Location = New System.Drawing.Point(5, (35 * count) + 65)
                        .Size = New System.Drawing.Size(20, 20)
                        .Name = "lbl_" & count + 1
                        If r("Installed") = "Yes" Then
                            .BackColor = Color.FromArgb(0, 255, 0)
                        ElseIf r("Installed") = "No" Then
                            .BackColor = Color.FromArgb(170, 0, 0)
                        End If
    
                    End With
    
                    PrimeMoversGroupBox.Controls.Add(label)
                    PrimeMoversGroupBox.Controls.Add(TextBox)
    
                    ' Dynamically create 'DELETE' button to remove the row if needed
    
                    Dim delbtn As Button = New Button
                    With delbtn
                        .Location = New System.Drawing.Point(290, (35 * count) + 60)
                        .Size = New System.Drawing.Size(30, 30)
                        .Name = "delbtn_" & count + 1
                        .Text = "-"
                    End With
    
                    ' Add a handler for the dynamic Button and label control
    
                    AddHandler delbtn.Click, AddressOf Me.delbtn_click
                    AddHandler label.Click, AddressOf Me.lbl_click
    
                    ' Place the dynamic delete button
    
                    PrimeMoversGroupBox.Controls.Add(delbtn)
                ElseIf r("Category") = "Drills" Then
                    Dim TextBox As TextBox = New TextBox
                    Dim label As Label = New Label
                    Dim count As Integer = DrillsGroupBox.Controls.OfType(Of TextBox).ToList.Count
    
                    With TextBox
                        .Location = New System.Drawing.Point(30, (35 * count) + 60)
                        .Size = New System.Drawing.Size(260, 20)
                        .Name = "txt_" & count + 1
                        .Text = r("Name")
                    End With
    
                    With label
                        .Location = New System.Drawing.Point(5, (35 * count) + 65)
                        .Size = New System.Drawing.Size(20, 20)
                        .Name = "lbl_" & count + 1
                        If r("Installed") = "Yes" Then
                            .BackColor = Color.FromArgb(0, 255, 0)
                        ElseIf r("Installed") = "No" Then
                            .BackColor = Color.FromArgb(170, 0, 0)
                        End If
    
                    End With
    
                    DrillsGroupBox.Controls.Add(label)
                    DrillsGroupBox.Controls.Add(TextBox)
    
                    ' Dynamically create 'DELETE' button to remove the row if needed
    
                    Dim delbtn As Button = New Button
                    With delbtn
                        .Location = New System.Drawing.Point(290, (35 * count) + 60)
                        .Size = New System.Drawing.Size(30, 30)
                        .Name = "delbtn_" & count + 1
                        .Text = "-"
                    End With
    
                    ' Add a handler for the dynamic Button and label control
    
                    AddHandler delbtn.Click, AddressOf Me.delbtn_click
                    AddHandler label.Click, AddressOf Me.lbl_click
    
                    ' Place the dynamic delete button
    
                    DrillsGroupBox.Controls.Add(delbtn)
                ElseIf r("Category") = "Jeeps" Then
                    Dim TextBox As TextBox = New TextBox
                    Dim label As Label = New Label
                    Dim count As Integer = JeepsGroupBox.Controls.OfType(Of TextBox).ToList.Count
    
                    With TextBox
                        .Location = New System.Drawing.Point(30, (35 * count) + 60)
                        .Size = New System.Drawing.Size(260, 20)
                        .Name = "txt_" & count + 1
                        .Text = r("Name")
                    End With
    
                    With label
                        .Location = New System.Drawing.Point(5, (35 * count) + 65)
                        .Size = New System.Drawing.Size(20, 20)
                        .Name = "lbl_" & count + 1
                        If r("Installed") = "Yes" Then
                            .BackColor = Color.FromArgb(0, 255, 0)
                        ElseIf r("Installed") = "No" Then
                            .BackColor = Color.FromArgb(170, 0, 0)
                        End If
    
                    End With
    
                    JeepsGroupBox.Controls.Add(label)
                    JeepsGroupBox.Controls.Add(TextBox)
    
                    ' Dynamically create 'DELETE' button to remove the row if needed
    
                    Dim delbtn As Button = New Button
                    With delbtn
                        .Location = New System.Drawing.Point(290, (35 * count) + 60)
                        .Size = New System.Drawing.Size(30, 30)
                        .Name = "delbtn_" & count + 1
                        .Text = "-"
                    End With
    
                    ' Add a handler for the dynamic Button and label control
    
                    AddHandler delbtn.Click, AddressOf Me.delbtn_click
                    AddHandler label.Click, AddressOf Me.lbl_click
    
                    ' Place the dynamic delete button
    
                    JeepsGroupBox.Controls.Add(delbtn)
                ElseIf r("Category") = "ForkLifts" Then
                    Dim TextBox As TextBox = New TextBox
                    Dim label As Label = New Label
                    Dim count As Integer = ForkliftsGroupBox.Controls.OfType(Of TextBox).ToList.Count
    
                    With TextBox
                        .Location = New System.Drawing.Point(30, (35 * count) + 60)
                        .Size = New System.Drawing.Size(260, 20)
                        .Name = "txt_" & count + 1
                        .Text = r("Name")
                    End With
    
                    With label
                        .Location = New System.Drawing.Point(5, (35 * count) + 65)
                        .Size = New System.Drawing.Size(20, 20)
                        .Name = "lbl_" & count + 1
                        If r("Installed") = "Yes" Then
                            .BackColor = Color.FromArgb(0, 255, 0)
                        ElseIf r("Installed") = "No" Then
                            .BackColor = Color.FromArgb(170, 0, 0)
                        End If
    
                    End With
    
                    ForkliftsGroupBox.Controls.Add(label)
                    ForkliftsGroupBox.Controls.Add(TextBox)
    
                    ' Dynamically create 'DELETE' button to remove the row if needed
    
                    Dim delbtn As Button = New Button
                    With delbtn
                        .Location = New System.Drawing.Point(290, (35 * count) + 60)
                        .Size = New System.Drawing.Size(30, 30)
                        .Name = "delbtn_" & count + 1
                        .Text = "-"
                    End With
    
                    ' Add a handler for the dynamic Button and label control
    
                    AddHandler delbtn.Click, AddressOf Me.delbtn_click
                    AddHandler label.Click, AddressOf Me.lbl_click
    
                    ' Place the dynamic delete button
    
                    ForkliftsGroupBox.Controls.Add(delbtn)
                ElseIf r("Category") = "Other" Then
                    Dim TextBox As TextBox = New TextBox
                    Dim label As Label = New Label
                    Dim count As Integer = OtherGroupBox.Controls.OfType(Of TextBox).ToList.Count
    
                    With TextBox
                        .Location = New System.Drawing.Point(30, (35 * count) + 60)
                        .Size = New System.Drawing.Size(260, 20)
                        .Name = "txt_" & count + 1
                        .Text = r("Name")
                    End With
    
                    With label
                        .Location = New System.Drawing.Point(5, (35 * count) + 65)
                        .Size = New System.Drawing.Size(20, 20)
                        .Name = "lbl_" & count + 1
                        If r("Installed") = "Yes" Then
                            .BackColor = Color.FromArgb(0, 255, 0)
                        ElseIf r("Installed") = "No" Then
                            .BackColor = Color.FromArgb(170, 0, 0)
                        End If
    
                    End With
    
                    OtherGroupBox.Controls.Add(label)
                    OtherGroupBox.Controls.Add(TextBox)
    
                    ' Dynamically create 'DELETE' button to remove the row if needed
    
                    Dim delbtn As Button = New Button
                    With delbtn
                        .Location = New System.Drawing.Point(290, (35 * count) + 60)
                        .Size = New System.Drawing.Size(30, 30)
                        .Name = "delbtn_" & count + 1
                        .Text = "-"
                    End With
    
                    ' Add a handler for the dynamic Button and label control
    
                    AddHandler delbtn.Click, AddressOf Me.delbtn_click
                    AddHandler label.Click, AddressOf Me.lbl_click
    
                    ' Place the dynamic delete button
    
                    OtherGroupBox.Controls.Add(delbtn)
                End If
            Next
            dt.WriteXml(path)
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
        End Sub
    
        Public Sub RemoveControls(ByVal obj As Object)
            For i As Integer = (obj.controls.count - 1) To 0 Step -1
                Dim ctrl As Control = obj.controls(i)
                If TypeOf ctrl Is Label Or TypeOf ctrl Is Button Or TypeOf ctrl Is TextBox Then
                    If ctrl.Tag IsNot "locked" Then
                        obj.controls.remove(ctrl)
                        ctrl.Dispose()
                    End If
                End If
            Next i
        End Sub
    
        Private Sub btnClearData_Click(sender As Object, e As EventArgs) Handles btnClearData.Click
    
            Dim result As DialogResult = MessageBox.Show("Clear all data and start from scratch?", "Clear all data", MessageBoxButtons.YesNo)
    
            If (result = DialogResult.Yes) Then
                RemoveControls(PrimeMoversGroupBox)
                RemoveControls(DrillsGroupBox)
                RemoveControls(JeepsGroupBox)
                RemoveControls(ForkliftsGroupBox)
                RemoveControls(OtherGroupBox)
            Else
                'nothng
            End If
    
        End Sub
    
        Private Sub btnLoadData_Click(sender As Object, e As EventArgs) Handles btnLoadData.Click
            Dim result As DialogResult = MessageBox.Show("Performing this action will clear the form," & vbCrLf & "and entries found in the current database will be loaded.", "Clear and reload form data??", MessageBoxButtons.YesNo)
            If (result = DialogResult.Yes) Then
                If My.Computer.FileSystem.FileExists("c:\temp\test.xml") Then
                    RemoveControls(PrimeMoversGroupBox)
                    RemoveControls(DrillsGroupBox)
                    RemoveControls(JeepsGroupBox)
                    RemoveControls(ForkliftsGroupBox)
                    RemoveControls(OtherGroupBox)
                    OpenData()
                Else
                    MsgBox("The file cannot be found!", MessageBoxButtons.OK)
                    Exit Sub
                End If
            Else
                ' nothing
            End If
        End Sub
    
        Private Sub btnAddPrimeMovers_Click(sender As Object, e As EventArgs) Handles btnAddPrimeMovers.Click, btnAddDrills.Click, btnAddJeeps.Click, btnAddForklifts.Click, btnAddOther.Click
            Dim TextBox As TextBox = New TextBox
            Dim label As Label = New Label
            Dim addbtn As Button = CType(sender, Button)
            Dim GB As Control = addbtn.Parent
            Dim count As Integer = GB.Controls.OfType(Of TextBox).ToList.Count
    
            With TextBox
                .Location = New System.Drawing.Point(30, (35 * count) + 60)
                .Size = New System.Drawing.Size(260, 20)
                .Name = "txt_" & count + 1
            End With
    
            With label
                .Location = New System.Drawing.Point(5, (35 * count) + 65)
                .Size = New System.Drawing.Size(20, 20)
                .Name = "lbl_" & count + 1
                .BackColor = Color.FromArgb(170, 0, 0)
            End With
    
            GB.Controls.Add(label)
            GB.Controls.Add(TextBox)
    
            ' Dynamically create 'DELETE' button to remove the row if needed
    
            Dim delbtn As Button = New Button
            With delbtn
                .Location = New System.Drawing.Point(290, (35 * count) + 60)
                .Size = New System.Drawing.Size(30, 30)
                .Name = "delbtn_" & count + 1
                .Text = "-"
            End With
    
            ' Add a handler for the dynamic Button and label control
    
            AddHandler delbtn.Click, AddressOf Me.delbtn_click
            AddHandler label.Click, AddressOf Me.lbl_click
    
            ' Place the dynamic delete button
    
            GB.Controls.Add(delbtn)
        End Sub
    
        Private Sub delbtn_click(ByVal sender As Object, ByVal e As EventArgs)
    
            Dim button As Button = CType(sender, Button) ' reference the button that was clicked
            Dim GB As Control = button.Parent ' Get parent location of the button click event
    
            Dim index As Integer = Integer.Parse(button.Name.Split("_")(1)) ' get index number of the button
            Dim count As Integer = GB.Controls.OfType(Of TextBox).ToList.Count ' count the "rows" in the current section
    
            If count = 0 Then Exit Sub ' nothing to remove, get outta' dodge!
    
            If count = index Or count = 1 Then ' remove the last row or the only row..
                GB.Controls.Remove(GB.Controls.Find("lbl_" & index, True)(0))
                GB.Controls.Remove(GB.Controls.Find("txt_" & index, True)(0))
                GB.Controls.Remove(button)
                button.Dispose()
                Exit Sub
            Else ' all other conditions assume more than 1 row and NOT removing the last row
                For i As Integer = index To count - 1 Step 1
                    Dim dtxt As TextBox = CType(GB.Controls.Find(("txt_" & i), True)(0), TextBox)
                    Dim otxt As TextBox = CType(GB.Controls.Find(("txt_" & i + 1), True)(0), TextBox)
                    Dim dlbl As Label = CType(GB.Controls.Find(("lbl_" & i), True)(0), Label)
                    Dim olbl As Label = CType(GB.Controls.Find(("lbl_" & i + 1), True)(0), Label)
                    dtxt.Text = otxt.Text
                    dlbl.BackColor = olbl.BackColor
                Next
                GB.Controls.Remove(GB.Controls.Find("lbl_" & count, True)(0))
                GB.Controls.Remove(GB.Controls.Find("txt_" & count, True)(0))
                GB.Controls.Remove(GB.Controls.Find("delbtn_" & count, True)(0))
            End If
    
        End Sub
    
        Private Sub lbl_click(ByVal sender As Object, ByVal e As EventArgs)
    
            Dim label As Label = CType(sender, Label)
            Dim labelindex As Integer = Integer.Parse(label.Name.Split("_")(1))
    
            With label
                If .BackColor = Color.FromArgb(170, 0, 0) Then
                    .BackColor = Color.FromArgb(0, 255, 0)
                    .ForeColor = Color.FromArgb(0, 0, 0)
                Else
                    .BackColor = Color.FromArgb(170, 0, 0)
                    .ForeColor = Color.FromArgb(255, 255, 255)
                End If
            End With
        End Sub
    
        Private Sub btnSaveData_Click(sender As Object, e As EventArgs) Handles btnSaveData.Click
            Dim pmcount As Integer = Me.PrimeMoversGroupBox.Controls.OfType(Of TextBox).ToList.Count
            Dim dcount As Integer = Me.DrillsGroupBox.Controls.OfType(Of TextBox).ToList.Count
            Dim jcount As Integer = Me.JeepsGroupBox.Controls.OfType(Of TextBox).ToList.Count
            Dim flcount As Integer = Me.ForkliftsGroupBox.Controls.OfType(Of TextBox).ToList.Count
            Dim ocount As Integer = Me.OtherGroupBox.Controls.OfType(Of TextBox).ToList.Count
    
            If pmcount = 0 And dcount = 0 And jcount = 0 And flcount = 0 And ocount = 0 Then
                MsgBox("There are no entries to save!", MessageBoxButtons.OK)
                Exit Sub
            End If
    
            Dim result As DialogResult = MessageBox.Show("Save the current data on the form?" & vbCrLf &
                                                         "Note: The data file will be overwritten and" & vbCrLf &
                                                         "blank entries will be ignored.", "Save form data??",
                                                                                           MessageBoxButtons.YesNo)
            If (result = DialogResult.No) Then
                Exit Sub
            Else
    
                Dim xmlsettings As New XmlWriterSettings()
                xmlsettings.Indent = True
    
                Using writer As XmlWriter = XmlWriter.Create("c:\temp\test.xml", xmlsettings)
                    writer.WriteProcessingInstruction("xml", "version=""1.0"" standalone=""yes""")
                    writer.WriteComment(Date.Now.ToString("dddd MMMM dd yyy hh:mm tt") & " - [" & Environment.UserName & "] [" & Environment.MachineName & "]")
                    writer.WriteStartElement("DocumentElement")
    
                    ' loop through all groupboxes and output the form data to the XML data file
    
                    For i As Integer = 1 To pmcount Step 1
                        Dim lblstatus As String
                        If PrimeMoversGroupBox.Controls.Find("txt_" & i, True)(0).Text IsNot "" Then
                            If PrimeMoversGroupBox.Controls.Find("lbl_" & i, True)(0).BackColor = Color.FromArgb(170, 0, 0) Then
                                lblstatus = "No"
                            Else
                                lblstatus = "Yes"
                            End If
                            writer.WriteStartElement("Asset")
                            writer.WriteStartElement("Category")
                            writer.WriteString("PrimeMovers")
                            writer.WriteEndElement()
                            writer.WriteStartElement("Installed")
                            writer.WriteString(lblstatus)
                            writer.WriteEndElement()
                            writer.WriteStartElement("Name")
                            writer.WriteString(PrimeMoversGroupBox.Controls.Find("txt_" & i, True)(0).Text)
                            writer.WriteEndElement()
                            writer.WriteEndElement()
                        End If
                    Next
                    For i As Integer = 1 To dcount Step 1
                        Dim lblstatus As String
                        If DrillsGroupBox.Controls.Find("txt_" & i, True)(0).Text IsNot "" Then
                            If DrillsGroupBox.Controls.Find("lbl_" & i, True)(0).BackColor = Color.FromArgb(170, 0, 0) Then
                                lblstatus = "No"
                            Else
                                lblstatus = "Yes"
                            End If
                            writer.WriteStartElement("Asset")
                            writer.WriteStartElement("Category")
                            writer.WriteString("Drills")
                            writer.WriteEndElement()
                            writer.WriteStartElement("Installed")
                            writer.WriteString(lblstatus)
                            writer.WriteEndElement()
                            writer.WriteStartElement("Name")
                            writer.WriteString(DrillsGroupBox.Controls.Find("txt_" & i, True)(0).Text)
                            writer.WriteEndElement()
                            writer.WriteEndElement()
                        End If
                    Next
                    For i As Integer = 1 To jcount Step 1
                        Dim lblstatus As String
                        If JeepsGroupBox.Controls.Find("txt_" & i, True)(0).Text IsNot "" Then
                            If JeepsGroupBox.Controls.Find("lbl_" & i, True)(0).BackColor = Color.FromArgb(170, 0, 0) Then
                                lblstatus = "No"
                            Else
                                lblstatus = "Yes"
                            End If
                            writer.WriteStartElement("Asset")
                            writer.WriteStartElement("Category")
                            writer.WriteString("Jeeps")
                            writer.WriteEndElement()
                            writer.WriteStartElement("Installed")
                            writer.WriteString(lblstatus)
                            writer.WriteEndElement()
                            writer.WriteStartElement("Name")
                            writer.WriteString(JeepsGroupBox.Controls.Find("txt_" & i, True)(0).Text)
                            writer.WriteEndElement()
                            writer.WriteEndElement()
                        End If
                    Next
                    For i As Integer = 1 To flcount Step 1
                        Dim lblstatus As String
                        If ForkliftsGroupBox.Controls.Find("txt_" & i, True)(0).Text IsNot "" Then
                            If ForkliftsGroupBox.Controls.Find("lbl_" & i, True)(0).BackColor = Color.FromArgb(170, 0, 0) Then
                                lblstatus = "No"
                            Else
                                lblstatus = "Yes"
                            End If
                            writer.WriteStartElement("Asset")
                            writer.WriteStartElement("Category")
                            writer.WriteString("Forklifts")
                            writer.WriteEndElement()
                            writer.WriteStartElement("Installed")
                            writer.WriteString(lblstatus)
                            writer.WriteEndElement()
                            writer.WriteStartElement("Name")
                            writer.WriteString(ForkliftsGroupBox.Controls.Find("txt_" & i, True)(0).Text)
                            writer.WriteEndElement()
                            writer.WriteEndElement()
                        End If
                    Next
                    For i As Integer = 1 To ocount Step 1
                        Dim lblstatus As String
                        If OtherGroupBox.Controls.Find("txt_" & i, True)(0).Text IsNot "" Then
                            If OtherGroupBox.Controls.Find("lbl_" & i, True)(0).BackColor = Color.FromArgb(170, 0, 0) Then
                                lblstatus = "No"
                            Else
                                lblstatus = "Yes"
                            End If
                            writer.WriteStartElement("Asset")
                            writer.WriteStartElement("Category")
                            writer.WriteString("Other")
                            writer.WriteEndElement()
                            writer.WriteStartElement("Installed")
                            writer.WriteString(lblstatus)
                            writer.WriteEndElement()
                            writer.WriteStartElement("Name")
                            writer.WriteString(OtherGroupBox.Controls.Find("txt_" & i, True)(0).Text)
                            writer.WriteEndElement()
                            writer.WriteEndElement()
                        End If
                    Next
    
                    writer.WriteEndElement()
                    writer.WriteEndDocument()
                    writer.Flush()
                    writer.Close()
                End Using
            End If
        End Sub
    
        Private Sub btnClearPrimeMovers_Click(sender As Object, e As EventArgs) Handles btnClearPrimeMovers.Click, btnClearDrills.Click, btnClearJeeps.Click, btnClearForkLifts.Click, btnClearOther.Click
            Dim button As Button = CType(sender, Button) ' reference the button that was clicked
            Dim GB As Control = button.Parent ' Get parent location of the button click event
    
            Dim result As DialogResult = MessageBox.Show("Clear " & GB.Text.ToString & " of all data?", "Clear " & GB.Text.ToString, MessageBoxButtons.YesNo)
    
            If (result = DialogResult.Yes) Then
                RemoveControls(GB)
            End If
    
    
        End Sub
    End Class


    Jason

    Monday, November 30, 2020 2:10 AM
  • Hi

    Well done, you have a working application.

    A couple of hints.  Check out the use of Path.Combine - you are not using it correctly.

    You might benifit from using a Select Case ..... End Select in place of your For ElseIf..End If structures.

    Congradulations anyway ...................


    Regards Les, Livingston, Scotland

    Monday, November 30, 2020 1:55 PM
  • Yes, the path.combine is not being used correctly. I will definitely fix that and read up. I'll also look at the Select Case .. End Select.

    Thanks again.


    Jason

    Monday, November 30, 2020 2:06 PM
  • One other approach.

        Private assets As List(Of Asset)
        Public Class Asset
            Private _assetEL As XElement
            Public Sub New(el As XElement)
                Me._assetEL = el
            End Sub
    
            Public Function Category() As String
                Return Me._assetEL.<Category>.Value
            End Function
    
            Public Function Installed() As Boolean
                Dim rv As Boolean = False
                rv = If(Me._assetEL.<Installed>.Value = "Yes", True, False)
                Return rv
            End Function
    
            Public Function Name() As String
                Return Me._assetEL.<Name>.Value
            End Function
        End Class
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim path As String = "your path here"
            Dim xe As XElement
            ' xe = XElement.Load(path) 'Production <<<<<<<
    
            ' for TESTING use literal.  Get rid of this for production
            xe = <assets>
                     <asset>
                         <Category>cat1</Category>
                         <Installed>Yes</Installed>
                         <Name>bob</Name>
                     </asset>
                     <asset>
                         <Category>cat1</Category>
                         <Installed>no</Installed>
                         <Name>jack</Name>
                     </asset>
                     <asset>
                         <Category>cat2</Category>
                         <Installed>Yes</Installed>
                         <Name>julie</Name>
                     </asset>
                     <asset>
                         <Category>cat3</Category>
                         <Installed>Yes</Installed>
                         <Name>linda</Name>
                     </asset>
                     <asset>
                         <Category>cat4</Category>
                         <Installed>No</Installed>
                         <Name>jake</Name>
                     </asset>
                     <asset>
                         <Category>cat5</Category>
                         <Installed>Yes</Installed>
                         <Name>paul</Name>
                     </asset>
                 </assets>
    
            'populate list
            assets = New List(Of Asset)
            For Each el As XElement In xe.Elements
                Dim foo As New Asset(el)
                assets.Add(foo)
            Next
    
            'examine each asset
            For Each a As Asset In assets
                Select Case a.Category
                    Case "cat1"
                        If a.Installed Then
    
                        End If
                    Case "cat2"
                    Case "cat3"
                    Case "cat4"
                    Case "cat5"
                    Case Else
                        Stop 'error
                End Select
            Next
        End Sub
    


    Search Documentation

    SerialPort Info

    Multics - An OS ahead of its time.

     "Those who use Application.DoEvents have no idea what it does

        and those who know what it does never use it."    former MSDN User JohnWein

    Monday, November 30, 2020 2:17 PM