我在这里再次尝试使用我在this other question中解释过的ArrayList
来解决这个问题...不同的是,这次我没有搜索任何内容替代解决方案,比如使用其他外部文件来避免真正的问题,我真的想用 My.Settings 和 ArrayList 解决这个问题,我我想了解 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)
答案 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,用户无法找到设置文件并轻松修补它。