ArrayList更改不会影响My.Settings

时间:2014-08-02 06:02:18

标签: .net vb.net winforms arraylist my.settings

我在这里再次尝试使用我在this other question中解释过的ArrayList来解决这个问题...不同的是,这次我没有搜索任何内容替代解决方案,比如使用其他外部文件来避免真正的问题,我真的想用 My.Settings ArrayList 解决这个问题,我我想了解 My.Settings

在这里发生了什么

问题在于,如果我们设置这样的设置:

enter image description here

然后在下次应用程序运行时保留对该设置执行的任何更改,此代码说明了问题:

Public Class Test

   Private Sub Test_Handler() Handles MyBase.Shown

       ' Create a temporal predefined ArrayList.
       Dim tmpArrayList As New ArrayList(capacity:=10I)
       With tmpArrayList
           .Add({"Item0", 0.0F})
           .Add({"Item1", 0.5F})
       End With

       ' Check the setting status.
       If My.Settings.MRU Is Nothing Then
           Debug.WriteLine("MRU setting is null.")
           Debug.WriteLine("Initializing the Setting...")
           My.Settings.MRU = New ArrayList(capacity:=10I)

       ElseIf My.Settings.MRU.Count = 0 Then
           Debug.WriteLine("MRU is not null but the ArrayList is empty.")
           Debug.WriteLine("Adding some items...")
           My.Settings.MRU = tmpArrayList.Clone

       ElseIf My.Settings.MRU.Count > 0 Then ' This part of the block  will never thrown.
           Debug.WriteLine("MRU setting is OK.")
           Debug.WriteLine("Item Count: " & CStr(My.Settings.MRU.Count))
           Threading.Thread.Sleep(Integer.MaxValue)

       End If

       Debug.WriteLine("Saving any changes")
       My.Settings.Save()

       Debug.WriteLine("Updating any changes")
       My.Settings.Reload()

       Debug.WriteLine(String.Empty)
       Debug.WriteLine("****************************************")
       Debug.WriteLine("Checking again the MRU setting status in...")
       For Count As Integer = 1 To 3
           Debug.WriteLine(CStr(Count) & New String("."c, Count))
           Threading.Thread.Sleep(TimeSpan.FromSeconds(1))
       Next
       Debug.WriteLine("****************************************")
       Debug.WriteLine(String.Empty)

       Me.Test_Handler()

   End Sub

End Class

有人可以教我理解为什么没有真正保存ArrayList,或者告诉我如何解决这个问题?。

  

更新

好的我写这个可序列化的类试图解决这个问题:

''' <summary>
''' A Class intended to use it as an Item for a MRU item collection that stores the item filepath, with additional info.
''' </summary>
<Serializable()>
Public Class MostRecentUsedItem

    ''' <summary>
    ''' Gets or sets the item filepath.
    ''' </summary>
    ''' <value>The file path.</value>
    Public Property FilePath As String

    ''' <summary>
    ''' (Optionally) Gets or sets the item index.
    ''' </summary>
    ''' <value>The index.</value>
    Public Property Index As Integer

    ''' <summary>
    ''' (Optionally) Gets or sets the item image.
    ''' </summary>
    ''' <value>The image.</value>
    Public Property Img As Bitmap

    ''' <summary>
    ''' (Optionally) Gets or sets the item last-time open date.
    ''' </summary>
    ''' <value>The index.</value>
    Public Property [Date] As Date

    ''' <summary>
    ''' (Optionally) Gets or sets the item tag.
    ''' </summary>
    ''' <value>The tag object.</value>
    Public Property Tag As Object

End Class

此外,我已经编写了这个帮助函数来帮助我解决这个问题:

''' <summary>
''' Determines whether an object can be XML serialized.
''' </summary>
''' <param name="Object">The object.</param>
''' <returns><c>true</c> if object is XML serializable; otherwise, <c>false</c>.</returns>
Private Function IsObjectSerializable(ByVal [Object] As Object) As Boolean

    Using fs As New IO.FileStream(IO.Path.GetTempFileName, IO.FileMode.Create)

        Dim Serializer As New Xml.Serialization.XmlSerializer([Object].GetType)

        Try
            Serializer.Serialize(fs, [Object])
            Return True

        Catch ex As InvalidOperationException
            Return False

        End Try

    End Using

End Function

在我像这样初始化设置时,它是可序列化的:

My.Settings.MRU = New ArrayList

目前我只添加一个字符串,它仍然是可序列化的:

My.Settings.MRU.Add("test string")

