Back

I am having trouble getting my Node layed out the way I want it. Ultimatly I would like a bar across the top with a small icon and text which looks similar to a windows form. And an image on the right that I can click to make the node smaller or larger.
Then I want a larger icon and text below the title bar. I want to place an image in the background but have the title portion just a solid colour.
I have been able to put the objects on the Node but positioning them and getting the background to be the same size as the node proves to be impossible. Is setSpotLocation( ) the best way to position objects can't I just set their Location?
I have changed the size of the images but they always seem to show up the same size.
I must be missing something fundamental about the objectmodel it's getting very frustrating to try and paint a node the way I want it to look.
Please help. I would love to talk to someone on the phone if that is possible.

Yes, SetSpotLocation are very useful utility functions. Look back to StorageNodeB that I did for you a little while back to see how it’s used in LayoutChildren.

Looks to me like you just want StorageNodeB with a header... is that right?
Is the size of your node fixed by the size of the background image?

Yes that is what I would like.

No the background is not fixed by the image. The image is just a way to colour the background.

No wonder I am so confused. I just read the documentation on LayoutChildren.

I would like an easy way to define where the above objects go without using Layout Children. Can I not just define objects and their locations within the Node. And set the background.Bounds = Node.Bounds?
The soultion in StorageNodeB seems overly complex to me.

No, there’s no complete auto-layout of node parts in GoDiagram, if you are defining a custom node. (GoListGroup actually does make a lot of layout fairly trivial though… a StorageNode uses that to save a lot of work.)

The layout for StorageNodeB computes the width & height of all the objects, and sets the bounds. Then, it positions the 4 main objects against that background. To Add your title bar, just add some more margin at the top and add the titlebar objects across the top.

I don’t want to AutoLayout the Node. I want control over where all the objects I add to the node go, including the Background.

What I’m saying is there’s no way to avoid writing a LayoutChildren for the kind of node you want to define. It’s the piece of the code that defines the spacial relationships of the objects.

What I’m saying is there’s no way to avoid writing a LayoutChildren for the kind of node you want to define.

OK, I don't have the time right now to make this pretty, but here is StorageNodeB with a real basic Titlebar. Code in red is what I added to add the titlebar.
I added horizontal GoListGroup at the top, and added the height of the titlebar to the layout of the parts below it.
Edited: Code removed... see new code below.

Appearance cleaned up a bit, and Paint code for getting the top rounded rectangles added…

code in red is what was added to add TitleBar to original StorageNodeB
code in blue is the new Paint code.
Note I left the "spacer" rectangle visible in the titlebar so you could see it... you'd probably want to set the Pen to make that invisible.
[code]
[Serializable]
public class StorageNodeB : GoBasicNode {
public StorageNodeB() {
this.Add(new GoListGroup()); // LeftBoxes
this.Add(new GoListGroup()); // RightBoxes
this.Add(new GoImage()); // icon
this.Add(new GoListGroup()); // title bar
}
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;
CreateTitleBar();
}
public virtual void CreateTitleBar() {
TitleBar.Orientation = Orientation.Horizontal; // across the top
// top left image
GoImage icon = new GoImage();
icon.Name = @"C:\GoDiagramMySamples\Demo1\icon.ico";
TitleBar.Add(icon);
// title
GoText t = new GoText();
t.Text = "title goes here";
TitleBar.Add(t);
// spacer
GoRectangle spacer = new GoRectangle();
TitleBar.Add(spacer);
TitleBar.AddChildName("spacer", spacer);
// right hand side icon
GoImage icon2 = new GoImage();
icon2.Name = @"C:\GoDiagramMySamples\Demo1\ARW06LT.ICO";
TitleBar.Add(icon2);
// set appearance of bar
TitleBar.BrushColor = Color.DeepSkyBlue;
TitleBar.Spacing = 10.0F;
TitleBar.TopLeftMargin = new SizeF(5.0F, 3.0F);
TitleBar.BottomRightMargin = new SizeF(5.0F, 3.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 = TitleBar.Height + 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);
TitleBar.SetSpotLocation(TopLeft, back, TopLeft, 0, 0);
Image.SetSpotLocation(TopLeft, back, TopLeft, margin.Width, TitleBar.Height + 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);
GoRectangle r = (GoRectangle)TitleBar.FindChild("spacer");
r.Width = newWidth - TitleBar.Width + r.Width;
if (this.Port != null)
this.Port.Bounds = back.Bounds;
}
}
public override void Paint(Graphics g, GoView view) {
GraphicsState oldstate = g.Save();
GraphicsPath path = null;
LinearGradientRoundedRectangle lg = (LinearGradientRoundedRectangle)this.Shape;
if (lg != null) {
GoRoundedRectangle rr = new GoRoundedRectangle();
rr.Bounds = lg.Bounds;
rr.Corner = lg.Corner;
path = rr.MakePath();
Region reg = new Region(path);
g.IntersectClip(reg);
reg.Dispose();
}
base.Paint(g, view);
GoShape.DrawPath(g, view, lg.Pen, new SolidBrush(Color.Empty), path);
if (path != null) path.Dispose();
g.Restore(oldstate); // redraw GoListGroup decoration without clipping
//if (lg != null) lg.PaintDecoration(g, view);
}
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; }
}
public GoListGroup TitleBar {
get { return this[5] as GoListGroup; }
}
}
[/code]

