Results 1 to 20 of 20

Thread: Make and combine a list of textboxes with a list of another object into one XML file

  1. #1

    Thread Starter
    Member
    Join Date
    Sep 2017
    Location
    Port St Lucie, Florida, United States
    Posts
    51

    Make and combine a list of textboxes with a list of another object into one XML file

    The first window of VB.Net code (below) is for creating moveable and resizable textboxes on a Windows Form1. The form needs a Button1. It all seems to work. The user clicks on the button to create as many textboxes as needed and moves them around.

    My aim is to create a list of all the textboxes that the user creates. I want to be able to serialize this list and save it into an XML file (and later deserialize it and open it again with all the textboxes reappearing on the form in their correct positions and sizes, etc.

    I have some code for making this list of textboxes (kindly offered by 4x2y on this forum). But I'm not sure how to integrate it into the first window of code below to make it work. The code I'm trying to use and understand is in the second window of code given below.

    Ultimately, I'm aiming to combine a list of textboxes with another list of objects. These other objects will be arrows that the user has drawn. (The user will be making arrow diagrams using textboxes and arrows.) I am able to make a list of all the arrows that the user makes, and I can serialize that list into an XML file and save it. So I can do that, and the code for that is in the third window of code below.

    I want to be able to create a list of textboxes that can be combined with that same XML file of arrows, so the user can save the whole diagram of arrows plus textboxes into one XML file, which can be read and opened for later use.

    Thank you for any help!


    Code:
    'Option Strict On
    Option Explicit On
    'Imports System.IO
    'Imports System.Xml
    'Imports System.Xml.Serialization
    
    Module OBJ_Resize
    
        Private AlowHS As Boolean = False
        Private AlowVS As Boolean = False
        Private AlowVHS As Boolean = False
        Private MXtmp As Integer = 0
        Private MYtmp As Integer = 0
        Private GMXtmp As Integer = 0
        Private GMYtmp As Integer = 0
    
        Function ResizeOBJ(ByRef Sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs, ByVal formX As Form) As Boolean
            If e.Button = Windows.Forms.MouseButtons.None Then
    
                If e.Y > Sender.Height - 10 And e.Y < Sender.Height And
                    e.X > Sender.Width - 10 And e.X < Sender.Width Then
                    Sender.Cursor = Cursors.SizeNWSE
                    AlowHS = False
                    AlowVS = False
                    AlowVHS = True
                ElseIf e.Y > Sender.Height - 10 And e.Y < Sender.Height And
                    e.X < Sender.Width - 20 Then
                    Sender.Cursor = Cursors.SizeNS
                    AlowVS = True
                    AlowHS = False
                    AlowVHS = False
                ElseIf e.X > Sender.Width - 10 And e.X < Sender.Width And
                    e.Y < Sender.Height - 20 Then
                    Sender.Cursor = Cursors.SizeWE
                    AlowHS = True
                    AlowVS = False
                    AlowVHS = False
                Else
                    If Sender.Cursor <> Cursors.Hand Then
                        Sender.Cursor = Cursors.Hand
                    End If
                    AlowHS = False
                    AlowVS = False
                    AlowVHS = False
                End If
            End If
    
            If e.Button = Windows.Forms.MouseButtons.Left Then
                If Cursor.Position.X < formX.Left + formX.Width And Cursor.Position.Y < formX.Top + formX.Height Then
                    If AlowHS = True Then
                        If Sender.Width > 20 Then
                            Sender.Width = Sender.Width - (MXtmp - e.X)
                        Else
                            Sender.Width += 1
                        End If
                    ElseIf AlowVS = True Then
                        If Sender.Height > 20 Then
                            Sender.Height = Sender.Height - (MYtmp - e.Y)
                        Else
                            Sender.Height += 1
                        End If
                    ElseIf AlowVHS = True Then
                        If Sender.Height > 20 And Sender.Width > 20 Then
                            Sender.Width = Sender.Width - (MXtmp - e.X)
                            Sender.Height = Sender.Height - (MYtmp - e.Y)
                        Else
                            Sender.Height += 1
                            Sender.Width += 1
                        End If
                    Else
                        If Sender.Top > 0 And Sender.Top < formX.Height - 50 Then
                            Sender.Top = Sender.Top - (GMYtmp - Cursor.Position.Y)
                        Else
                            If Sender.Top <= 0 Then
                                Sender.Top += 1
                            Else
                                Sender.Top -= 1
                            End If
                        End If
                        If Sender.Left > 0 And Sender.Left < formX.Width - 30 Then
                            Sender.Left = Sender.Left - (GMXtmp - Cursor.Position.X)
                        Else
                            If Sender.Left <= 0 Then
                                Sender.Left += 1
                            Else
                                Sender.Left -= 1
                            End If
                        End If
                    End If
                End If
            End If
            MXtmp = e.X
            MYtmp = e.Y
            GMXtmp = Cursor.Position.X
            GMYtmp = Cursor.Position.Y
            Return 0
        End Function
    End Module
    
    Public Class Form1
        Private saveWork As Boolean
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Width = 1000
            Height = 800
    
        End Sub
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim TextBox1 As New TextBox()
            With TextBox1
                TextBox1.Width = 100
                TextBox1.Height = 50
                TextBox1.Location = New Point(330, 60)
                TextBox1.Multiline = True
                Dim Arial As New Font(TextBox1.Font.FontFamily, 10, FontStyle.Regular)
                TextBox1.Font = Arial
                TextBox1.Name = "TextBox1"
                TextBox1.ScrollBars = 0
            End With
            Controls.Add(TextBox1)
            TextBox1.BringToFront()
            AddHandler TextBox1.MouseMove, AddressOf TextBox1_MouseMove
        End Sub
    
        Private Sub TextBox1_MouseMove(sender As Object, e As MouseEventArgs)
            OBJ_Resize.ResizeOBJ(sender, e, Me)
        End Sub
    Here is code for making a list of textboxes (or any control on a form). I don't know how to go about integrating this into what I want to do.

    Code:
    Public Class MyFormLayout
        Private Const TableName As String = "Control"
    
        Public Shared Sub Save(frm As Form, strFile As String)
            Dim dt As New DataTable With {.TableName = TableName}
            Dim ds As New DataSet With {.DataSetName = "MyFormLayout"}
            ds.Tables.Add(dt)
            dt.Columns.Add("Type")
            dt.Columns.Add("Name")
            dt.Columns.Add("Text")
            dt.Columns.Add("Left")
            dt.Columns.Add("Top")
            dt.Columns.Add("Width")
            dt.Columns.Add("Height")
            SaveProperties(frm, ds)
            ds.WriteXml(strFile)
        End Sub
    
        Private Shared Sub SaveProperties(ByVal cParent As Control, ds As DataSet)
            Dim ctl As Control
            Dim dr As DataRow
            For Each ctl In cParent.Controls
                dr = ds.Tables(0).NewRow
                dr("Type") = ctl.GetType.ToString
                dr("Name") = ctl.Name
                dr("Text") = ctl.Text
                dr("Left") = ctl.Left
                dr("Top") = ctl.Top
                dr("Width") = ctl.Width
                dr("Height") = ctl.Height
                ds.Tables(0).Rows.Add(dr)
                SaveProperties(ctl, ds)
            Next
        End Sub
    
        Public Shared Sub Read(frm As Form, strFile As String)
            Dim xDoc As New XmlDocument
            xDoc.Load(strFile)
            Dim xList As XmlNodeList = xDoc.SelectNodes("//" & TableName)
            For Each xNode As XmlNode In xList
                Select Case xNode.SelectSingleNode("Type").InnerText
                    Case "System.Windows.Forms.Label"
                        Dim ctl As Label = New Label
                        ReadProperties(ctl, xNode)
                        frm.Controls.Add(ctl)
                    Case "System.Windows.Forms.TextBox"
                        Dim ctl As TextBox = New TextBox
                        ReadProperties(ctl, xNode)
                        frm.Controls.Add(ctl)
                        '
                        'Add more cases for other Controls you want to read.
                        '
                End Select
            Next
        End Sub
    
        Private Shared Sub ReadProperties(ByVal ctl As Control, xNode As XmlNode)
            ctl.Name = xNode.SelectSingleNode("Name").InnerText
            ctl.Text = xNode.SelectSingleNode("Text").InnerText
            ctl.Top = CInt(xNode.SelectSingleNode("Top").InnerText)
            ctl.Left = CInt(xNode.SelectSingleNode("Left").InnerText)
            ctl.Width = CInt(xNode.SelectSingleNode("Width").InnerText)
            ctl.Height = CInt(xNode.SelectSingleNode("Height").InnerText)
            ctl.Visible = True
        End Sub
    
    End Class
    Below is code for drawing arrows and serializing the list of arrows into an XML file that can be saved and opened later.

    Code:
    Option Strict On
    Option Explicit On
    Imports System.IO
    Imports System.Xml.Serialization
    
    Public Class Form1
    
        Private myPen As Pen = New Pen(Drawing.Color.Black) With {.Width = 2,
            .CustomEndCap = New Drawing2D.AdjustableArrowCap(6, 6, False)}
        Private drawStartArrow As Boolean
        Private listStartCount As Integer
        Private listEndCount As Integer
        Private Arrow As New ReasonArrows
        Private chkArrow As New ReasonArrows
        Private listArrows As New List(Of ReasonArrows)
        Private saveWork As Boolean = True
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Height = 800
            Width = 1000
        End Sub
    
        Private Sub Form1_MouseDown(sender As Object, e As MouseEventArgs) Handles Me.MouseDown
            Me.DoubleBuffered() = True
            If e.Button = MouseButtons.Left And Cursor = Cursors.Cross Then
                Arrow.StartPoint = e.Location
            ElseIf e.Button = MouseButtons.Left And Cursor = Cursors.SizeAll Then
                For checkPoint As Integer = 0 To listArrows.Count - 1
                    If Math.Abs(e.X - listArrows(checkPoint).StartPoint.X) < 5 And
                       Math.Abs(e.Y - listArrows(checkPoint).StartPoint.Y) < 5 Then
                        Cursor.Position = PointToScreen(listArrows(checkPoint).StartPoint)
                        listArrows(checkPoint).StartPoint = New Point(e.X, e.Y)
                        listStartCount = checkPoint
                        drawStartArrow = True
                    ElseIf Math.Abs(e.X - listArrows(checkPoint).EndPoint.X) < 5 And
                    Math.Abs(e.Y - listArrows(checkPoint).EndPoint.Y) < 5 Then
                        Cursor.Position = PointToScreen(listArrows(checkPoint).EndPoint)
                        listArrows(checkPoint).EndPoint = New Point(e.X, e.Y)
                        listEndCount = checkPoint
                        drawStartArrow = False
                    End If
                Next
            End If
        End Sub
    
        Private Sub Form1_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
            Me.DoubleBuffered() = True
            If e.Button = MouseButtons.Left And Cursor = Cursors.Cross Then
                Arrow.EndPoint = e.Location
                Me.Invalidate()
            ElseIf e.Button = MouseButtons.Left And Cursor = Cursors.SizeAll And drawStartArrow = True Then
                listArrows(listStartCount).StartPoint = e.Location
                Invalidate()
            ElseIf e.Button = MouseButtons.Left And Cursor = Cursors.SizeAll And drawStartArrow = False Then
                listArrows(listEndCount).EndPoint = e.Location
                Invalidate()
            ElseIf Cursor = Cursors.Arrow And Not e.Button = MouseButtons.Left Then
                For checkPoint As Integer = 0 To listArrows.Count - 1
                    If Math.Abs(e.X - listArrows(checkPoint).StartPoint.X) < 5 And
                        Math.Abs(e.Y - listArrows(checkPoint).StartPoint.Y) < 5 Then
                        chkArrow.StartPoint = listArrows(checkPoint).StartPoint
                        Cursor = Cursors.SizeAll
                    ElseIf Math.Abs(e.X - listArrows(checkPoint).EndPoint.X) < 5 And
                        Math.Abs(e.Y - listArrows(checkPoint).EndPoint.Y) < 5 Then
                        chkArrow.EndPoint = listArrows(checkPoint).EndPoint
                        Cursor = Cursors.SizeAll
                    End If
                Next
            ElseIf Cursor = Cursors.SizeAll And Not e.Button = MouseButtons.Left Then
                If (e.X >= chkArrow.StartPoint.X + 5 Or e.X <= chkArrow.StartPoint.X - 5 Or
                e.Y >= chkArrow.StartPoint.Y + 5 Or e.Y <= chkArrow.StartPoint.Y - 5) And
                (e.X >= chkArrow.EndPoint.X + 5 Or e.X <= chkArrow.EndPoint.X - 5 Or
                e.Y >= chkArrow.EndPoint.Y + 5 Or e.Y <= chkArrow.EndPoint.Y - 5) Then
                    Cursor = Cursors.Arrow
                End If
            End If
        End Sub
    
        Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
            e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
            If Cursor = Cursors.Cross Then
                e.Graphics.DrawLine(myPen, Arrow.StartPoint, Arrow.EndPoint)
            End If
            For Each ArrowToDraw In listArrows
                e.Graphics.DrawLine(myPen, ArrowToDraw.StartPoint, ArrowToDraw.EndPoint)
            Next
        End Sub
    
        Private Sub Form1_MouseUp(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
            If Cursor = Cursors.Cross And e.Button = MouseButtons.Left Then
                listArrows.Add(New ReasonArrows With {.StartPoint = Arrow.StartPoint, .EndPoint = Arrow.EndPoint})
                Cursor = Cursors.Arrow
                saveWork = False
            End If
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Cursor = Cursors.Cross
        End Sub
    
        Private Sub SaveAsToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles SaveAsToolStripMenuItem.Click
            With SaveFileDialog1
                .InitialDirectory = "C:\"
                .Title = "Specify Destination Filename"
                .Filter = "XML Files (*.xml)|*.xml|Text Files (*.txt)|*.txt"
                .FilterIndex = 1
                .FileName = "Your File Name"
                .OverwritePrompt = True
                .CreatePrompt = True
                If .ShowDialog() = Windows.Forms.DialogResult.OK Then
                    Dim objStreamWriter As New StreamWriter(SaveFileDialog1.FileName)
                    Dim xmlSerialize As New XmlSerializer(listArrows.GetType) 'Determine what object types are present
                    xmlSerialize.Serialize(objStreamWriter, listArrows)
                    objStreamWriter.Close()
                    .Dispose()
                    saveWork = True
                End If
            End With
        End Sub
    
        Private Sub OpenToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles OpenToolStripMenuItem.Click
            If saveWork = False Then
                MessageBox.Show("Just a reminder -- you haven't SAVED your current work yet.")
            End If
            With OpenFileDialog1
                .InitialDirectory = "C:\"
                .Title = "Select a File"
                .FileName = "Select a File"
                .Filter = "XML Files (*.xml)|*.xml|Text Files (*.txt)|*.txt"
                .FilterIndex = 1
                If .ShowDialog() = Windows.Forms.DialogResult.OK Then
                    listArrows.Clear()
                    Dim objStreamReader As New StreamReader(OpenFileDialog1.FileName) 'Read File
                    Dim serializer As New XmlSerializer(GetType(List(Of ReasonArrows)))
                    listArrows = CType(serializer.Deserialize(objStreamReader), List(Of ReasonArrows))
                    Me.Invalidate()
                    objStreamReader.Close()
                    .Dispose()
                End If
            End With
        End Sub
    End Class
    
    Public Class ReasonArrows
        Public Property StartPoint As Point
        Public Property EndPoint As Point
    End Class

  2. #2
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    While you can only serialise one object, that object can be rather complex.

    At the moment you are serialising listArrows, which is already a complex object: it is a List(Of ReasonArrows) , and each ReasonArrows item has two sub-objects (both of which have a data type of Point).


    What you can do is create another Class just to represent the data you want to save, which would contain a List(Of ReasonArrows) , plus any other data you want to save.

    In the code you showed you are using a DataTable and a DataSet for the controls, and I think that either of those data types could be saved... but I assume you are only using them for the saving ability that the DataSet gives you, and it would be more efficient (and a bit simpler) to save a List(Of Control) instead, as you could use simpler version of the code in SaveProperties to copy items to the list.

    One possible way to set it up:
    Code:
    Public Class DataToSerialize
        Public Arrows as List(Of ReasonArrows)
        Public Controls as List(Of Control)
    End Class
    Code:
                    Dim dataToSave as New DataToSerialize
                    dataToSave.Arrows = listArrows
                    dataToSave.Controls = New List(Of Control)
                    For Each ctl as Control In cParent.Controls
                        dataToSave.Controls.Add(ctl)
                    Next
                    Dim xmlSerialize As New XmlSerializer(dataToSave.GetType) 'Determine what object types are present
                    xmlSerialize.Serialize(objStreamWriter, dataToSave)

  3. #3
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    After a bit more thinking, while that code for saving the controls is very simple, it is also saving a lot more properties than you really care about, and will therefore take much more storage space.

    What would be more appropriate would be creating a new Class containing the properties you want (TypeName, Name, Text, Left, Top, Width, Height), and use something similar to your SaveProperties routine to put the control details into the list.

  4. #4
    PowerPoster
    Join Date
    Sep 2006
    Location
    Egypt
    Posts
    2,579

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    Quote Originally Posted by dwigley View Post
    I have some code for making this list of textboxes (kindly offered by 4x2y on this forum). But I'm not sure how to integrate it into the first window of code below to make it work.
    To save TextBoxes call
    VB.NET Code:
    1. MyFormLayout.Save(Me, "your_file_name")

    To read call
    VB.NET Code:
    1. MyFormLayout.Read(Me, "your_file_name")

    Until you find a solution to save both list in one XML file, you can read/save using different two files like this

    vb.net Code:
    1. Private Sub SaveAsToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles SaveAsToolStripMenuItem.Click
    2.         With SaveFileDialog1
    3.             .InitialDirectory = "C:\"
    4.             .Title = "Specify Destination Filename"
    5.             .Filter = "XML Files (*.xml)|*.xml|Text Files (*.txt)|*.txt"
    6.             .FilterIndex = 1
    7.             .FileName = "Your File Name"
    8.             .OverwritePrompt = True
    9.             .CreatePrompt = True
    10.             If .ShowDialog() = Windows.Forms.DialogResult.OK Then
    11.                 Dim objStreamWriter As New StreamWriter(SaveFileDialog1.FileName)
    12.                 Dim xmlSerialize As New XmlSerializer(listArrows.GetType) 'Determine what object types are present
    13.                 xmlSerialize.Serialize(objStreamWriter, listArrows)
    14.                 objStreamWriter.Close()
    15.                 .Dispose()
    16.                 saveWork = True
    17.  
    18.                 'Save TextBoxes with auto generated file name
    19.                 MyFormLayout.Save(Me, GetTextBoxFileName(SaveFileDialog1.FileName))
    20.             End If
    21.         End With
    22.     End Sub
    23.  
    24.     Private Sub OpenToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles OpenToolStripMenuItem.Click
    25.         If saveWork = False Then
    26.             MessageBox.Show("Just a reminder -- you haven't SAVED your current work yet.")
    27.         End If
    28.         With OpenFileDialog1
    29.             .InitialDirectory = "C:\"
    30.             .Title = "Select a File"
    31.             .FileName = "Select a File"
    32.             .Filter = "XML Files (*.xml)|*.xml|Text Files (*.txt)|*.txt"
    33.             .FilterIndex = 1
    34.             If .ShowDialog() = Windows.Forms.DialogResult.OK Then
    35.                 listArrows.Clear()
    36.                 Dim objStreamReader As New StreamReader(OpenFileDialog1.FileName) 'Read File
    37.                 Dim serializer As New XmlSerializer(GetType(List(Of ReasonArrows)))
    38.                 listArrows = CType(serializer.Deserialize(objStreamReader), List(Of ReasonArrows))
    39.                 Me.Invalidate()
    40.                 objStreamReader.Close()
    41.                 .Dispose()
    42.  
    43.                 'Read TextBoxes with auto generated file name
    44.                 MyFormLayout.Read(Me, GetTextBoxFileName(OpenFileDialog1.FileName))
    45.             End If
    46.         End With
    47.     End Sub
    48.  
    49.     Private Function GetTextBoxFileName(strBaseName As String) As String
    50.         Dim d As String = IO.Path.GetDirectoryName(strBaseName)
    51.         Dim f As String = IO.Path.GetFileNameWithoutExtension(strBaseName)
    52.         Dim e As String = IO.Path.GetExtension(strBaseName)
    53.         Return IO.Path.Combine(d, f & "_textbox" & e)
    54.     End Function



  5. #5

    Thread Starter
    Member
    Join Date
    Sep 2017
    Location
    Port St Lucie, Florida, United States
    Posts
    51

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    4y2x, that is really incredible! Thanks. That's almost a solution. I think this could be acceptable until I can find a way to make one list of everything and save into one XML file.

    One problem. When I reload the arrows and textboxes, the textboxes are no longer movable and resizable. They reload in the correct positions, but they are at a default size and the Resize function I have is not applying to them. Is there a way to reload the textboxes so they can continue to be resized and moved? That's something that I have to be able to do with the textboxes and arrows, make them so they are still movable and resizable. The arrows all load fine, and I can move them.

    Thanks for the help!

  6. #6

    Thread Starter
    Member
    Join Date
    Sep 2017
    Location
    Port St Lucie, Florida, United States
    Posts
    51

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    So si_the_geek, I think I get the idea. I will make a new class with two properties that will contain the list of arrows and the list of textboxes. Then I will serialize that class.

    But you say that I should make a list of just the textbox properties that I want, and I do that by creating a class that has just the textbox properties I want. Is that correct?

    Now if I do it this way, when I reload the XML file, will the arrows and textboxes load so that they will all still be movable and resizable?

    Thanks for the help.

  7. #7
    PowerPoster
    Join Date
    Sep 2006
    Location
    Egypt
    Posts
    2,579

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    Quote Originally Posted by dwigley View Post
    One problem. When I reload the arrows and textboxes, the textboxes are no longer movable and resizable. They reload in the correct positions, but they are at a default size and the Resize function I have is not applying to them. Is there a way to reload the textboxes so they can continue to be resized and moved? That's something that I have to be able to do with the textboxes and arrows, make them so they are still movable and resizable. The arrows all load fine, and I can move them.

    Thanks for the help!
    In OpenToolStripMenuItem_Click replace
    vb.net Code:
    1. 'Read TextBoxes with auto generated file name
    2.                 MyFormLayout.Read(Me, GetTextBoxFileName(OpenFileDialog1.FileName))
    with

    vb.net Code:
    1. 'Read TextBoxes with auto generated file name
    2.                 MyFormLayout.Read(Me, GetTextBoxFileName(OpenFileDialog1.FileName))
    3.                 For Each txt As TextBox In Me.Controls.OfType(Of TextBox)
    4.                     If txt.Name = "TextBox1" Then
    5.                         'Note: "TextBox1" is the name you have set when creating textboxes in Button1_Click event
    6.                         ' I do this check in order to not affect other textboxes (which are not added by the user)
    7.                         txt.Multiline = True ' I set here because MyFormLayout class don't save Multiline property. (add it yourself if you want)
    8.                         AddHandler txt.MouseMove, AddressOf TextBox1_MouseMove
    9.                     End If
    10.                 Next



  8. #8

    Thread Starter
    Member
    Join Date
    Sep 2017
    Location
    Port St Lucie, Florida, United States
    Posts
    51

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    4x2y, I can't thank you enough for this help. It works fine. It's really something. I can save the arrows and textboxes into two files. There are two files, but I only have to load one to get both loaded. I'm starting to understand the code. I'll have to study it. I can put other controls on the forms too. It's very useful code -- to be able to read all of the controls on a form.

    Now I can work on figuring out how to combine everything into one file. I'll start with si_the_geek's suggestions.

    Thank you again!

  9. #9
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    Quote Originally Posted by dwigley View Post
    So si_the_geek, I think I get the idea. I will make a new class with two properties that will contain the list of arrows and the list of textboxes. Then I will serialize that class.

    But you say that I should make a list of just the textbox properties that I want, and I do that by creating a class that has just the textbox properties I want. Is that correct?
    Correct

    Now if I do it this way, when I reload the XML file, will the arrows and textboxes load so that they will all still be movable and resizable?
    You need to add the kind of code 4x2y showed in post #7, but then they should.


    Here is an untested example of the saving:
    Code:
    Public Class DataToSerialize
        Public Arrows as List(Of ReasonArrows)
        Public Controls as List(Of ControlToSerialize)
    End Class
    
    Public Class ControlToSerialize
      TypeName as String
      Name as String
      Text as String
      Left as Integer
      Top as Integer
      Width as Integer
      Height as Integer
    End Class
    Code:
        Private Shared Function GetControlsToSerialize(ByVal cParent As Control) As List(of ControlToSerialize)
            Dim retValue As New List(of ControlToSerialize)
            For Each ctl As Control In cParent.Controls
                CurrentControl = New ControlToSerialize 
                CurrentControl.TypeName = ctl.GetType.ToString
                CurrentControl.Name = ctl.Name
                CurrentControl.Text = ctl.Text
                CurrentControl.Left = ctl.Left
                CurrentControl.Top = ctl.Top
                CurrentControl.Width = ctl.Width
                CurrentControl.Height = ctl.Height
                retValue.Add(CurrentControl)
            Next
            Return retValue
        End Sub
    Code:
                    Dim dataToSave as New DataToSerialize
                    dataToSave.Arrows = listArrows
                    dataToSave.Controls = GetControlsToSerialize(frm)
                    Dim xmlSerialize As New XmlSerializer(dataToSave.GetType) 'Determine what object types are present
                    xmlSerialize.Serialize(objStreamWriter, dataToSave)

  10. #10

    Thread Starter
    Member
    Join Date
    Sep 2017
    Location
    Port St Lucie, Florida, United States
    Posts
    51

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    Ok si_the_geek for the help, I appreciate it very much. I'll get coding!

  11. #11

    Thread Starter
    Member
    Join Date
    Sep 2017
    Location
    Port St Lucie, Florida, United States
    Posts
    51

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    geek, I was successful at saving an XML file using your suggestions (above) about making one class (DataToSerialize) that contains all of the data I want to save. But I'm unable to write the code to read the XML file back in. Do you have any suggestions on that? Thanks.

  12. #12
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    The code at the end of post #9 (or rather your current code) is the equivalent of this Save code from post #1:
    Quote Originally Posted by dwigley View Post
    Code:
                    Dim objStreamWriter As New StreamWriter(SaveFileDialog1.FileName)
                    Dim xmlSerialize As New XmlSerializer(listArrows.GetType) 'Determine what object types are present
                    xmlSerialize.Serialize(objStreamWriter, listArrows)
                    objStreamWriter.Close()
    So what you need to do is determine the equivalent of the Open code from post #1:
    Code:
                    listArrows.Clear()
                    Dim objStreamReader As New StreamReader(OpenFileDialog1.FileName) 'Read File
                    Dim serializer As New XmlSerializer(GetType(List(Of ReasonArrows)))
                    listArrows = CType(serializer.Deserialize(objStreamReader), List(Of ReasonArrows))
                    objStreamReader.Close()
    As you have saved using the data type DataToSerialize, that is what you need to use to load it (in place of List(Of ReasonArrows) ), and once you have it in a variable you can then use the properties to set the listArrows variable.

    In terms of the controls you need to work out the opposite of GetControlsToSerialize, using the same ideas presented in this thread (including the AddHandler part). It should be a Sub rather than a Function, and the List should be passed in as a parameter along with the parent.

    Hopefully that will be enough, but if you get stuck post what you come up with.

  13. #13

    Thread Starter
    Member
    Join Date
    Sep 2017
    Location
    Port St Lucie, Florida, United States
    Posts
    51

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    Thanks for the help si_the_geek. I have one last problem -- loading the textboxes. The code in the first window gives the three classes of objects I'm using. "ArrowObject" replaces the name "ReasonArrows." It all seems to be correct.

    Code:
    Public Class ArrowObject
        Public Property StartPoint As Point
        Public Property EndPoint As Point
        Public Property Dash As Boolean
        Public Property LinkLine As Boolean
        Public Property CondLine As Boolean
    End Class
    
    Public Class Control
        Public Property TypeName As String
        Public Property Name As String
        Public Property Text As String
        Public Property Left As Integer
        Public Property Top As Integer
        Public Property Width As Integer
        Public Property Height As Integer
    End Class
    
    Public Class DataToSerialize
        Public Property AllArrows As List(Of ArrowObject)
        Public Property ControlValues As List(Of Control)
    End Class

    The code in this second window is my "GetControlsToSerialize." It seems to work fine. The If clause is making sure I'm just getting the textboxes that I need, which all have the same name "TextBox1."

    Code:
    Private Shared Function GetControlsToSerialize(ByVal cParent As Windows.Forms.Control) As List(Of Control)
            Dim AllValues As New List(Of Control)
            For Each ctl As Windows.Forms.Control In cParent.Controls
                If ctl.Name = "TextBox1" Then
                    Dim CurrentControl As New Control
                    With CurrentControl
                        CurrentControl.TypeName = ctl.GetType.ToString
                        CurrentControl.Name = ctl.Name
                        CurrentControl.Text = ctl.Text
                        CurrentControl.Left = ctl.Left
                        CurrentControl.Top = ctl.Top
                        CurrentControl.Width = ctl.Width
                        CurrentControl.Height = ctl.Height
                        AllValues.Add(CurrentControl)
                    End With
                End If
            Next
            Return AllValues
        End Function
    The code here in the third window serializes DataToSerialize and puts it all into an XML file. It seems to work fine.

    Code:
    If .ShowDialog() = Windows.Forms.DialogResult.OK Then
                    Dim objStreamWriter As New StreamWriter(SaveFileDialog1.FileName)
                    Dim dataToSave As New DataToSerialize With {
                        .AllArrows = listArrows,
                        .ControlValues = GetControlsToSerialize(Me)
                    }
                    Dim xmlSerialize As New XmlSerializer(dataToSave.GetType)
                    xmlSerialize.Serialize(objStreamWriter, dataToSave)
                    objStreamWriter.Close()
                End If
    This code below deserializes the listArrows. It seems to work. I can reload all of the arrow objects that I have, and I can move them and resize them.

    Code:
    If .ShowDialog() = Windows.Forms.DialogResult.OK Then
                    listArrows.Clear()
                    Dim objStreamReader As New StreamReader(OpenFileDialog1.FileName) 'Read File
                    Dim serializer As New XmlSerializer(GetType(DataToSerialize))
                    Dim dataToRead As New DataToSerialize
                    dataToRead = CType(serializer.Deserialize(objStreamReader), DataToSerialize)
                    listArrows = dataToRead.AllArrows
                    Me.Invalidate()
                    objStreamReader.Close()
                End If
    The last thing I have to do is deserialize the textboxes in the XML file and load them. The other property of the DataToSerialize variable would be "GetControlsToSerialize." I don't really know how to go about doing the "opposite" of "GetControlsToSerialize" as you suggest. I need to reconstruct a list of "Me.Controls" from the XML File. Is that right? I'm stumped.

    One other thing is that before I load anything, as you can see, I clear the arrow objects off the form with "listArrows.Clear()." But I will also need to clear the textboxes off the form too. Is there a way to do that? Thanks again.

  14. #14
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    That code all seems OK, but I would recommend changing the name of the class Control a bit because there is already a class called Control, and it could cause confusion (in people, and possibly the compiler).

    GetControlsToSerialize loops thru the controls, and copies the property values to a list to be saved... so the opposite would be looping thru the list of saved data, and adding controls to the form using code very similar to Button1_Click in post #1 (but using the values from the list, rather than hard-coded ones).

    eg:
    Code:
        Private Shared Sub DeSerializeControls(ByVal cParent As Windows.Forms.Control, controls As List(Of Control))
            For Each ctl As Control in controls 
                'add control here, using the properties of ctl
            Next
        End Sub
    Code:
    listArrows = dataToRead.AllArrows
    DeSerializeControls(Me, dataToRead.ControlValues)

    Quote Originally Posted by dwigley View Post
    One other thing is that before I load anything, as you can see, I clear the arrow objects off the form with "listArrows.Clear()." But I will also need to clear the textboxes off the form too. Is there a way to do that? Thanks again.
    That's a good point... my memory is a bit hazy as it has been so long since I've done it, but I think it should be something like this:
    Code:
    Dim CopyOfControls = Controls.Clone
       For Each ControltoRemove in CopyOfControls
         If ControltoRemove.Name = "TextBox1" Then
            RemoveHandler ControltoRemove.MouseMove, AddressOf TextBox1_MouseMove
            Controls.Remove(ControltoRemove)
            ControltoRemove.Dispose()
         End If
      Next

  15. #15
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    @dwigley: you aren't doing anything wrong, you ask your questions well! But here's the problem with this thread and how it's hurting you.

    The question is, "How do I save a very specific thing to XML?" So you got an answer. But it was a very specific answer. So when the problem changed to, "How do I save a different, specific thing to XML?" you had to ask again. Then you got a specific answer. Then the problem became, "How do I save both things to one file?" and here we are, making specific answers again.

    The correct "answer" goes like this:
    1. Here is how to save *anything* to XML.
    2. "Anything" can be a class with multiple things in it.

    If you had learned those two answers, you would've been finished on October 18th. When someone gives you a copy/pastable example, try to understand it, and ask them to explain it if they didn't. If they don't explain it, then you should assume they don't know either. Do you want advice from people who can't explain it? I sure don't.

    So now I'll take a crack at it.

    What I'm going to show you is how to save ANYTHING to XML. It's a little more complicated than the simplest examples. But this technique works EVERY time for ANY object, so if you always do it this way you never have to ask a question again.

    You start by making a class for the data. People make a big dang deal about how complex classes are but they are part of the ABCs of Visual Basic. This is what your class will look like at first:
    Code:
    Public Class MyApplicationData
    
    End Class
    See? That's what people make a big deal about. It's going to get more complicated, but programming is like socks: you don't try to put on both socks at the same time, you put them on one at a time. That's not very exciting XML, so we need to grab another sock.

    Say you want to save information about your controls. Keep a notebook next to your computer for this purpose: open to a blank page and write: "What do I need to save from a Control?" on the top line. Think about a Control. It has at least 30 properties. Do you need to save EVERY property? Probably not. I bet if you are trying to save a form's layout, you want the Name, Location, Size, and Text. Let's go with that set. The only not-obvious one is Name. Why are we saving it? Well, it's a good way to uniquely identify Controls, so it might be convenient to keep it around. I just noticed you save a "Type" to know how to create it later, so let's save that. We're going to make a Class to represent this:
    Code:
    Public Class SavedControl
        Public Property Name As String
        Public Property Location As Point
        Public Property Size As Size
        Public Property Text As String
        Public Property ControlType As String
    End Class
    That's useful if we want to save ONE Control. We want to save MANY. So we should have a List(Of SavedControl) somewhere. That belongs on our main class:
    Code:
    Public Class MyApplicationData
    
        Public Property Controls As List(Of SavedControl)
    
    End Class
    This has a tiny problem. List(Of T) is a class, so it'll be Nothing when we make a new instance of this type. So let's grab another sock and fix that problem with a constructor. That's a fancy word for Sub New:
    Code:
    Public Class MyApplicationData
    
        Public Property Controls As List(Of SavedControl)
    
        Public Sub New()
            Controls = New List(Of SavedControl)()
        End Sub
    
    End Class
    Now I'm sure you want to play with this, so let's go ahead and explain how to do that.

    The code you have uses a DataSet, which is a different shortcut some people use for saving XML. I'm using objects, which have their own shortcut and are (in my opinion) a tradeoff that is "harder to set up" but "easier to use". My technique works if you want to make binary files, XML files, plain text files, JSON files, databases, or serialization formats that haven't been written yet. DataSet works directly with XML and databases. When I want a solution that "works every time" I like to use the most flexible solution. That's why I'm using objects.

    To save an object to XML, you need an XmlSerializer instance. There are a lot of options, but you only need those options when someone else writes the XML, makes bad decisions, and forces you to use their dumb XML. The only thing the XmlSerializer needs to know is what type you plan to serialize. That type is always the "root", so it will be MyApplicationData.

    To save data, you need to create a MyApplicationData, add all the data you want to save, then call the XmlSerializer.Serialize() method. That method wants a Stream, which is a clunky way to say "a file". There's some other overloads for more options, they only matter if someone's forcing you to use stupid XML.

    So a new version of the code to save your file would look something like:
    Code:
    Public Sub Save(input As Form, filePath As String)
        ' Gather all the data you want to save.
        Dim data = SaveProperties(input, data)
    
        ' Create the output file.
        Using output = File.OpenWrite(filePath)
            ' Make a serializer that can save the data.
            Dim serializer As New XmlSerializer(GetType(MyApplicationData))
            ' Save the data.
            serializer.Serialize(output, data)
        End Using
    End Sub
    
    Public Function SaveProperties(input As Form) As MyApplicationData
        Dim data As New MyApplicationData()
        For Each controlToSave As Control In input.Controls
            Dim controlData As New SavedControl With {
                .Name = controlToSave.Name,
                .Text = controlToSave.Text,
                .Location = controlToSave.Location,
                .Size = controlToSave.Size
                .ControlType = controlToSave.GetType().ToString()
            }
            data.Controls.Add(controlData)
        Next
    
        Return data
    End Function
    If you read through it, it looks REALLY similar to the DataSet approach. It looks shorter, because it doesn't have to set up the columns, but one could argue "making the class" is the same exercise. Whatever. Let's get another sock and read the properties.

    To read XML with this approach, you need another XmlSerializer. It still needs to know what type it is working with. Now you call Deserialize(). It wants a Stream, and will return the object it deserializes. Since it returns Object, we'll have to cast. No big deal.
    Code:
    Public Sub Read(target As Form, filePath As String)
        Dim data As MyApplicationData
    
        ' Open the file.
        Using input = File.OpenRead(filePath)
            ' Create a serializer.
            Dim serializer As New XmlSerializer(GetType(MyApplicationData))
            ' Read the object.
            data = DirectCast(serializer.Deserialize(input), MyApplicationData)
        End Using
    
        ' Use the object to update the form.
        SetProperties(target, data)
    End Sub
    
    Private Sub SetProperties(target As Form, data As MyApplicationData)
        For Each savedControl In data.SavedControls
            ' Create the right kind of control.
            Dim newControl = CreateControl(savedControl.ControlType)
            If newControl IsNot Nothing Then
                ' Set all the saved properties.
                newControl.Name = savedControl.Name
                newControl.Location = savedControl.Location
                newControl.Size = savedControl.Size
                newControl.Text = savedControl.Text
                
                ' Add the control to the form.
                target.Controls.Add(newControl)
            End If
        Next
    End Sub
            
    Private Function CreateControl(ByVal controlType As String) As Control
        Select Case controlType
            Case "System.Windows.Forms.Label"
                Return New Label()
            Case "System.Windows.Forms.TextBox"
                Return New TextBox
            '...
        End Case
    
        Return Nothing
    End Function
    This is the part where I feel you get "easier" code. You don't have to use XmlNode, which means no InnerText, no CInt(), etc. Everything's automatically converted to the right type for you. The less you have to think, the fewer mistakes you will make.

    Now you know how to "save anything to XML". As proof, let's extend it to save these Arrows of yours. I'm not going to use exactly your class, but it will be so close you should be able to figure out what to do. I think you'll be surprised how simple this is.

    Let's say we have this as the "Arrow" type:
    Code:
    Public Class Arrow
        Public Property Start As Point
        Public Property End As Point
    End Class
    (I see in your code this is what you have now, I'm too lazy to delete the last paragraph.)

    If you want to save these, you need to add them to MyApplicationData:
    Code:
    Public Class MyApplicationData
    
        Public Property Controls As List(Of SavedControl)
        Public Property Arrows As List(Of Arrow)
    
        Public Sub New()
            Controls = New List(Of SavedControl)()
            Arrows = New List(Of Arrow)()
        End Sub
    
    End Class
    Why didn't I make a SavedArrow class this time? Well, I wrote in my notebook, "What do I need to save from an Arrow?" It turned out I need every property, so there was not a good reason to make a new class. I make new classes when what I save is a little different from the original object. When there's no difference, I use the same object.

    Now you need to update your "save" code to be able to get it from your form. So you might have this in your form:
    Code:
    Public Class Form1
    
        ' [...]
    
        Public Property Arrows As List(Of Arrow)
    
        ' [...]
    
    End Class
    You need to change the parts of the Save code that get the data, and you need to change the parts of the Load code that populate it.

    Here's one problem though: if you want to save Arrows, that's not a property on Form. It's a property on Form1. So we may as well alter our save code to work ONLY with a Form1:
    Code:
    Public Sub Save(input As Form1, filePath As String)
        ' [...]
    End Sub
    
    Private Function SaveProperties(input As Form1) As MyApplicationData
        Dim data As New MyApplicationData()
        For Each controlToSave As Control In input.Controls
            ' [...]
        Next
    
        For Each arrowToSave As Arrow In input.Arrows
            data.Arrows.Add(arrowToSave)
        Next
    
        Return data
    End Function
    
    Public Sub Read(target As Form1, filePath As String)
        ' [...]
    End Sub
    
    Private Sub SetProperties(target As Form, data As MyApplicationData)
        For Each savedControl In data.SavedControls
            ' [...]
        Next
    
        For Each savedArrow In data.Arrows
            target.Arrows.Add(savedArrow)
        Next
    End Sub
    That's a very small change, isn't it?

    Once you set up the basic code for MyApplicationData, it turns out you follow exactly the same steps to add ANYTHING to what is saved:
    1. Make a class with the properties you want to save.
    2. Add a property to MyApplicationData that stores the instance(s) of the class.
    3. Update the Save() code to gather all of the data from the appropriate Form and put it in MyApplicationData.
    4. Update the Read() code to transfer the data from MyApplicationData back to the form.

    That's it, no real mystery. Once you get the parts with the XmlSerializer working, you never touch it again. You add properties to the MyApplication object and so long as you add the right things to Save() and Read() they update by magic.

    I prefer this to using a DataSet, but you could update the DataSet code to work. Here's a rough outline of THAT tutorial.
    • Each DataTable in a DataSet is basically a class. So where I say "make a class", you'd be "adding a new Table and setting up that table's columns".
    • Instead of "create instances of a class" you are now "adding rows to the appropriate table".
    • I don't know why the heck your example code manually parses XML instead of using DataSet.ReadXML(). I'd change the code to do that. If you don't, you have all the InnerText and casting nonsense.
    • But even if you use ReadXML(), each row is "a class" only you have to do work to cast from Object to the right type.

    Personally, DataSets feel clunky to me compared to classes, but whatever. It works, and there's not a strong reason to shy away other than 'someone just wrote the whole danged object tutorial for you'.

    Now you've got about 15 socks on each foot. Your feet will never be cold again. Which is why I started with a fishing analogy!
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  16. #16

    Thread Starter
    Member
    Join Date
    Sep 2017
    Location
    Port St Lucie, Florida, United States
    Posts
    51

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    Eureka. Thanks si_the_geek for the big help on the ideas and code. It seems to all work now. I'll give some of the code below. To summarize: It combines (into one class) a class of drawn lines and arrows with another class of (movable) textboxes, so that they can be serialized and saved (wherever the user wants to save it) into a single XML file, which can be used later to reload the objects all back onto a form. The very hard part for me was getting the textboxes to save and reload, since they are built-in VB controls that I had to get into my class and then back out of my class and back onto the form again as its controls.

    Here are the classes.

    Code:
    Public Class ArrowObject
        Public Property StartPoint As Point
        Public Property EndPoint As Point
        Public Property Dash As Boolean
        Public Property LinkLine As Boolean
        Public Property CondLine As Boolean
    
    End Class
    
    Public Class TextBoxControl
        Public Property TypeName As String
        Public Property Name As String
        Public Property Text As String
        Public Property Left As Integer
        Public Property Top As Integer
        Public Property Width As Integer
        Public Property Height As Integer
        Public Property Multiline As Boolean
    End Class
    
    Public Class DataToSerialize
        Public Property AllArrows As List(Of ArrowObject)
        Public Property ControlValues As List(Of TextBoxControl)
    End Class
    Here is the code to get the textboxes off of the form and into my TextBoxControl class (and into a list) for serialization. The lines and arrows (ArrowObject) were collected elsewhere.

    Code:
    Private Shared Function GetControlsToSerialize(ByVal cParent As Windows.Forms.Control) As List(Of TextBoxControl)
            Dim AllValues As New List(Of TextBoxControl)
            For Each ctl As Windows.Forms.Control In cParent.Controls
                If ctl.Name = "TextBox1" Then
                    Dim CurrentControl As New TextBoxControl
                    With CurrentControl
                        CurrentControl.TypeName = ctl.GetType.ToString
                        CurrentControl.Name = ctl.Name
                        CurrentControl.Text = ctl.Text
                        CurrentControl.Left = ctl.Left
                        CurrentControl.Top = ctl.Top
                        CurrentControl.Width = ctl.Width
                        CurrentControl.Height = ctl.Height
                        CurrentControl.Multiline = True
                        AllValues.Add(CurrentControl)
                    End With
                End If
            Next
            Return AllValues
        End Function
    This is code for saving everything into one XML file using the DataToSerialize class.

    Code:
    Private Sub SaveAsToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles SaveAsToolStripMenuItem.Click
            With SaveFileDialog1
                .InitialDirectory = "C:\"
                .Title = "Specify Destination Filename"
                .Filter = "XML Files (*.xml)|*.xml|Text Files (*.txt)|*.txt"
                .FilterIndex = 1
                .FileName = "Your File Name"
                .OverwritePrompt = True
                .CreatePrompt = True
                If .ShowDialog() = Windows.Forms.DialogResult.OK Then
                    Dim objStreamWriter As New StreamWriter(SaveFileDialog1.FileName)
                    Dim dataToSave As New DataToSerialize With {
                        .AllArrows = listArrows,
                        .ControlValues = GetControlsToSerialize(Me)
                    }
                    Dim xmlSerialize As New XmlSerializer(dataToSave.GetType)
                    xmlSerialize.Serialize(objStreamWriter, dataToSave)
                    objStreamWriter.Close()
    This is code for deserializing the textboxes in the XML file and loading them onto the form.

    Code:
    Private Sub DeSerializeControls(ByVal cParent As Windows.Forms.Control, myTextBoxes As List(Of TextBoxControl))
            For Each ctl As TextBoxControl In myTextBoxes
                Dim TextBox1 As New TextBox
                With TextBox1
                    TextBox1.Name = ctl.Name
                    TextBox1.Text = ctl.Text
                    TextBox1.Left = ctl.Left
                    TextBox1.Top = ctl.Top
                    TextBox1.Width = ctl.Width
                    TextBox1.Height = ctl.Height
                    TextBox1.Multiline = ctl.Multiline
                End With
                Me.Controls.Add(TextBox1)
                AddHandler TextBox1.MouseMove, AddressOf TextBox1_MouseMove
            Next
    
        End Sub
    This is code for loading everything onto the form again.

    Code:
    Private Sub OpenToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles OpenToolStripMenuItem.Click
            If saveWork = False Then
                MessageBox.Show("Just a reminder -- you haven't SAVED your current work yet.")
            End If
            With OpenFileDialog1
                .InitialDirectory = "C:\"
                .Title = "Select a File"
                .FileName = "Select a File"
                .Filter = "XML Files (*.xml)|*.xml|Text Files (*.txt)|*.txt"
                .FilterIndex = 1
                If .ShowDialog() = Windows.Forms.DialogResult.OK Then
                    listArrows.Clear()
    
                    For repeat As Double = 0 To (Controls.Count) / 2
                        For Each TextB As TextBox In Controls().OfType(Of TextBox)
                            Controls.Remove(TextB)
                        Next
                    Next
    
                    'Another way to clear the textboxes.
                    'For repeat As Double = 0 To (Controls.Count) / 2
                    '    For Each ControltoRemove As Control In Controls
                    '        If ControltoRemove.Name = "TextBox1" Then
                    '            RemoveHandler ControltoRemove.MouseMove, AddressOf TextBox1_MouseMove
                    '            Controls.Remove(ControltoRemove)
                    '            ControltoRemove.Dispose()
                    '        End If
                    '    Next
                    'Next
    
                    Dim objStreamReader As New StreamReader(OpenFileDialog1.FileName) 'Read File
                    Dim serializer As New XmlSerializer(GetType(DataToSerialize))
                    Dim dataToRead As New DataToSerialize
                    dataToRead = CType(serializer.Deserialize(objStreamReader), DataToSerialize)
                    listArrows = dataToRead.AllArrows
                    DeSerializeControls(Me, dataToRead.ControlValues)
                    Me.Invalidate()
                    objStreamReader.Close()
    I realize that the code my not be the most correct, but it's working.

    geek, notice that there is some code that you gave me that is commented in this last window. It's for clearing the form of textboxes before loading the XML file. I made it work. But right above it you can see the code that I was using for doing the same thing. One crazy thing that I don't understand about it is that this code (and yours) only clears half of the textboxes. So to get all of the textboxes to clear, I had to loop it around a number of times equal to half the number of the textboxes on the form! That's why there is this line of code on both.

    Code:
    For repeat As Double = 0 To (Controls.Count) / 2
    Any ideas about this mystery? Thanks again for your great help.

  17. #17
    PowerPoster
    Join Date
    Sep 2006
    Location
    Egypt
    Posts
    2,579

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    Quote Originally Posted by dwigley View Post
    One crazy thing that I don't understand about it is that this code (and yours) only clears half of the textboxes. So to get all of the textboxes to clear, I had to loop it around a number of times equal to half the number of the textboxes on the form! That's why there is this line of code on both.

    Code:
    For repeat As Double = 0 To (Controls.Count) / 2
    Any ideas about this mystery? Thanks again for your great help.
    You can't do that using a For Each, but you can do using backward For...Next loop, so while you remove an element from the list, you move toward the first element and you will never skip any element. For example, when the first element removed, the second element becomes the first and the third element becomes the second thus in the next iteration, the third element removed instead of the second element and so on.

    vb.net Code:
    1. For j = Controls.Count - 1 To 0 Step -1
    2.             If Controls(j).Name = "TextBox1" Then
    3.                 RemoveHandler Controls(j).MouseMove, AddressOf TextBox1_MouseMove
    4.                 Controls.Remove(Controls(j))
    5.             End If
    6.         Next



  18. #18
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    That's it... I realised that using For Each on a collection while also removing from it wouldn't work properly, but somehow got confused and used .Clone to attempt to avoid it. Annoyingly I knew the right way, I just didn't think of it at the time.

    Ah well, you have the answer now

  19. #19

    Thread Starter
    Member
    Join Date
    Sep 2017
    Location
    Port St Lucie, Florida, United States
    Posts
    51

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    Thanks Spynne for putting all the time into explaining these things. It is very helpful for me, and I'm sure for many others too. Now that I have some experience doing it both ways, I think I prefer using the classes over the DataSet. As for fully understanding your explanation, it will take some time. I didn't know anything about programming until I tried to do this application. I was going good until I hit this XML Serialization stuff. I didn't want to, but I had to ask for help about it. The answer involved learning more than just knowing how to save anything into an XML file. It required, for instance, knowing how to make classes that combine classes of things. I had no idea that's what had to be done when I first asked about it. I'm very glad I went through this circuitous path. It didn't hurt me one bit. Thanks for all the good and thoughtful help!

  20. #20

    Thread Starter
    Member
    Join Date
    Sep 2017
    Location
    Port St Lucie, Florida, United States
    Posts
    51

    Re: Make and combine a list of textboxes with a list of another object into one XML f

    Ah, that's interesting how you can't use that using a For Each. Thanks.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width