【问题标题】:Deserializing sets the contents of MainWindow in wpf to null反序列化将 wpf 中 MainWindow 的内容设置为 null
【发布时间】:2020-10-13 14:31:36
【问题描述】:

我正在尝试反序列化 MainWindow 中边框控件的 Opacity 属性

 public MainWindow()
        {
            InitializeComponent();
            Serialization.DeSerialize(this);
        }

  public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("OffOpacity", Off.Opacity);
        }

        public MainWindow(SerializationInfo info, StreamingContext context)
        {
            Off.Opacity = (double)info.GetValue("OffOpacity", typeof(double));
        }

这是反序列化方法:

  public static void DeSerialize(MainWindow M)
        {
            try
            {   var binaryIO = new BinaryFormatter();
                using (Stream stream = File.Open(@"./History.dat", FileMode.Open))
                {
                    M = (MainWindow)binaryIO.Deserialize(stream);
                }
            }catch(Exception e) { }
           
        }

InitializeComponent(); 之后我检查了 Off 并且它不为空。在 Serialization.DeSerialize(this); 运行并调用获取值的构造函数后,Off 出于某种原因变为 null,并在尝试获取值时给了我一个 nullreference 错误。

【问题讨论】:

  • 您正在混合状态和数据,并且正在使用不会像这样工作的引用(在另一个函数中分配 M 不会影响您传递的 this)。基本上,您的表单在InitializeComponent() 之后启动并运行,它具有运行时状态,并且(MainWindow)binaryIO.Deserialize(stream) 返回的反序列化表单还没有调用其InitializeComponent(),因此它的Off 成员中没有实例。
  • 你是说,当我们设置M等于反序列化的形式时,首先运行的构造函数,不是执行InitializeComponent()的构造函数;但是获得价值的那个?
  • 反序列化时,不运行构造函数。你不能像那样序列化表单。
  • public MainWindow(SerializationInfo info, StreamingContext context) 在我们执行反序列化方法之后运行,它从 info 中获取值并将它们分配给属性。这不是构造函数吗?虽然,因为它没有 InitializeComponent();它给了我一个空引用。我想我必须包含它才能解决问题?
  • 是的,BinaryFormatter 在它正在反序列化的实例上调用 that 构造函数,但这不会影响您从中调用 Serialization.DeSerialize(this) 的 MainWindow。

标签: c# wpf serialization


【解决方案1】:

您会收到此异常,因为Off 是一个控件,它在运行时通过InitializeComponent() 实例化和配置(正在设置子属性)。序列化程序创建MainWindownew 实例,并按照约定调用特殊的MainWindow(SerializationInfo info, StreamingContext context) 构造函数。

因为构造函数没有调用InitializeComponent,所以您的Off 控件是null。快速的解决方案是将构造函数更改为:

public MainWindow(SerializationInfo info, StreamingContext context)
{
    InitializeComponent();
    Off.Opacity = (double)info.GetValue("OffOpacity", typeof(double));
}

现在您的代码运行了,但不透明度不会应用于您的可见表单,因为它应用于由序列化程序创建的实例。所以你必须复制感兴趣的属性:

public static void DeSerialize(MainWindow m)
{
    try
    {
        var binaryIO = new BinaryFormatter();
        using (Stream stream = File.Open(@"./History.dat", FileMode.Open))
        {
            var deserializedMainWindow = (MainWindow)binaryIO.Deserialize(stream);
            
            m.Off.Opacity = deserializedMainWindow.Off.Opacity;
            m.Other.Property = deserializedMainWindow.Other.Property;
            m.Foo.Bar = deserializedMainWindow.Foo.Bar;
            // ...
        }
    }
    catch(Exception e) { }   
}

您甚至可能无法访问MainWindow 之外的这些属性,假设它们不是公开的,而您现在到处都有您的属性名称。

有一些库(Telerik 就是其中之一)可以帮助序列化表单布局,而无需您一遍又一遍地指定您感兴趣的属性。如果您想自己做并且它只是少数几个属性,我建议将适当的布局(反)序列化到自己的类中:

public class MainWindowLayout
{
    public double? OffOpacity { get; set; }
    public string OtherProperty { get; set; }
    public bool? FooBar { get; set; }
}

然后以你的形式:

public void SerializeLayout()
{
    var layout = new MainWindowLayout
    {
        OffOpacity = Off.Opacity;
        OtherProperty = Other.Property;
        FooBar = Foo.Bar;
    };
    
    File.WriteAllText("MainWindowLayout.json", JsonConvert.SerializeObject(layout));
}

public void DeserializeLayout()
{
    // TODO: error handling and defaults
    var json = File.ReadAllText("MainWindowLayout.json");
    var layout = JsonConvert.DeserializeObject<MainWindowLayout>(json);
    
    Off.Opacity = layout.OffOpacity;
    Other.Property = layout.OtherProperty;
    Foo.Bar = layout.FooBar;
}

如果您不想要 JSON 但坚持使用 BinaryFormatter,则调整代码以支持它。

【讨论】:

  • 非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-14
  • 1970-01-01
  • 2011-12-12
  • 2020-06-23
  • 2019-04-03
  • 1970-01-01
相关资源
最近更新 更多