I’m calling my CreateTitlebar method from the constructor but the Titlebar is not there yet when LayoutChildren is called.

Why can't i layout the objects at the end of the constructor. or in some event that is called after the node is created. I obviously don't understand layout children and it's use in your example.

Right…

the first line of LayoutChildren
if (this.Count < 6) return; // avoid layout until all parts added
should test to see if all the parts are there before you lay it out.
The parts are the Shape, the port, left and right boxes, the label, the icon. That's 6. The Titlebar adds 1 more. So, make the test "< 7".
Not sure why that I didn't hit that.

This is a maintenance nightmare.

Overriding this is not a great way to do it.
This method is called to layout each child yet you want to lay them out in relation to each other so you check the count of children so it only runs your code once all the children are present.
Why is there no event or method that fires at this time.
There must be a better way.
Dave

For groups that may have many children, overrides (of LayoutChildren) will often check the GoObject.Initializing flag. If true, this method usually does nothing; later when all the changes have been performed is that flag set to false and this method is called explicitly with a null argument.

Well, if you want a way more like how the Form designer explicitly places all the controls in a form, you could do:

[code] [Serializable]
public class StorageNodeC : GoBoxNode, IGoCollapsible {
public StorageNodeC() {
this.Port.Brush = null;
this.PortBorderMargin = new SizeF();

  GoListGroup body = new GoListGroup();
  body.Selectable = false;
  body.BorderPen = Pens.Black;
  body.LinePen = Pens.Black;
  body.Corner = new SizeF(5, 5);
  body.TopLeftMargin = new SizeF();
  body.BottomRightMargin = new SizeF();
  body.PickableBackground = true;

  GoListGroup titlebar = new GoListGroup();
  titlebar.Selectable = false;
  titlebar.Alignment = Middle;
  titlebar.Orientation = Orientation.Horizontal;
  titlebar.Brush = Brushes.DarkBlue;

  GoImage titleimage = new GoImage();
  titleimage.Selectable = false;
  titleimage.Name = "star.gif";
  titleimage.Size = new SizeF(16, 16);
  titlebar.Add(titleimage);

  GoText titletext = new GoText();
  titletext.Selectable = false;
  titletext.Text = "";
  titletext.TextColor = Color.White;
  titletext.Bold = true;
  titletext.AutoResizes = false;
  titletext.StringTrimming = StringTrimming.EllipsisCharacter;
  titlebar.Add(titletext);

  GoCollapsibleHandle titlehandle = new GoCollapsibleHandle();
  titlehandle.Selectable = false;
  titlehandle.Size = new SizeF(14, 14);
  titlebar.Add(titlehandle);

  body.Add(titlebar);

  GoGroup info = new GoGroup();
  info.Selectable = false;

  GoImage background = new GoImage();
  background.Selectable = false;
  background.Name = "sky.png";
  background.Size = new SizeF(200, 200);
  info.Add(background);

  body.Add(info);

  this.Body = body;
  this.Text = "Title";
}

public float HeaderWidth {
  get { return 200; }
}

public override String Text {
  get {
    GoListGroup lg = this.Header;
    GoText t = (GoText)lg[1];
    return t.Text;
  }
  set {
    GoListGroup lg = this.Header;
    GoText t = (GoText)lg[1];
    t.Text = value;
    // adjust width of header and width of background image accordingly
    t.Width = this.HeaderWidth - lg[0].Width - lg[2].Width;
    GoObject backimg = this.Info[0];
    backimg.Width = lg.Width;
  }
}

public GoListGroup ListGroup {
  get { return (GoListGroup)this.Body; }
}

public GoListGroup Header {
  get { return (GoListGroup)this.ListGroup[0]; }
}

public GoGroup Info {
  get { return (GoGroup)this.ListGroup[1]; }
}

public bool Collapsible {
  get { return true; }
  set { }
}

public bool IsExpanded {
  get { return this.Info.Visible; }
}

public void Collapse() {
  this.Info.Visible = false;
  this.ListGroup.LayoutChildren(null);
}

public void Expand() {
  this.Info.Visible = true;
  this.ListGroup.LayoutChildren(null);
}

public DropTarget AddInfo(String imagename, String text, float x, float y) {
  DropTarget dt = new DropTarget();
  dt.ImageName = imagename;
  dt.Text = text;
  PointF p = this.Info.Position;
  dt.Position = new PointF(p.X+x, p.Y+y);
  this.Info.Add(dt);
  return dt;
}

}