但是,当我尝试添加可序列化类或任何其他类型的数据类型(如String())时,ArrayList开始不可序列化,如下所示:

My.Settings.MRU.Add({"Collection", "Of", "Strings"})

或者像这样:

Dim MRUItem As New MostRecentUsedItem
MRUItem.FilePath = "C:\Test.ext"
My.Settings.MRU.Add(MRUItem)

...因此ArrayList内容在下次运行时不会保留,无法序列化。

我还尝试将设置类型从System.Collections.ArrayList更改为System.Object(拼命),所以现在我可以执行此操作,但问题仍然存在,我的意思是该集合未保存在下一个应用运行中:

My.Settings.MRU = New List(Of MostRecentUsedItem)
Dim MRUItem As New MostRecentUsedItem
MRUItem.FilePath = "C:\Test.ext"
My.Settings.MRU.Add(MRUItem)

3 个答案:

答案 0 :(得分:2)

为什么这么说?因为您将Integer视为Boolean

以下行始终为真(符合条件),因为Count属性永远不会返回-1

ElseIf Not My.Settings.MRU.Count Then

这就是为什么永远不会达到这条线的原因。

ElseIf My.Settings.MRU.Count Then 

您应该做的是用以下代码替换您的代码:

ElseIf My.Settings.MRU.Count = 0 Then
Else

并且,与往常一样,将Option Strict设置为On


简单测试

For i As Integer = -2 To 2
    Debug.Write(i.ToString())
    If (Not i) Then
        Debug.Write(", Not i")
    ElseIf (i) Then
        Debug.Write(", i")
    End If
    Debug.Write(Environment.NewLine)
Next

结果:

-2, Not i
-1, i
 0, Not i
 1, Not i
 2, Not i

答案 1 :(得分:2)

由于Application Settings将复杂类型序列化为XML,因此在保存之前必须确保特定类型可以序列化为XML。

您可以使用以下方法测试数据类型:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim tmpArrayList As New ArrayList()
    With tmpArrayList
        .Add({"Item0", 0.0F})
        .Add({"Item1", 0.5F})
    End With

    Dim XmlSerializer1 As New XmlSerializer(tmpArrayList.GetType)
    XmlSerializer1.Serialize(Console.Out, tmpArrayList)
End Sub

此代码无法序列化ArrayList并返回以下消息:

  

类型'System.InvalidOperationException'的未处理异常   发生在System.Xml.dll中其他信息:出现错误   生成XML文档。

但是如果您尝试在ArrayList中存储简单数据类型,则序列化将成功

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    Dim tmpArrayList As New ArrayList()
    With tmpArrayList
        .Add("Item0")
        .Add("Item1")
    End With

    Dim XmlSerializer1 As New XmlSerializer(tmpArrayList.GetType)
    XmlSerializer1.Serialize(Console.Out, tmpArrayList)
End Sub

在“应用程序设置”中存储数据时会发生相同的情况,但区别在于它不会返回错误。

有用的链接:

修改

使用DataTable实现

创建一个新的Windows窗体项目,在应用程序设置中添加一个名为NewMRU的新设置,其数据类型为System.Data.DataTable,并尝试以下代码。

Imports System.IO
Imports System.Xml.Serialization
Imports System.Drawing.Imaging

