Nodes on Nodes

My client would like to know if we was create a node that contains some number of boxes on it. A block represents a device operation. That device has storage locations and we would like to show those locations.

I do not want to create a nodetype for each device as I have no idea how many there will be or what they will be.
It would look something like this.
Sure, you could do that.
Do the boxes on the left and right always line up?
Do the boxes always appear in pairs (or could you have 3 on the left and 2 on the right, for example?)
Are the boxes "active" in that clicking on them or drag/drop or other user interaction does something box-specific?
forgot a question: Do you want labels for each row of boxes? or is the label for the node (as in your example) what you want?
Do the boxes on the left and right always line up?
It doesn't really matter if they do.
Do the boxes always appear in pairs (or could you have 3 on the left and 2 on the right, for example?)
No they do not always appear in pairs. There could be 1 storage location or there could be 10.
Are the boxes "active" in that clicking on them or drag/drop or other user interaction does something box-specific?
Yes. I think we would at least want to be able to right click on them and launch a dialog.
Do you want labels for each row of boxes? or is the label for the node (as in your example) what you want?
Each box will likely require a label.
Try this "StorageNode" class... (Updated to remove myLeftBoxes/myrightBoxes)
using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
using Northwoods.Go;
namespace Demo1 {
[Serializable]
public class StorageNode : GoBasicNode {
public StorageNode()
{
this.Add(new GoListGroup()); // LeftBoxes
this.Add(new GoListGroup()); // RightBoxes
}
public void Initialize() {
LabelSpot = GoObject.Middle;
this.Text = "Try this";
this.MiddleLabelMargin = new SizeF(200, 100); // makes the node a reasonably big, fixed size.
LeftBoxes.Selectable = false;
LeftBoxes.Spacing = 5.0F; // adds space between boxes
RightBoxes.Selectable = false;
RightBoxes.Spacing = 5.0F;
}
protected override GoShape CreateShape(GoPort p)
{
// create the bigger circle/ellipse around and behind the port
LinearGradientRoundedRectangle rr4 = new LinearGradientRoundedRectangle(); // from Demo1
rr4.Bounds = new RectangleF(825, 190, 100, 50);
rr4.EndColor = Color.LightBlue;
rr4.Pen = Pens.LightBlue;
rr4.Selectable = false;
return rr4;
}
public GoObject AddBox(string label, bool left)
{
// a box is a GoRectangle and a GoText parented by a GoGroup
// the group is then added to the left or right GoListGroup
GoText t = new GoText();
t.Text = label;
t.Selectable = false;
GoRectangle r = new GoRectangle();
r.Selectable = false;
r.Size = new SizeF(60, 20);
GoGroup g = new GoGroup();
g.Selectable = false;
g.Add(r);
g.Add(t);
t.SetSpotLocation(MiddleCenter, r, MiddleCenter);
if (left)
{
this.LeftBoxes.Add(g);
}
else
{
this.RightBoxes.Add(g);
}
return g;
}
public override void LayoutChildren(GoObject childchanged)
{
if (this.Count < 5) return; // avoid layout until all parts added
base.LayoutChildren(childchanged);
if (this.Shape != null)
{
float vpad = 10;
float hpad = 20;
LeftBoxes.SetSpotLocation(TopLeft, this.Shape, TopLeft, hpad, vpad);
RightBoxes.SetSpotLocation(TopRight, this.Shape, TopRight, -hpad, vpad);
}
}
public GoListGroup LeftBoxes
{
get { return this[2] as GoListGroup; }
}
public GoListGroup RightBoxes
{
get { return this[3] as GoListGroup; }
}
}
}
Sample usage:
StorageNode sn = new StorageNode();
sn.Initialize();
sn.Location = new PointF(200, 250);
sn.AddBox("A", true);
sn.AddBox("B", true);
doc.Add(sn);
sn = new StorageNode();
sn.Initialize();
sn.Location = new PointF(400, 250);
sn.AddBox("A", true);
sn.AddBox("B", true);
sn.AddBox("C", true);
sn.AddBox("D", true);
sn.AddBox("X", false);
sn.AddBox("Y", false);
doc.Add(sn);
I am having trouble adding other types of objects to my Node. Also I have tried setting the size of the object I have assigned to the background and the size of my node but it never comes out looking like I expect it to.
What I would like is a node with an Image at the topLeft and text at the TopRight.
Do I need to use a GroupList? When I add an image I get a box around my node and the image is in that box not in the node?
THis is one of the version of what I end up with. I must be missing something.
I should be able to add the boxes below that using your example below.

Can you show us a mockup (e.g. from Paint or powerpoint) that shows what you want?

Does this give you the idea. Don’t mind my poor PaintBrush skills. :)

