2

I've set this emptyness property:

enter image description here

But after assigning a new ArrayList object to the property, the property always still empty in every application execution.

In a handler of MyBase.Load event I do a call to this method, just for testing this issue:

sub blahblah handles mybase.load
    me.CheckRecentFiles
end sub

Private Sub CheckRecentFiles()

    Try
        ' This throws an object not referenced exception 'cause always is empty.
        MsgBox(My.Settings.RecentFiles.Count)
    Catch ex As Exception
        MsgBox(ex.Message)
    End Try

    ' Just for testing, if the collection is empty then I instance a new ArrayList
    ' and I assign it to the property and I save it and exit from the application.
    ' But even doing this the property is always empty in the next execution.
    If My.Settings.RecentFiles Is Nothing Then

        My.Settings.RecentFiles = New ArrayList
        My.Settings.RecentFiles.Add({"test-Item1", "test-Item2", "Test-Item3"})
        My.Settings.Save()
        Application.Exit()

    End If

End Sub

As you can see in the code above I'm assigning a new ArrayList with one entry, but the changes only take effect during that appplication execution, if I exit from the app then the property goes empty again.

And also ofcourse I've checked this option:

enter image description here

But anyways that is unnecessary since I'm saving the setting manualy in the code, so...

Why happens this?.

How I can solve this problem?.

UPDATE:

I've investigated and seems that this is a known problem that Arrays, ArrayLists and any Generic collections(Of Type) can't be saved by the my.settings (but on the other hand a StringCollection can)

But in the last answer of this post (the MemoryStream answer) explains an easy way to permanently save the changes of an ArrayList to my.settings and then read it in the next application run.

The answer seems very good, but I'm a little bit lost with the code and the steps to procceed, so someone could explain the steps explained there but in a human-readable language for me please?

I've verified that the ArrayList remains in the next application run, yes but I'm not sure of what I'm doing 'cause if the MemoryStream contains the old ArrayList then what I'm doing now Is assigning the My.Settings.MRU setting as an Arraylist that contains more Arraylists instead the originaly ArrayList that contained a String() ?, and anyways how to load the array entries after saving the setting this way?.

This is what I've tried from that answer:

' Create the ArrayList
Dim tmpArrayList = New System.Collections.ArrayList
tmpArrayList.Add({"test-Item1-1", "test-Item1-2", "Test-Item1-3"})
tmpArrayList.Add({"test-Item2-1", "test-Item2-2", "Test-Item2-3"})

' Serialize the arraylist entries:
Dim formatter As Runtime.Serialization.IFormatter =
    New Runtime.Serialization.Formatters.Binary.BinaryFormatter
Dim ms1 As New IO.MemoryStream
formatter.Serialize(ms1, tmpArrayList)

' Save the ArrayList
My.Settings.MRU = New ArrayList(ms1.ToArray) ' I think it hould be like that?

' Load the ArrayList contained in My.Settings.MRU (no idea)
Community
  • 1
  • 1
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • I suggest you to use a text file or a database to save your data. – spongebob Jul 29 '14 at 06:13
  • Thanks, I know how to implement the alternatives that you've commented (ini file or xml database) but I am familiar with the My.Settings usage and I want to preserve it in this case 'cause this application really does not require the manipulation of a text file to store/load user settings 'cause I'm not trying to set a setting where the user could decide its value by editing a file, I'm just trying to set an MRU setting. As I said i'm familiar with MY.Settings interface but this is the first time that I get this kind of problem, and I would like to fix it not avoid it. Thanks for comment. – ElektroStudios Jul 29 '14 at 07:19
  • Each file, also a database, can be changed by anyone. You can choose an hidden path, such as `AppData`. – spongebob Jul 29 '14 at 07:45
  • @Joiner yes, I know you are right, but using My.Settings it already creates a file in AppData (Roaming) folder that we could say "it's hidden for he user" avoiding the creation of a custom implementation of reading/writting config-files with custom objects (like my ArrayList and all those objects that can be threated). Thanks for comment! – ElektroStudios Jul 29 '14 at 08:00
  • If you want to use my.settings - add a string key and serialize the ArrayList to a string, e.g. using Xmlserialiaztion. Then store that in My.Settings – Christian Sauer Jul 29 '14 at 09:20
  • @Christian Sauer please could you provide an example of that? the "Serialize" method expects some objects but none as a String, and then I imagine that in the next app run I should deserialize the setting I'm right? – ElektroStudios Jul 30 '14 at 14:36

1 Answers1

2

If you have the data in an arrayList (or List or Collection), and you are looking at the BinaryFormatter for the workaround, there is no good reason to also also use My.Settings. You can do what it does thru the BinaryFormatter, which is just saving the file and picking a name.

Imports System.Runtime.Serialization.Formatters.Binary

