public void SaveXmlCollapsingRecords() {
CollapsingRecordXmlWriter xw = new CollapsingRecordXmlWriter();
xw.Objects = myView.Document;
using (StreamWriter file = new StreamWriter(@"C:\GoDiagram3\records.xml")) {
xw.Generate(file);
}
}
public void LoadXmlCollapsingRecords() {
myView.StartTransaction();
myView.Document.Clear();
CollapsingRecordXmlReader r = new CollapsingRecordXmlReader();
r.RootObject = myView.Document;
using (StreamReader file = new StreamReader(@"C:\GoDiagram3\records.xml")) {
r.Consume(file);
}
myView.FinishTransaction("loaded XML");
}
public class CollapsingRecordXmlReader : GoXmlReader {
public CollapsingRecordXmlReader() {
}
public override void RegisterTransformers() {
CollapsingRecordXmlWriter.RegisterTransformers(this);
}
}
public class CollapsingRecordXmlWriter : GoXmlWriter {
public CollapsingRecordXmlWriter() {
this.RootElementName = "doc";
}
public override void RegisterTransformers() {
RegisterTransformers(this);
}
internal static void RegisterTransformers(GoXmlReaderWriterBase rw) {
// create a prototype document
GoDocument doc = new GoDocument();
GoXmlBindingTransformer bt = new GoXmlBindingTransformer("doc", doc);
rw.AddTransformer(bt);
CollapsingRecordNode crn = new CollapsingRecordNode();
bt = new CollapsingRecordNodeTransformer(crn);
bt.IdAttributeUsedForSharedObjects = true;
bt.HandlesNamedPorts = true;
bt.HandlesChildren = true; // generates and consumes child objects
bt.ChildrenCollectionPath = "List"; // collection of children is held in this property
bt.AddBinding("Location", "Location");
bt.AddBinding("ItemWidth", "ItemWidth"); // is read too early, need to reset it to finish up
bt.AddBinding("Text", "Text");
rw.AddTransformer(bt);
CollapsingRecordNodeItemList crnil = new CollapsingRecordNodeItemList();
bt = new CollapsingRecordNodeItemListTransformer(crnil);
bt.HandlesNamedPorts = true;
bt.HandlesChildren = true; // generates and consumes child objects
bt.AddBinding("Text", "Text");
rw.AddTransformer(bt);
CollapsingRecordNodeItem crni = new CollapsingRecordNodeItem();
crni.Init("", "", false); // create a GoImage, so we can set the Name
bt = new GoXmlBindingTransformer(crni);
bt.HandlesNamedPorts = true;
bt.AddBinding("Text", "Text");
bt.AddBinding("Image", "Image.Name");
rw.AddTransformer(bt);
GoLink link = new GoLink();
bt = new GoXmlBindingTransformer(link);
bt.AddBinding("From", "FromPort");
bt.AddBinding("To", "ToPort");
rw.AddTransformer(bt);
}
}
public class CollapsingRecordNodeTransformer : GoXmlBindingTransformer {
public CollapsingRecordNodeTransformer(Object proto) : base(proto) { }
public override void GenerateBody(object obj) {
CollapsingRecordNode crn = (CollapsingRecordNode)obj;
bool first = true;
foreach (GoObject c in crn.List) {
if (first) {
first = false; // skip first CollapsingRecordNodeItem, which is the Header
} else {
this.Writer.GenerateObject(c);
}
}
}
protected override void GeneratePortReferences(object obj) {
CollapsingRecordNode crn = (CollapsingRecordNode)obj;
base.GeneratePortReferences(crn.List.Header);
}
protected override void ConsumePortReferences(object obj) {
CollapsingRecordNode crn = (CollapsingRecordNode)obj;
base.ConsumePortReferences(crn.List.Header);
}
public override void ConsumeObjectFinish(object obj) {
CollapsingRecordNode crn = (CollapsingRecordNode)obj;
crn.ItemWidth = crn.ItemWidth; // reset it now that the items are present
}
}
public class CollapsingRecordNodeItemListTransformer : GoXmlBindingTransformer {
public CollapsingRecordNodeItemListTransformer(Object proto) : base(proto) {}
public override void GenerateBody(object obj) {
CollapsingRecordNodeItemList list = (CollapsingRecordNodeItemList)obj;
bool first = true;
foreach (GoObject c in list) {
if (first) {
first = false; // skip first CollapsingRecordNodeItem, which is the Header
} else {
this.Writer.GenerateObject(c);
}
}
}
protected override void GeneratePortReferences(object obj) {
CollapsingRecordNodeItemList list = (CollapsingRecordNodeItemList)obj;
base.GeneratePortReferences(list.Header);
}
protected override void ConsumePortReferences(object obj) {
CollapsingRecordNodeItemList list = (CollapsingRecordNodeItemList)obj;
base.ConsumePortReferences(list.Header);
}
}
Since we’re duplicating the “item†information for the “headers†of the CollapsingRecordNode and CollapsingRecordNodeItemList,
we need to do the same for the ports on each item.
That’s why I overrode the ...PortReferences methods to operate on the Header instead of on the object (...Node or ...NodeItemList) itself.
In retrospect, it might have been clearer and simpler to just change the definition of the CollapsingRecord... classes,
instead of complicating the implementation of the XML transformers by overriding all those methods to treat the Header specially.
But the advantage of this is that you don’t need to write out brush and font and size information for each item.
Those properties are determined entirely by the implementation in code.
On the other hand, moving some of the visual customizations into the XML gives you more flexibility to control the appearance of each item.
Those are policy decisions that you need to make for your applications.