Hello,
I have an app with two ListBoxes and a TextField (which is used to add data in the first LB).
Which is the best method to implement saving and opening for such an app?
Thank you
Hello,
I have an app with two ListBoxes and a TextField (which is used to add data in the first LB).
Which is the best method to implement saving and opening for such an app?
Thank you
I wouldn’t call it the best, but you can get and set the entire Listbox with CellValueAt(-1, -1)
. Couple that with the example code from the docs for TextOutputStream and TextInputStream and you’ve got a quick 'n dirty Listbox save and load.
What do you want to do ?
a. Save a ListBox contents
Check first if the ListBox have Rows, then follow Tims advice.
b. Store the current window contents before the application left ?
In App.CancelClose (or Windows CancelCLose Event: do as above for both ListBox, then append the TextField contents if not empty.
So:
Check if ListBox1 is not empty, if so: create the text file and save the ListBox contents
Same for ListBox2, and append its contents to the file,
Check if the TextField is not empty and, if so, append its contents to the file, then close it.
To read back correctly, you have to set some delimiter to separate the data from the two ListBoxes and the TextField.
Eventually, you can use BinaryStream to make the writing/reading using the text width before the text, so at read time, you could read that amount of text, attribute it to the ListBoxes / TextField.
Things do get harder if you have assigned any CellTags, RowTags, or ColumnTags as they would have to be handled separately if you needed to save them also.
For this task, I suggest to use the BinaryStream class and loop thru all cells in the listbox.
I’d create a listbox subclass with these methods in it:
[code]Public Sub SaveLB(bs As BinaryStream)
dim i,x,y As Integer
if bs=nil then Return
bs.WriteInt32 17 'Tag to validate the file on read
bs.WriteInt32 1 'Version: if later Xojo adds something to the listbox, increment that number. On read, read what the version knew.
bs.WriteInt32 me.ColumnCount 'In case the listbox can have more or less columns than default
for x=0 to me.ColumnCount-1
bs.WriteInt32 me.ColumnAt(0).WidthActual
SaveType bs,me.ColumnTypeAt(x)
SaveTag bs,me.ColumnTagAt(x)
next
bs.WriteInt32 54 'Tag between headers and content
bs.WriteInt32 me.RowCount
for y=0 to me.RowCount-1
SaveTag bs,me.RowTagAt(y)
for x=0 to me.ColumnCount-1
SaveType bs,me.CellTypeAt(y,x)
if i=2 then bs.WriteBoolean me.CellCheckBoxValueAt(y,x)
if i=3 or i=4 then SaveString bs,me.CellValueAt(y,x)
SaveString bs,me.CellTooltipAt(y,x)
bs.WriteBoolean me.CellBold(y,x)
bs.WriteBoolean me.CellItalic(y,x)
bs.WriteBoolean me.CellUnderline(y,x)
SaveTag bs,me.CellTagAt(y,x)
next
bs.WriteBoolean me.Selected(y)
next
bs.WriteInt32 35 'Another tag; separator for content and properties to set after loading data
bs.WriteInt32 me.SortingColumn
Select case me.ColumnSortDirectionAt(me.SortingColumn)
case Listbox.SortDirections.Ascending
i=1
case Listbox.SortDirections.Descending
i=-1
case Listbox.SortDirections.None
i=0
End Select
bs.WriteInt32 i
bs.WriteInt32 me.ScrollPosition
bs.WriteInt32 me.ScrollPositionX
bs.WriteInt32 11 'End tag
End Sub
Public Sub SaveType(bs As BinaryStream, Type As Listbox.CellTypes)
dim i As Integer
if bs=nil then Return
Select case Type
case Listbox.CellTypes.CheckBox
i=2
case Listbox.CellTypes.Normal
i=1
case Listbox.CellTypes.TextArea
i=4
case Listbox.CellTypes.TextField
i=3
Else 'Default (or unknown
)
i=0
End Select
bs.WriteInt32 i
End Sub
Public Sub SaveString(bs As BinaryStream, Data As String)
if bs=nil then Return
bs.WriteInt32 Data.Length
bs.Write Data
End Sub
Public Sub SaveTag(bs As BinaryStream, Tag As Variant)
if bs=nil then Return
dim vt As Integer
vt=VarType(Tag)
bs.WriteInt32 vt
if vt>=4096 then Return 'No support for array variants yet
Select case vt
case 0 'Nil
//Nothing
case 2
bs.WriteInt32 Tag
case 3
bs.WriteInt64 Tag
case 4
bs.WriteSingle Tag
case 5
bs.WriteDouble Tag
case 6
bs.WriteCurrency Tag
case 8
SaveString bs,Tag
case 11
bs.WriteBoolean Tag
case 16
dim clr As Color
clr=Tag.ColorValue
bs.WriteInt32 clr.Red
bs.WriteInt32 clr.Green
bs.WriteInt32 clr.Blue
case 9 'Object
Select case True
case Tag IsA PushButton
dim pb As PushButton=PushButton(Tag)
bs.WriteInt32 1
SaveString bs,pb.Caption
bs.WriteBoolean pb.Default
bs.WriteBoolean pb.Cancel
case Tag IsA DateTime
dim dt As DateTime=DateTime(Tag)
bs.WriteInt32 2
bs.WriteDouble dt.SecondsFrom1970
else 'Tag contains a class we haven't expected; either ignore saving it or implement how to save it (add a case here)
bs.WriteInt32 -2 'Not saved
End Select
End Select
End Sub
Public Function LoadLB(bs As BinaryStream) as Integer
dim i,j,Version,x,y As Integer
if bs=nil then Return -50 'BinaryStream not set
if bs.ReadInt32<>17 then Return -39 'Invalid format/corrupted file
Version=bs.ReadInt32
me.RemoveAllRows 'Start with a fresh listbox
me.ColumnCount=bs.ReadInt32
for x=0 to me.ColumnCount-1
me.ColumnAt(0).WidthActual=bs.ReadInt32
me.ColumnTypeAt(x)=LoadType(bs)
me.ColumnTagAt(x)=LoadTag(bs)
next
if bs.ReadInt32<>54 then Return -39 'Invalid format/corrupted file
j=bs.ReadInt32-1 'Row count-1
for y=0 to j
me.AddRow “” 'Just add an empty row for now
me.RowTagAt(y)=LoadTag(bs)
for x=0 to me.ColumnCount-1
me.CellTypeAt(y,x)=LoadType(bs)
Select case me.CellTypeAt(y,x)
case Listbox.CellTypes.CheckBox
me.CellCheckBoxValueAt(y,x)=bs.ReadBoolean
case Listbox.CellTypes.TextArea,Listbox.CellTypes.TextField
me.CellValueAt(y,x)=LoadString(bs)
End Select
me.CellTooltipAt(y,x)=LoadString(bs)
me.CellBold(y,x)=bs.ReadBoolean
me.CellItalic(y,x)=bs.ReadBoolean
me.CellUnderline(y,x)=bs.ReadBoolean
me.CellTagAt(y,x)=LoadTag(bs)
next
me.Selected(y)=bs.ReadBoolean
next
if bs.ReadInt32<>35 then Return -39 'Invalid format/corrupted file
me.SortingColumn=bs.ReadInt32
dim sd As Listbox.SortDirections
sd=Listbox.SortDirections.None 'Default value
Select case bs.ReadInt32
case -1
sd=Listbox.SortDirections.Descending
case 1
sd=Listbox.SortDirections.Ascending
End Select
me.ColumnSortDirectionAt(me.SortingColumn)=sd
me.ScrollPosition=bs.ReadInt32
me.ScrollPositionX=bs.ReadInt32
if bs.ReadInt32<>11 then Return -39 'Invalid format/corrupted file
End Function
Public Function LoadString(bs As BinaryStream) as String
if bs=nil then Return “”
Return bs.Read(bs.ReadInt32,Encodings.UTF8)
End Function
Public Function LoadTag(bs As BinaryStream) as Variant
if bs=nil then Return nil
dim vt As Integer
vt=bs.ReadInt32
if vt>=4096 then Return nil 'No support for array variants yet
Select case vt
case 0 'Nil
Return nil
case 2
Return bs.ReadInt32
case 3
Return bs.ReadInt64
case 4
Return bs.ReadSingle
case 5
Return bs.ReadDouble
case 6
Return bs.ReadCurrency
case 8
Return LoadString(bs)
case 11
Return bs.ReadBoolean
case 16
Return RGB(bs.ReadInt32,bs.ReadInt32,bs.ReadInt32)
case 9 'Object
Select case bs.ReadInt32 'What kind of object was saved
case 1
dim pb As PushButton=PB1
pb.Caption=LoadString(bs)
pb.Default=bs.ReadBoolean
pb.Cancel=bs.ReadBoolean
case 2
Return new DateTime(bs.ReadDouble)
case -2 'Not saved
System.DebugLog "Something wasn't saved last time."
else 'Something was saved, but we don't know what (either something from a newer version or a forgotten class from earlier versions)
MessageBox "Warning: some data are invalid."
End Select
End Select
End Function
Public Function LoadType(bs As BinaryStream) as Listbox.CellTypes
if bs=nil then Return Listbox.CellTypes.Default
Select case bs.ReadInt32
case 2
Return Listbox.CellTypes.CheckBox
case 1
Return Listbox.CellTypes.Normal
case 4
Return Listbox.CellTypes.TextArea
case 3
Return Listbox.CellTypes.TextField
Else 'Default (or unknown
)
Return Listbox.CellTypes.Default
End Select
End Function
[/code]
(the PushButton part represents any class you may have in the project; define how to save your class)
That’s just an example.
What’s wrong with a simple SQLite database? Isn’t this exactly the type of thing for which it is intended?
For plain text only, perhaps. To store styles and various other related things, I’d think it’s less appropriate.
Seems to me that a Listbox is a display article.
Not a data store.
The actual data should be in a database or a class, which can be saved/serialised.
The listbox should just display what the data store contains.
Kristin, nothing wrong with an SQLite DB. I was looking for something to stay out of it until I start delving into SQLite (I’m not into it yet)
[quote=496899:@Jeff Tullin]Seems to me that a Listbox is a display article.
Not a data store.
The actual data should be in a database or a class, which can be saved/serialised.
The listbox should just display what the data store contains.[/quote]
It may depend whether you allow the user to edit the cells and validate the changes at the end, e.g. when the window closes.
There are several possible ways; I’m not saying mine is better, it was just an example.
And, for one, I’ll never rely on a database, but that’s just a matter of opinions.