[code] // A GoSubGraph that adds a Lamp shape, positioned immediately to the
// right of the Handle, with the Label just to the right of the Lamp.
[Serializable]
public class LampSubGraph : GoSubGraph {
public LampSubGraph() {
// create a Lamp – could be any kind of object, including a group,
// but is currently assumed to be a GoShape
GoEllipse e = new GoEllipse();
e.Selectable = false;
e.Size = new SizeF(10, 10);
e.Pen = null;
e.BrushColor = Color.Black;
Add(e);
AddChildName(“Lamp”, e);
}
public GoShape Lamp {
get { return FindChild("Lamp") as GoShape; }
}
// this is needed to handle the case when there are no child nodes--
// gotta have something when the subgraph is collapsed
protected override GoObject CreateCollapsedObject() {
GoShape s = new GoRectangle();
s.Visible = false;
s.Printable = false;
s.Pen = null;
return s;
}
public override bool PaintsDecoration(GoView view) {
return true; // always paint the border, even though there's a CollapsedObject
}
// ignore GoSubGraph.LabelSpot & CollapsedLabelSpot:
// layout Label in LayoutHandle instead, along with Handle and Lamp
public override void LayoutLabel() {}
// layout Handle, Lamp, and Label, all in a row outside of the inner area
public override void LayoutHandle() {
GoSubGraphHandle hnd = this.Handle;
GoShape lamp = this.Lamp;
GoText lab = this.Label;
RectangleF rect;
if (this.IsExpanded) {
rect = ComputeInsideMargins(null);
} else {
SizeF maxsize = ComputeCollapsedSize(true);
rect = ComputeCollapsedRectangle(maxsize);
}
PointF p = rect.Location;
if (hnd != null) {
// move Handle only when expanded
if (this.IsExpanded) {
hnd.Position = p;
} else {
p = hnd.Position;
}
p.X += hnd.Width + 3; // spacing between Handle and Lamp
}
if (lamp != null) {
lamp.Position = p;
p.X += lamp.Width + 3; // spacing between Lamp and Label
}
if (lab != null) {
lab.Position = p;
}
}
public SizeF ComputeHeaderSize() {
GoSubGraphHandle hnd = this.Handle;
GoShape lamp = this.Lamp;
GoText lab = this.Label;
float w = 0;
float maxh = 0;
if (hnd != null) {
w += hnd.Width;
maxh = Math.Max(maxh, hnd.Height);
}
if (lamp != null) {
w += 3 + lamp.Width; // include spacing too
maxh = Math.Max(maxh, lamp.Height);
}
if (lab != null) {
w += 3 + lab.Width; // include spacing too
maxh = Math.Max(maxh, lab.Height);
}
return new SizeF(w, maxh);
}
public override RectangleF ComputeInsideMargins(GoObject ignore) {
SizeF hsize = ComputeHeaderSize();
if (this.IsExpanded) {
RectangleF r = base.ComputeInsideMargins(ignore);
return new RectangleF(r.X, r.Y - hsize.Height, Math.Max(r.Width, hsize.Width), r.Height + hsize.Height);
} else {
PointF hpos = ComputeReferencePoint();
return new RectangleF(hpos.X, hpos.Y, hsize.Width, hsize.Height);
}
}
// the following overrides are for ignoring the new Lamp child object
protected override bool ComputeInsideMarginsSkip(GoObject child) {
if (child == this.Lamp) return true;
// need to skip Label too, since we're positioning it outside the inner area
// where the nodes are
if (child == this.Label) return true;
return base.ComputeInsideMarginsSkip(child);
}
protected override void SaveChildBounds(GoObject child, RectangleF sgrect) {
if (child == this.Lamp) return;
base.SaveChildBounds(child, sgrect);
}
protected override void CollapseChild(GoObject child, RectangleF sgrect) {
if (child == this.Lamp) return;
base.CollapseChild(child, sgrect);
}
}[/code]
-
OK, the Handle/Lamp/Label (now called the “header”) are now inside the border. Your screenshot didn’t show any border, so I assumed that you wanted everything outside the border.
-
Override GoObject.CreateBoundingHandle to return what you want.
-
That’s done by overriding GoSubGraph.PaintsDecoration to always return true, as above.
For simple “lamps”, such as the ellipse that you seem to have, you could easily implement a simpler solution than above by not creating and adding a shape object to the subgraph. Adding that object that is laid out specially and is not a child node or link complicates the implementation, as you can see by the additional overrides that were required.
An alternative is to just override Paint to draw the ellipse. That’s real easy for simple shapes for which you can easily call the desired Graphics method(s). But you’d need to implement a property to hold the color if you want to change the color dynamically.
However, using a real GoObject as the “lamp” gives you a lot more flexibility in both appearance and behavior. You could have arbitrarily complex objects instead, and they could be modified dynamically, including changing their sizes. For example you can easily change the color of the ellipse by:
lsg.Lamp.BrushColor = Color.Red
You could use a GoDrawing or a GoImage just as easily. On the other hand, implementing GoDrawing functionality in a Paint method would be hard.