Public Class Form1

    Dim DataTable1 As New DataTable("MySettingsDataTable")

    Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        DataTable1.Columns.Add("FilePath", GetType(String))
        DataTable1.Columns.Add("Index", GetType(Integer))
        DataTable1.Columns.Add("Img", GetType(Byte()))
        DataTable1.Columns.Add("Date", GetType(DateTime))
    End Sub

    Private Sub button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        AddPropertyRow("C:\Temp", 1, GetBytesFromBitmap(Me.Icon.ToBitmap), Now)
        AddPropertyRow("C:\Windows", 2, GetBytesFromBitmap(Me.Icon.ToBitmap), Now)

        My.Settings.NewMRU = DataTable1
        My.Settings.Save()

        'MsgBox(IsObjectSerializable(DataTable1))
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        If Not My.Settings.NewMRU Is Nothing Then
            Dim i As Integer

            For i = 0 To My.Settings.NewMRU.Rows.Count - 1
                Debug.WriteLine("Row - " & i + 1)
                Debug.WriteLine("FilePath = " & My.Settings.NewMRU.Rows(i).Item("FilePath"))
                Debug.WriteLine("Index = " & My.Settings.NewMRU.Rows(i).Item("Index"))
                Debug.WriteLine("Img bytes count = " & TryCast(My.Settings.NewMRU.Rows(i).Item("Img"), Byte()).Length)
                Debug.WriteLine("Date = " & My.Settings.NewMRU.Rows(i).Item("Date"))
                Debug.WriteLine("")
            Next
        End If

        'PictureBox1.Image = GetBitmapFromBytes(TryCast(My.Settings.NewMRU.Rows(0).Item("Img"), Byte()))
    End Sub

    Private Sub AddPropertyRow(ByVal FilePath As String, ByVal Index As Integer,
                               ByVal Img() As Byte, ByVal [Date] As Date)
        DataTable1.Rows.Add(DataTable1.NewRow)
        Dim RowIndex As Integer = DataTable1.Rows.Count - 1
        DataTable1.Rows(RowIndex).Item("FilePath") = FilePath
        DataTable1.Rows(RowIndex).Item("Index") = Index
        DataTable1.Rows(RowIndex).Item("Img") = Img
        DataTable1.Rows(RowIndex).Item("Date") = [Date]
    End Sub

    Private Function IsObjectSerializable(ByVal [Object] As Object) As Boolean
        Using fs As New IO.FileStream(IO.Path.GetTempFileName, IO.FileMode.Create)
            Dim Serializer As New Xml.Serialization.XmlSerializer([Object].GetType)

            Try
                Serializer.Serialize(fs, [Object])
                Return True
            Catch ex As InvalidOperationException
                Return False
            End Try
        End Using
    End Function

    Private Function GetBytesFromBitmap(ByVal Bitmap1 As Bitmap) As Byte()
        Dim BitmapBytes() As Byte

        Using MemoryStream1 As New MemoryStream()
            Bitmap1.Save(MemoryStream1, ImageFormat.Bmp)
            BitmapBytes = MemoryStream1.GetBuffer()
        End Using

        Return BitmapBytes
    End Function

    Private Function GetBitmapFromBytes(ByVal Bytes As Byte()) As Bitmap
        Dim Bitmap1 As Bitmap = Nothing

        Using MemoryStream1 As New MemoryStream(Bytes, 0, Bytes.Length)
            Bitmap1 = Image.FromStream(MemoryStream1)
        End Using

        Return Bitmap1
    End Function

End Class

如果你想使用Tag属性(我从代码中省略了它,因为它不可序列化),你应该将它的值(Item,Value)拆分为DataTable中的列。

答案 2 :(得分:1)

这不适用于My.Settings中的ArrayList:

.Add({"Item0", 0.0F})
My.Settings.MRU.Add({"Collection", "Of", "Strings"})

无论出于何种原因,当每个元素都是数据数组时,My.Settings无法/不会序列化。首先,这些类型是混合的,可能有所贡献。使用简单数据和AddRange将:

.AddRange("Foo1", "Bar2", "FooBar")

它也不会序列化ArrayList个对象,即MostReccentItem。我不确定为什么,但在这两种情况下,图表似乎太复杂了,无法满足它所期待的My.Settings或者它是如何在内部进行的。

我认为你应该接受,无论你想做什么,或者你想怎么做,对于My.Settings来说都太复杂了。如果您正在修改序列化程序,那么只需一小步就可以完成:

你已经有了这个:

<Serializable()>
Public Class MostRecentUsedItem

    Public Property FilePath As String

    Public Property Index As Integer

    Public Property Img As Bitmap

    Public Property [Date] As Date

    Public Property Tag As Object

End Class

添加List以替换My.Settings容器,并添加文件名var:

Private SettingsFile As String = "C:\Temp\MRUSetTest.bin"
Private MRUList As New List(Of MostRecentUsedItem)

将序列化代码更改为此用于保存(我使用BF):

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

加载数据的另一个小块:

' ToDo: add am If Exists line for the very first time the app runs.

Dim bf As New BinaryFormatter
Using fs As New FileStream(SettingsFile , FileMode.Open)
    MRUList = CType(bf.Deserialize(fs), List(Of MostRecentUsedItem))
End Using

对于My.Settings来说,没有任何神奇的东西值得您努力尝试让它发挥作用。以上将保存/加载您的列表,这应该是重点。

在更复杂的应用程序中,让MRUList成为另一个包含用户选项等其他设置的类的成员非常简单,只需序列化那个更大的类。

您可以使用XMLSerializer,但这会增加一些限制。我更喜欢使用BinaryFormatter,用户无法找到设置文件并轻松修补它。