Netron是一个C#开源图形库,可以帮助开发人员开发出类似Visio的作图软件。本文继前文”Netron开发快速上手(一)“讨论如何利用Netron里的序列化功能快速保存自己开发的图形对象。
一个用Netron开发的实际应用请看:发布一个免费开源软件-- PAD流程图绘制软件PADFlowChart
一、 Netron对象序列化
序列化Netron对象需要以下几个步骤:
- 添加序列化标签]Serializable]
[Serializable] public class BlockShape : AbstractFlowChartShape
- 实现ISerializable接口
如果是从Shape类或Entity类(Shape类的父类)继承,则已经继承了ISerializable接口,你需要做的就是重载GetObjectData方法,该方法用于序列化时被序列化过程调用,提供要序列化的数据
public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("m_leftConnector", m_leftConnector); … }
GetObjectData方法要先调用基类的方法,否则基类的数据不会被序列化。
用info.AddValue()加入你要序列化的数据
- 实现序列化的构造函数
类的序列化构造函数用于反序列化对象。当从磁盘读取序列化数据时,用于生成相应的对象。下面是个例子:
protected BlockShape(SerializationInfo info, StreamingContext context) : base(info, context) { m_leftConnector = (Connector)info.GetValue("m_leftConnector", typeof(Connector)); m_leftConnector.BelongsTo = this; Connectors.Add(m_leftConnector); … }
a) 注意要调用基类的序列化构造函数。
b) 用info.GetValue(“<数据名字>”,<数据类型>)来反序列化数据
- 必要时重载IEntity:PostDeserialization()方法
IEntity::PostDeserialization()方法将会在Netron的反序列化过程中被Netron.GraphLib.IO.Binary.BinarySerializer::UnwrapBundle()方法调用,调用的时机是所有对象建立以后。在这里你可以做一些初始化工作。因为反序列化时除了序列化构造函数其它的构造函数是不会被调用的。
二、 添加“打开/保存”代码
添加了图形对象的序列化代码后,你还需要在自己的应用程序中对“打开/保存”菜单命令添加相应的代码来打开/保存你的数据
- 打开文件:
GraphControl::Open()
- 保存文件:
GraphControl::SaveAs()
另外你可以用GraphControl::IsDirty来判断目前画布上的图形对象是否已经发生了改变需要保存;
GraphControl::OnDirtyChanged事件可以在画布上的内容发生改变后通知开发人员做相应的处理。
三、 Netron序列化过程分析
- Netron的序列化过程如下:
GraphControl:: SaveAs()
=>IO.Binary.BinarySerializer::SaveAs()
=> BinaryCapsule:: GetObjectData()
=>GraphAbstract:: GetObjectData()
而在GraphAbstract:: GetObjectData()里则分别序列化了其中的Shapes和Connections,对集合Shapes和Connections的序列化会导致Shape及Connection的GetObjectData()方法被调用。
- Netron的反序列化过程如下:
GraphControl:: Open
=> IO.Binary.BinarySerializer:: Open()
=> BinaryFormatter:: Deserialize()
BinaryFormatter:: Deserialize()将会在读取序列化文件时调用相应对象的序列化构造函数进行反序列化。和前述序列化过程相反,反序列化过程会先调用Shape和Connection的序列化构造函数生成Shapes和Connections集合,然后调用GraphAbstract的序列化构造函数生成GrapAbstract对象,然后生成BinaryCapsule对象。
随后IO.Binary.BinarySerializer:: Open()将调用UnwrapBundle()方法,使Connection和Shape的Connector连接起来。因为在Netron中,Shape通过Connection相连,而Connection的From/To都指向Shape的Connector成员,在序列化过程中,Connector对象是在Shape中进行序列化,而Connection则只序列化了From/To的UID(String类型); UnwrapBundle()方法将对每一个Connection对象查找和From UID/To UID匹配的Connector,然后将Connection的From/To指向相应的Connector对象。
最后,在UnwrapBundle()方法中还会调用Shape和Connection的PostDeserialization()。