题
我试图实现一个装载/保存能用于Windows的形式应用程序。
我已经得到了以下部分组成:
- 一棵树看
- 几个列表中的风景
- 几个文本的箱子
- 一对夫妇的对象(其中有一个很大的dictionarylist)
我想要实现一种方式来保存所有此进入一个文件,并且恢复/加载。
什么是最好的方式做到这一点?
我认为XML化的路要走,但我不是很确定如何,或者从哪里开始。或将它需要一个非常复杂的解决方案能够这样做吗?
解决方案
这里有一个例子,结合对象和一些祖先 到UI;使用C#3.0在这里是纯粹是为了简洁起见- 一切都会的工作C#2.0。
大多数这里的代码是设置的形式,以及/或 处理财产的更改通知- 重要的是,没有任何代致力于更新 UI对象模型,或者对象的模型 UI。
还注意到IDE可以做很多的数据结合代码 对你,只是通过删除一个BindingSource到 形成和设置的数据源,以一种类型的通过 该对话在财产的网格。
注意,它不是必要的,以提供性的改变 通知(所引发的东西)-但是, 最2路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));
}
}
其他提示
理想的是,你不应该坚持UI状态;你应该坚持国家的一些对象模型代表了你的数据。除 TreeView
, 它是相当微不足道的使用的数据结合配合对象模型的用户界面。这可能是一个 DataTable
-基础的方法,或一定类层次(我的偏好)。
一旦你有分离的数据从用户界面,节省的数据是简单的。有很多例子 XmlSerializer
等等。
是的,你应该肯定使用XML化。但是,正如马克*Gravell指出,必须有目的持有的数据显示通过你闺组第一次。然后你几乎可以让(de)自动化,以最小的代码行。
有一个问题的样本上。考虑,最终应用程序是更新。你的对象模型可能发生急剧变化,因此它不能获得deserialized.有些事你可以做到确保一个反xml版本1可以deserialized到您的对象模型版本2但是,如果有可能,你可以有很大的结构变化的xml反的是 不 的路要走。
如果是这种情况,并应用程序部署到顾客我会强烈建议采取进一步的看看你的节省/载的逻辑。
Versionized化/反
serialize你的对象国家的形式:
<ObjectState version="1">
<Field1>value</Field1>
... etc ...
</ObjectState>
所以现在你有版本的对象模型所产生的所保存的状态。在你的反可以采取特别的测量,以适应于这一事实。例如编写Field1价值在一个列表中的一些其他的对象。
另一种办法将是:
Versionized化和转换之前,反
serialize你的对象国作为上面提到的(与一个版本属性)。
当反序列化看版本属性,如果这不是你期望转化对象国与xsl-脚本或c#码给你的前版本。你可以保存一个清单xsl转换在当前项目
- conversions
- v1-v2
- v2-v3
如果你是目前在第3版和想载你xml文件的看版本属性和运行的所有xsl脚本以获得当前版本(第3版).所以你会跑xsl-脚本v1-v2和之后v2-3.
在这种情况下你可以拥有正常化,并反课程,必须不关心倒退的能力。
它是相当微不足道的使用 数据结合配合对象模型 UI。
我怎么能配合对象GUI控制没有一个持久性存储?如果我做手工,这意味着我必须写荒谬量的代码,用于每一个个单一的对象中存储器。我已经有某些种类的储存对于这种数据,但它不是结合进行排序的情况,它就像读这写在这里。
我应该写入一个装载机,它载列化的XML和获取的对象,然后阅读对象并填写了整个GUI?显然,这更像手册载入不具约束力。我失去了一些东西?
这里是一大篇关于如何使你的一类或结构的序列化。我会创造一级,这将允许你在商店的所有数据。使类serialable.这种方式在只有几行的代码你可以保存所有你的数据文件。然后只用一些更多线的代码你可以检索的数据文件。
一种替代将你的课程是使用一个ADO.NET 数据集的数据存储,这有内在的设施持续存在XML文件。代码将是最小的,你可以仅储存有关数据需要通过设计的表格,适合的模型的操作你的执行情况。此外,还将能够使用相同的代码如果你决定之后持续存在的UI国家一个数据库,而不是一个本地的文件。你只需要有一个备用功能来保存的数据集。