为什么我的ObservableCollection序列化不起作用?

3

我正在尝试序列化和反序列化这个ObservableCollection:

public class DataCollection : ObservableCollection<Data>
{
}

public class Data : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool? _enabled;

    public string Name { get; set; }
    public bool? Enabled 
    {
        get { return _enabled; }
        set { _enabled = value; NotifyPropertyChanged("Enabled"); }
    }

    public Data(string name, bool? enabled)
    {
        this.ScriptName = name;
        this.Enabled = enabled;
    }

    private void NotifyPropertyChanged(string property)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
}

使用这个类(就像最简单的将Observable集合对象保存到XML文件中的方法示例中所示):
class UserPreferences
{
    private DataCollection _dataLst;
    private const string _dataLstFileName = "Data.xml";

    public DataCollection DataLst { get { return _dataLst; } set { _dataLst = value; } }

    public UserPreferences()
    {
        Load();
    }

    public void Load()
    {
        if (File.Exists(_dataLstFileName))
        {
            using (var reader = new StreamReader(_dataLstFileName))
            {
                var xs = new XmlSerializer(typeof(DataCollection));
                _dataLst = (DataCollection) xs.Deserialize(reader);
            }
        }
        else
        {
            _dataLst = new DataCollection();
        }
    }

    public void Save()
    {
        using (var writer = new StreamWriter(_dataLstFileName))
        {
            var xs = new XmlSerializer(typeof(DataCollection));
            xs.Serialize(writer, _dataLst);
        }
    }
}

以下是我如何在我的应用程序中调用它:

public partial class MainWindow : Window
{
    private DataCollection DataLst;

    ...

    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        var userPrefs = new UserPreferences();

        userPrefs.DataLst = DataLst; // DataList isn't null and contains correct data
        userPrefs.Save(); 
    }
}

但它会创建空文件并在下面这行代码卡住(即使没有异常,应用程序窗口变成黑色且没有响应):

var xs = new XmlSerializer(typeof(DataCollection));  

在你的 DataCollection 上添加 [Serializable] - nemesv
@nemesv 这并没有帮到我,问题仍然存在。 - Alex P.
2个回答

5
经过一番研究,似乎在序列化ObservableCollection<T>时会出现问题。更多信息请参见这篇博客文章。
简而言之:
问题在于事件没有被标记为不可序列化。因此,每当您尝试序列化ObservableCollection的实例时,还将尝试序列化任何事件处理程序。当您使用该集合进行其主要场景(数据绑定)时,会将WPF控件附加到事件。
你的Data类也会面临类似的问题;请按以下方式更新你的PropertyChanged事件:
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;

除了其他人已经提到的更新之外,您的Data类还应该标记为Serializable。

我曾经在某个地方读到过,.NET 4.0 中的事件问题已被解决。看起来,当我为 Data 类添加无参构造函数时它能正常工作。 - Alex P.
我会将这个答案留在这里,因为这可能会帮助使用早期版本框架的人。但是你的数据类仍然需要更新其属性更改事件,否则它也会尝试序列化任何已订阅的内容。 - Benjamin Gale
2
2016年4月 - 只是确认当前的.NET中没有问题 - ObservableCollection<T>可序列化,无需特殊处理。甚至在Xamarin.Forms跨平台解决方案中的PCL项目中也可以使用。 - Clint StLaurent
[Serializable][Serializable()] 之间有什么区别吗? - sergiol

3
  1. 您的Data类必须有一个无参构造函数,否则XmlSerializer将永远无法创建Data实例。
  2. 不要存储DataCollection,而应该存储和检索Data[],这样可以更容易地进行操作。
  3. 在存储时,您可以调用ToArray方法获取Data[],并使用typeof(Data[])用于序列化器。
  4. 在读取时,您可以读取数组并将项目添加到集合中。

谢谢,看起来在我添加了一个无参构造函数之后它可以工作了。但是你说的“存储和检索Data[]而不是存储DataCollection”是什么意思?为什么这样更容易?我正在使用ObservableCollection作为ListBox ItemsSource绑定(WPF)。 - Alex P.
有时我们会遇到问题,需要额外编写代码。我不记得具体是什么问题了,但我们避免序列化集合,而总是将其序列化为数组。因为序列化的数组在旧版本的.NET中也很容易访问。 - Akash Kava

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接