Private MRUArrayList = New ArrayList
' file location
 myFile = System.IO.Path.Combine(Environment.GetFolderPath(Environment. _
                    SpecialFolder.ApplicationData),
                                    CompName,
                                    ProgramName,
                                    File)

Save Settings:

Dim bf As New BinaryFormatter
Using fs As New FileStream(myFile, FileMode.OpenOrCreate)
    bf.Serialize(fs, MRUArrayList )
End Using

Load Settings:

' dont attempt for the first time run
If File.Exists(myFile) = False Then Return False

Dim bf As New BinaryFormatter
Using fs As New FileStream(myFile, FileMode.Open)
    MRUArrayList = CType(bf.Deserialize(fs), ArrayList)
End Using

Once you have to resort to BF for the workaround, replacing the Memory Stream with a File Stream gets rid of the need for My.Settings entirely, lets you store the file wherever you want and it wont change by version, user changing the EXE name or anything else unless you change the file name formula above.

For an App with more than just the MRU ArrayList, you can use a Class in its place to store all the settings to the location you want very much like Settings does. You just need to tag the Class as <Serializable>. It remains one line of code to save the entire class, one line to reconstitute it. There are some limitations, but they are not difficult to overcome.

Private myNewSettings As New myNewSettingsClass
...

bf.Serialize(fs, myNewSettings)

myNewSettings = CType(bf.Deserialize(fs), myNewSettingsClass )

In other situations, you can use the XML serializer or ProtoBuf-NET as needed for the situation.

You can also have your new settings automatically saved when the program exits: Go to Project Properties --> Application --> Click View Application Events. Select "Application Events" from the left menu, and ShutDown from the right event menu.

Private Sub MyApplication_Shutdown(sender As Object, 
          e As EventArgs) Handles Me.Shutdown

   ' add your code to Save (above) here
End Sub

Likewise you can have it automatically load them in the Startup event.

Ňɏssa Pøngjǣrdenlarp
  • 38,411
  • 12
  • 59
  • 178
  • It's a cool solution but I really wanted to know and solve the real problem with My.Settings – ElektroStudios Jul 29 '14 at 12:18
  • the "solution" you linked to is not doing what you think. It is using a new settings class, when VB asks for the data it uses IFormatter to return binary serialized data to VB which is then XML serialized. It is not a fix at all, but a cruder way to do the above with more code. – Ňɏssa Pøngjǣrdenlarp Jul 29 '14 at 12:30
  • just a question, I just can't use the "copyto" method of an instanced ArrayList to copy the contents into the My.Settings.MyArrayList property and maybe that will solve the real problem? I've tried it but the method expects an Array (not an arraylist) could you help me with that? – ElektroStudios Jul 29 '14 at 12:34
  • 1
    see this answer: http://stackoverflow.com/a/24112414/1070452 if you do not have a default entry it is nothing as you noted, you cant add to nothing so it fails. Initialize it and it should be ok...but the above is still better. – Ňɏssa Pøngjǣrdenlarp Jul 29 '14 at 12:47
  • that works for a StringCollection but not for an ArrayList, no matter if you try to initialize the object or to click the "ellipsis" in the settings editor, the changes done at execution on the ArrayList does not remain on the next run. I think the ArrayList should point to pointers then really nothing is added/copied permanently, but seems that nobody knows how to solve this!, I've tried it using the "clone" method but there is no way to see the changes in the next run, maybe the "copyto" really copies the arraylist contents instead pointers but you know I fail to use the method – ElektroStudios Jul 30 '14 at 09:18
  • Ok I'm a little bit closer, the 'Collections.ArrayList.Repeat' method really copies the contents itself to My.Settings.MyArrayList, the problem is that is not exactlly what expects 'cause it only copies a value (or an entire ArrayList but contained into an ArrayList) it returns an ArrayList that remains on the next run, the "Clone" method returns an ArrayList but does not remains on the next run, I'm really sure that the "MyArrayList.CopyTo" method should be the key to solve this... – ElektroStudios Jul 30 '14 at 09:29
  • 1
    In your original code (before you started on a workaround), use addrange: `My.Settings.MRU.AddRange({"Hoover", "Ziggy", "Punkin"})`. Using `Add` sort of creates a jagged array (count=1) which then **isnt** in the MRU the next time either automatically or from .Reload. Using Add only creates one element in the MRU which is a String Array of those values (not what you probably want). There is probably an exception not being caught by VS. Tested several times, Add never works, AddRange always worls, but you do need to initialize MRU the first time thru if there are no defaults. – Ňɏssa Pøngjǣrdenlarp Jul 30 '14 at 12:37
  • The AddRange in my case has the same effect of the Add method... does not preserve the changes on the Setting the next time. – ElektroStudios Aug 02 '14 at 06:14