-
Oct 18th, 2017, 11:07 PM
#1
Thread Starter
Member
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
-
Oct 19th, 2017, 11:30 AM
#2
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)
-
Oct 19th, 2017, 12:32 PM
#3
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.
-
Oct 19th, 2017, 04:27 PM
#4
Re: Make and combine a list of textboxes with a list of another object into one XML f
Originally Posted by dwigley
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:
MyFormLayout.Save(Me, "your_file_name")
To read call
VB.NET Code:
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:
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 'Save TextBoxes with auto generated file name MyFormLayout.Save(Me, GetTextBoxFileName(SaveFileDialog1.FileName)) 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() 'Read TextBoxes with auto generated file name MyFormLayout.Read(Me, GetTextBoxFileName(OpenFileDialog1.FileName)) End If End With End Sub Private Function GetTextBoxFileName(strBaseName As String) As String Dim d As String = IO.Path.GetDirectoryName(strBaseName) Dim f As String = IO.Path.GetFileNameWithoutExtension(strBaseName) Dim e As String = IO.Path.GetExtension(strBaseName) Return IO.Path.Combine(d, f & "_textbox" & e) End Function
-
Oct 19th, 2017, 08:55 PM
#5
Thread Starter
Member
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!
-
Oct 19th, 2017, 09:25 PM
#6
Thread Starter
Member
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.
-
Oct 19th, 2017, 09:55 PM
#7
Re: Make and combine a list of textboxes with a list of another object into one XML f
Originally Posted by dwigley
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:
'Read TextBoxes with auto generated file name MyFormLayout.Read(Me, GetTextBoxFileName(OpenFileDialog1.FileName))
with
vb.net Code:
'Read TextBoxes with auto generated file name MyFormLayout.Read(Me, GetTextBoxFileName(OpenFileDialog1.FileName)) For Each txt As TextBox In Me.Controls.OfType(Of TextBox) If txt.Name = "TextBox1" Then 'Note: "TextBox1" is the name you have set when creating textboxes in Button1_Click event ' I do this check in order to not affect other textboxes (which are not added by the user) txt.Multiline = True ' I set here because MyFormLayout class don't save Multiline property. (add it yourself if you want) AddHandler txt.MouseMove, AddressOf TextBox1_MouseMove End If Next
-
Oct 19th, 2017, 11:50 PM
#8
Thread Starter
Member
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!
-
Oct 20th, 2017, 05:11 AM
#9
Re: Make and combine a list of textboxes with a list of another object into one XML f
Originally Posted by dwigley
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)
-
Oct 20th, 2017, 07:36 PM
#10
Thread Starter
Member
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!
-
Oct 27th, 2017, 11:42 PM
#11
Thread Starter
Member
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.
-
Oct 28th, 2017, 03:01 AM
#12
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:
Originally Posted by dwigley
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.
-
Oct 28th, 2017, 04:10 PM
#13
Thread Starter
Member
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.
-
Oct 29th, 2017, 04:24 AM
#14
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)
Originally Posted by dwigley
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
-
Oct 29th, 2017, 11:14 AM
#15
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:
- Here is how to save *anything* to XML.
- "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:
- Make a class with the properties you want to save.
- Add a property to MyApplicationData that stores the instance(s) of the class.
- Update the Save() code to gather all of the data from the appropriate Form and put it in MyApplicationData.
- 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.
-
Oct 29th, 2017, 07:33 PM
#16
Thread Starter
Member
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.
-
Oct 29th, 2017, 08:59 PM
#17
Re: Make and combine a list of textboxes with a list of another object into one XML f
Originally Posted by dwigley
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:
For j = Controls.Count - 1 To 0 Step -1 If Controls(j).Name = "TextBox1" Then RemoveHandler Controls(j).MouseMove, AddressOf TextBox1_MouseMove Controls.Remove(Controls(j)) End If Next
-
Oct 30th, 2017, 05:45 AM
#18
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
-
Oct 31st, 2017, 12:33 AM
#19
Thread Starter
Member
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!
-
Oct 31st, 2017, 12:33 AM
#20
Thread Starter
Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|