0

我在这里再次尝试用我在另一个问题ArrayList中解释过的问题来解决这个问题......不同之处在于这次我没有寻找任何替代解决方案,例如使用其他外部文件来避免真正的问题,我真的很想使用My.SettingsArrayList来解决这个问题,我想了解这里发生的关于My.Settings的事情!

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

在此处输入图像描述

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

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.ArrayListSystem.Object(拼命地),所以现在我可以这样做,但问题仍然存在,我的意思是在下一次运行应用程序时不会保存该集合:

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

3 回答 3

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.Xml.dll 中出现“System.InvalidOperationException”类型的未处理异常附加信息:生成 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 属性(我在代码中省略了它,因为它不可序列化),您应该将其值(项目、值)拆分为 DataTable 中的列。

于 2014-08-02T09:37:32.363 回答
2

为什么你说?因为你对待 aInteger就像它是 a 一样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 StrictOn.


简单测试

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
于 2014-08-02T07:39:08.753 回答
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,用户无法轻松找到设置文件并对其进行修改。

于 2014-08-02T12:58:50.027 回答