[code]
[Serializable]
public class StorageNodeB : GoBasicNode {
public StorageNodeB() {
this.Add(new GoListGroup()); // LeftBoxes
this.Add(new GoListGroup()); // RightBoxes
this.Add(new GoImage()); // icon
}
public void Initialize() {
LabelSpot = GoObject.Middle;
this.MiddleLabelMargin = new SizeF(20, 20);
LeftBoxes.Selectable = false;
LeftBoxes.Spacing = 5.0F; // adds space between boxes
RightBoxes.Selectable = false;
RightBoxes.Spacing = 5.0F;
}
protected override GoText CreateLabel(string name) {
GoText label = base.CreateLabel(name);
label.Multiline = true;
return label;
}

protected override GoShape CreateShape(GoPort p) {
// create the bigger circle/ellipse around and behind the port
LinearGradientRoundedRectangle rr4 = new LinearGradientRoundedRectangle(); // from Demo1
rr4.Bounds = new RectangleF(825, 190, 100, 50);
rr4.EndColor = Color.LightBlue;
rr4.Pen = Pens.LightBlue;
rr4.Selectable = false;
return rr4;
}
public GoObject AddBox(string label, bool left) {
// a box is a GoRectangle and a GoText parented by a GoGroup
// the group is then added to the left or right GoListGroup
GoText t = new GoText();
t.Text = label;
t.Selectable = false;
GoRectangle r = new GoRectangle();
r.Selectable = false;
r.Size = new SizeF(40, 40);
GoGroup g = new GoGroup();
g.Selectable = false;
g.Add(r);
g.Add(t);
t.SetSpotLocation(MiddleLeft, r, MiddleRight, 15, 0);
if (left) {
this.LeftBoxes.Add(g);
} else {
this.RightBoxes.Add(g);
}
return g;
}
public override void LayoutChildren(GoObject childchanged) {
if (this.Count < 6) return; // avoid layout until all parts added
GoShape back = this.Shape;
GoText label = this.Label;
if (back != null && label != null) {
SizeF margin = this.MiddleLabelMargin;
PointF center = back.Center;
float hpad = margin.Width * 3;
float newWidth = Math.Max(hpad + Image.Width + label.Width, hpad + LeftBoxes.Width + RightBoxes.Width);
float newHeight = margin.Height + Math.Max(label.Height, Image.Height) + margin.Height + LeftBoxes.Height + margin.Height;
back.Bounds = new RectangleF(center.X - newWidth / 2, center.Y - newHeight / 2, newWidth, newHeight);
Image.SetSpotLocation(TopLeft, back, TopLeft, margin.Width, margin.Height);
label.SetSpotLocation(TopLeft, this.Image, TopRight, margin.Width, 0);
LeftBoxes.SetSpotLocation(TopLeft, this.Image, BottomLeft, 0, margin.Height);
RightBoxes.SetSpotLocation(TopLeft, this.LeftBoxes, TopRight, margin.Width, 0);
if (this.Port != null)
this.Port.Bounds = back.Bounds;
}
}
public GoListGroup LeftBoxes {
get { return this[2] as GoListGroup; }
}
public GoListGroup RightBoxes {
get { return this[3] as GoListGroup; }
}
public GoImage Image {
get { return this[4] as GoImage; }
}
}
[/code]

Some further comments on last night’s post…

There isn't a "right" way to put the parts of a node together, there are always options.
Here, I'm using the Label from GoBasicNode in a way that isn't exactly legit... the LayoutChildren is ignoring the LabelSpot = GoObject.Middle.
I could have derived StorageNodeB from StorageNode, but in the interest of keeping the code all in one place for sample purposes, I duplicated a bunch of StorageNode.
GoListGroups are a huge time saver when they meet your node layout needs.
So the LayoutChildren is the essential difference here... I place the icon, the label and the Left/Right GoListGroup boxes.
This LayoutChildren also computes the Bounds for the node, something StorageNode didn't do (it really should have...).
I could also use AddChildName to name the Image and Boxes.
This node doesn't have any provision for dropping an icon in by the Plate A label.... you could replace the GoRectangle with a GoImage.

Thanks for the example.

I am trying to work it in right now. I am using GoTextNode as my base class and therefore some of the properties and overrides are not there for different.
  • MiddleLabelMargin
  • CreatShape
  • CreateLabel
Any suggestions?
Dave

FYI…

The way I was handling the gradiant blue background was inheriting from GoRoundedRectangle and setting that to the Background property of my GoTextNode.
So the Shape stuff in here does not seem to work for me.

GoText has TopLeftMargin and BottomRightMargin, you could substitute those. (or just hardcode some margins)

GoText has CreateBackground instead of CreateShape.
I just override CreateLabel to set the multiline flag.

I’m a bit confused by this line.

if (this.Port != null)
this.Port.Bounds = back.Bounds;
GoTextNode does not have a Port property. What exactly are you doing here so I can replace it with the equivalent.
Dave

GoBasicNode either has a little port in the middle of the node, or the background object is the Port. This line of code (used when the label is over the middle of the node) sets the bounds of the background object to be the active “port” for the Node. GoTextNode has a different port set up, so it has no equivalent.

Can you explain this Code:

this.Add(new GoListGroup());
this.Add(new GoListGroup());
this.Add(new GoImage());
public GoListGroup NestBoxes
{
get { return this[2] as GoListGroup; }
}

public GoListGroup NestTextBoxes
{
get { return this[3] as GoListGroup; }
}

public GoImage Image
{
get { return this[4] as GoImage; }
}
How do I know they are index 2, 3, and 4?
The constructor has not executed when LayoutChildren is called therefore NestBoxes and Image are all Null.

Forget that last question. I created properties and private fields and made direct references to the boxes and Image instead of the index.

So I almost have it working at this point I am getting this.
Any idea why this is happening? I can send you my code if it helps.

The private members complicate serialization and object Copy.

Looks like the management of the background rectangle in the GoText has been messed up somehow, but that's just a guess.