My.Settings does not save an ArrayList

2020-02-15 02:51发布

问题:

I've set this emptyness property:

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:

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)

回答1:

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.