[Serializable]
public class DropTarget : GoListGroup {
public DropTarget() {
this.Selectable = false;
this.Orientation = Orientation.Horizontal;
this.Alignment = Middle;

  GoImage img = new GoImage();
  img.Selectable = false;
  img.AutoResizes = false;
  img.Name = "doc.gif";  // ?? or maybe img.Index = ...
  img.Size = new SizeF(20, 20);
  Add(img);

  GoText txt = new GoText();
  txt.Selectable = false;
  txt.Text = "text";
  Add(txt);
}

public override bool OnSelectionDropped(GoObjectEventArgs evt, GoView view) {
  MessageBox.Show("dropped: " + view.Selection.Primary.ToString() + " on " + this.Text);
  return true;
}

public String Text {
  get { return ((GoText)this[1]).Text; }
  set { ((GoText)this[1]).Text = value; }
}

public String ImageName {  // ?? or maybe this should be ImageIndex instead
  get { return ((GoImage)this[0]).Name; }
  set { ((GoImage)this[0]).Name = value; }
}

}[/code]

I also took the liberty of implementing a SelectionDropped handler for each “DropTarget”, and implementing the IGoCollapsible interface with a GoCollapsibleHandle.

Usage:

StorageNodeC snc = new StorageNodeC(); snc.Text = "Title"; snc.AddInfo("doc.gif", "Text", 10, 10); snc.AddInfo("star.gif", "nest", 30, 30); snc.AddInfo("star.gif", "empty", 30, 50); doc.Add(snc);

Sorry I don’t have time for a screenshot.

I’m using GoTextNode and it does not have a Body.

I'm trying to compare what your guys are giving me to what I have but it's not easy.

GoBasicNode has a (GoShape) Shape property as background, GoText has a (GoObject) Background property as background. Walter used BoxNode as the base class for his StorageNodeC, which like GoText uses any GoObject as Body. 3 different names, all essentially the same thing.

OK I got it.

One problem left I tried to add the code to round the corners of the Toolbar and I get errors with this line.
LinearGradientRoundedRectangle lg = (LinearGradientRoundedRectangle)this.Shape;
Apparently Shape is not a memeber of GoTextNode
And it can't find LinearGradientRoundedRectangle
What should I be using instead?

It’s defined in Demo1. You can substitute GoRoundedRectangle if you don’t want the Gradient.

Also I used Background instead of shape.

Thanks