【问题标题】:What's the way to implement Save / Load functionality?实现保存/加载功能的方法是什么?
【发布时间】:2010-09-25 18:28:40
【问题描述】:

我正在尝试为 Windows 窗体应用程序实现加载/保存功能。

我有以下组件:

  • 树状视图
  • 几个列表视图
  • 几个文本框
  • 几个对象(包含一个大字典列表)

我想实现一种方法将所有这些保存到一个文件中,并在以后恢复/加载它。

最好的方法是什么?

我认为 XML 序列化是可行的方法,但我不太确定如何或从哪里开始。还是需要一个非常复杂的解决方案才能做到这一点?

【问题讨论】:

    标签: .net winforms load save


    【解决方案1】:

    上面的示例存在问题。 考虑到最终您的应用程序已更新。您的对象模型可能会发生巨大变化,因此无法反序列化。您可以做一些事情来确保从 xml 版本 1 的反序列化可以反序列化为版本 2 中的对象模型,但是如果您有可能进行重大的结构更改,则 xml 反序列化是不是要走的路。

    如果是这种情况并且您的应用程序已部署给客户,我强烈建议您进一步查看您的保存/加载逻辑。

    版本化序列化/反序列化
    以以下形式序列化您的对象状态:

    <ObjectState version="1">
        <Field1>value</Field1>
        ... etc ...
    </ObjectState>
    

    所以现在您有了生成保存状态的对象模型的版本。 在您的反序列化中,您可以采取特殊的措施来适应这一事实。例如,将 Field1-Value 写入其他对象的列表中。

    另一种方法是:

    反序列化前的版本化序列化和转换
    如上所述序列化您的对象状态(使用版本属性)。
    反序列化时,如果这不是您期望的版本,请查看版本属性,将带有 xsl 脚本或 c# 代码的序列化对象状态转换为当前版本。 您可以在当前项目中保存 xsl 转换列表

    - conversions
        - v1-v2
        - v2-v3
    

    如果您当前使用的是版本 3,并且想要加载您的 xml 文件,请查看版本属性并运行所有 xsl 脚本以获取您当前的版本(版本 3)。所以你会运行 xsl-script v1-v2,然后运行 ​​v2-v3。

    在这种情况下,您可以拥有不必关心向后功能的正常序列化和反序列化类。

    【讨论】:

      【解决方案2】:

      序列化类的另一种方法是使用 ADO.NET 数据集进行数据存储,该数据集具有用于持久保存到 XML 文件的内置设施。代码将是最少的,您可以通过设计适合您正在执行的操作模型的表来仅存储您需要的相关数据。此外,如果您决定稍后将 UI 状态保存到数据库而不是本地文件,您将能够使用相同的代码。您只需要一个备用函数来保存数据集。

      【讨论】:

      • 感谢您的想法,但除非我绝对需要它,否则我会将所有内容都保存在内存中(性能问题),但是我认为 ADO.NET 在内存中获得了某种结构支持。
      【解决方案3】:

      这是一个绑定对象和一些祖先的示例 到用户界面;此处使用 C# 3.0 纯粹是为了简洁 - 一切都适用于 C# 2.0。

      这里的大部分代码是设置表单,和/或 处理属性更改通知 - 重要的是,没有任何代码专门用于更新 来自对象模型的 UI,或来自 用户界面。

      还要注意 IDE 可以做很多数据绑定代码 对你来说,只需将 BindingSource 放到 表单并将 DataSource 设置为通过 属性网格中的对话框。

      请注意,提供属性更改不是必需的 通知(PropertyChanged 的​​东西) - 但是, 大多数 2-way UI 绑定将工作得更好 如果你确实实现了这一点。不是 PostSharp 有一些 用最少的代码做到这一点的有趣方式。

      using System;
      using System.Collections.Generic;
      using System.ComponentModel;
      using System.IO;
      using System.Windows.Forms;
      using System.Xml.Serialization;
      static class Program { // formatted for vertical space
          [STAThread]
          static void Main() {
              Application.EnableVisualStyles();
      
              Button load, save, newCust;
              BindingSource source = new BindingSource { DataSource = typeof(Customer) };
              XmlSerializer serializer = new XmlSerializer(typeof(Customer));
              using (Form form = new Form {
                  DataBindings = {{"Text", source, "Name"}}, // show customer name as form title
                  Controls = {
                      new DataGridView { Dock = DockStyle.Fill, // grid of orders
                          DataSource = source, DataMember = "Orders"},
                      new TextBox { Dock = DockStyle.Top, ReadOnly = true, // readonly order ref
                          DataBindings = {{"Text", source, "Orders.OrderRef"}}},
                      new TextBox { Dock = DockStyle.Top, // editable customer name
                          DataBindings = {{"Text", source, "Name"}}},
                      (save = new Button { Dock = DockStyle.Bottom, Text = "save" }),
                      (load = new Button{ Dock = DockStyle.Bottom, Text = "load"}),
                      (newCust = new Button{ Dock = DockStyle.Bottom, Text = "new"}),   
                  }
              })
              {
                  const string PATH = "customer.xml";
                  form.Load += delegate {
                      newCust.PerformClick(); // create new cust when loading form
                      load.Enabled = File.Exists(PATH);
                  };
                  save.Click += delegate {
                      using (var stream = File.Create(PATH)) {
                          serializer.Serialize(stream, source.DataSource);
                      }
                      load.Enabled = true;
                  };
                  load.Click += delegate {
                      using (var stream = File.OpenRead(PATH)) {
                          source.DataSource = serializer.Deserialize(stream);
                      }
                  };
                  newCust.Click += delegate {
                      source.DataSource = new Customer();
                  };
                  Application.Run(form);
              } 
          }
      }
      
      [Serializable]
      public sealed class Customer : NotifyBase {
          private int customerId;
          [DisplayName("Customer Number")]
          public int CustomerId {
              get { return customerId; }
              set { SetField(ref customerId, value, "CustomerId"); }
          }
      
          private string name;
          public string Name {
              get { return name; }
              set { SetField(ref name, value, "Name"); }
          }
      
          public List<Order> Orders { get; set; } // XmlSerializer demands setter
      
          public Customer() {
              Orders = new List<Order>();
          }
      }
      
      [Serializable]
      public sealed class Order : NotifyBase {
          private int orderId;
          [DisplayName("Order Number")]
          public int OrderId  {
              get { return orderId; }
              set { SetField(ref orderId, value, "OrderId"); }
          }
      
          private string orderRef;
          [DisplayName("Reference")]
          public string OrderRef {
              get { return orderRef; }
              set { SetField(ref orderRef, value, "OrderRef"); }
          }
      
          private decimal orderValue, carriageValue;
      
          [DisplayName("Order Value")]
          public decimal OrderValue {
              get { return orderValue; }
              set {
                  if (SetField(ref orderValue, value, "OrderValue")) {
                      OnPropertyChanged("TotalValue");
                  }
              }
          }
      
          [DisplayName("Carriage Value")]
          public decimal CarriageValue {
              get { return carriageValue; }
              set {
                  if (SetField(ref carriageValue, value, "CarriageValue")) {
                      OnPropertyChanged("TotalValue");
                  }
              }
          }
      
          [DisplayName("Total Value")]
          public decimal TotalValue { get { return OrderValue + CarriageValue; } }
      }
      
      [Serializable]
      public class NotifyBase { // purely for convenience
          [field: NonSerialized]
          public event PropertyChangedEventHandler PropertyChanged;
      
          protected bool SetField<T>(ref T field, T value, string propertyName) {
              if (!EqualityComparer<T>.Default.Equals(field, value)) {
                  field = value;
                  OnPropertyChanged(propertyName);
                  return true;
              }
              return false;
          }
          protected virtual void OnPropertyChanged(string propertyName) {
              PropertyChangedEventHandler handler = PropertyChanged;
              if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
          }
      }
      

      【讨论】:

      • 感谢代码,看起来这回答了我的问题,我会尝试实施这样的解决方案,看看我是否能成功。我见过 postsharp,但我不太确定这是否是一个好方法。
      【解决方案4】:

      这是一篇关于如何使您的类或结构可序列化的精彩文章。我将创建一个类,允许您存储所需的所有数据。使类可序列化。这样,只需几行代码,您就可以将所有数据保存到文件中。然后,只需再添加几行代码,您就可以从文件中检索数据。

      http://www.codeproject.com/KB/cs/objserial.aspx

      【讨论】:

        【解决方案5】:

        使用起来相当简单 将对象模型绑定到的数据绑定 用户界面。

        如何在没有持久存储的情况下将对象与 GUI 控件联系起来?如果我手动执行,这意味着我必须为内存中的每个对象编写大量的代码。我已经为这些数据提供了某种类存储,但它不是绑定排序场景,就像在这里读这个写一样。

        我是否应该编写一个加载器来加载序列化的 XML 并获取对象,然后读取对象并填充整个 GUI?显然这更像是手动加载而不是绑定。我错过了什么吗?

        【讨论】:

        • 我在火车上放一个例子;将在几个小时后回复。抱歉耽搁了:需要睡觉;-p
        • 感谢您的示例,它看起来很棒。我会挖的。也很好奇您对此使用 Postsharp 的看法。
        【解决方案6】:

        是的,您绝对应该为此使用 XML 序列化。但正如 Marc Gravell 所指出的,您必须首先拥有保存 GUI 组件显示的数据的对象。然后,您实际上可以用最少的代码行自动(反)序列化。

        【讨论】:

          【解决方案7】:

          理想情况下,您不应该保留 UI 状态;你应该坚持代表你的数据的一些对象模型的状态。除了TreeView,使用数据绑定将对象模型绑定到 UI 是相当简单的。这可以是基于DataTable 的方法,也可以是自定义类层次结构(我的偏好)。

          从 UI 中分离数据后,保存数据就很简单了。 XmlSerializer 等有很多例子。

          【讨论】:

          • 我不太确定该怎么做,我从未在控件和类之间使用数据绑定(我只将它用于持久存储、设置等)。我会调查的。
          • 你能给我或指出一个例子来说明对象和控件之间的这种排序绑定吗?
          猜你喜欢
          • 1970-01-01
          • 2018-02-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多