【问题标题】:ArrayList changes does not affect to My.SettingsArrayList 更改不会影响到 My.Settings
【发布时间】:2014-08-02 06:02:17
【问题描述】:

我又来这里尝试用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.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)

【问题讨论】:

  • 有人可以告诉你ArrayList 已经过时了。
  • @John Saunders 您有更好的建议,使用 My.Settings 来替换接受对象数组(如 ArrayList)的 Type 的使用?,因为 StringCollection 对我没有帮助,它只接受 1对象并且需要是一个字符串,并且 My.Settings 不接受通用类型,所以无论是否过时,这是唯一满足我需要的类型(或者至少是我能找到的唯一兼容的类型)。
  • 这是问题所在:.Add({"Item0", 0.0F})My.Settings.MRU.Add({"Collection", "Of", "Strings"}) My.Settings 只是不会/不能处理以这种方式构建的 ArrayList(每个元素都是一个数组)。使用AddRange 用简单的数据对其进行测试,它会起作用。如果您拥有或喜欢 MRU CLass,只需将其直接序列化为文件并完成即可 - My.Settings 确实不会增加任何价值,只是额外的步骤。

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


【解决方案1】:

由于应用程序设置将复杂类型序列化为 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 属性(我在代码中省略了它,因为它不可序列化),你应该将它的值(项目、值)拆分为 DataTable 中的列。

【讨论】:

  • 非常感谢您提供的信息,那么如果我理解得很好,问题是无法保存 ArrayList(在 My.Settings.MRU 中)上的更改,因为 ArrayList 内容无法保存序列化?在这种情况下,我想我应该创建自己的类,将其声明为可序列化,将一些属性添加到类中,然后将 ArrayList 用法替换为我自己的 Type 并将其序列化为 ...where?我不明白曾经写过自己的类如何保存我的新设置来替换数组列表。
  • 我认为最简单的方法是将数据保存在 DataTable 中并保存在应用程序设置中。
  • 我尝试创建自己的可序列化类,但包含我的类型的 ArrayList 不可序列化,这表示您提供的代码并且在下次运行时不会保留 arraylist,所以我也尝试将设置类型从 ArryList 更改为 System.Object 以尝试存储 List(of MRUItems) 但也不能序列化,我真的不知道我能做什么......而且数据表不知道关于如何管理它。
【解决方案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

【讨论】:

  • 这是使用逻辑运算符的结果。您的第一条评论(正确)是关于比较运算符的。
  • 感谢您让我在比较中看到我的错误,但不幸的是,这只是小事一桩,我想说这并不能解决 ArrayList 的问题,这不是因为比较不好, ArrayList 在修复比较后不会保存 My.Settings 中的更改。 PS:对不起我的英语。
【解决方案3】:

这不适用于 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,用户无法轻松找到设置文件并对其进行修改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-16
    • 1970-01-01
    • 2019-01-19
    • 2013-05-09
    • 2012-01-18
    • 2021-10-06
    • 1970-01-01
    相关资源
    最近更新 更多