Well, it’s late, and I should probably wait until tomorrow to look at this code again before I post it. But, since it’s probably already Monday for you, I’ll toss this over the wall tonight and hope it is of some help to you…
Note I pulled a couple of methods out of TreeDraggingTool and a bunch of code from TreeAppNode. The port now calls the parent node any time a link is added or removed to set the collapsible handle state.
[code]
/*
* Copyright © Northwoods Software Corporation, 1998-2007. All Rights
* Reserved.
*
* Restricted Rights: Use, duplication, or disclosure by the U.S.
* Government is subject to restrictions as set forth in subparagraph
* (c) (1) (ii) of DFARS 252.227-7013, or in FAR 52.227-19, or in FAR
* 52.227-14 Alt. III, as applicable.
*/
using System;
using System.Collections;
using System.Drawing;
using Northwoods.Go;
namespace Demo1 {
///
/// A MultiPortNode has an icon, a label, and some number of ports.
///
[Serializable]
public class CollapsibleMultiPortNode : GoIconicNode, IGoCollapsible {
///
/// Create an empty MultiPortNode--call to
/// create an icon and a label.
///
public CollapsibleMultiPortNode() {
this.DraggableLabel = true;
GoCollapsibleHandle h = new GoCollapsibleHandle();
h.Position = this.Position;
Add(h);
AddChildName("handle", h);
UpdateHandle();
}
///
/// Don't create any ports initially--these need to be added and positioned explicitly.
///
/// a by default
protected override GoPort CreatePort() {
if (this.Initializing) return null;
CollapsibleMultiPortNodePort p = new CollapsibleMultiPortNodePort();
return p;
}
public override void LayoutChildren(GoObject childchanged) {
base.LayoutChildren(childchanged);
GoObject h = FindChild("handle");
if (h == null || this.Icon == null) return;
h.SetSpotLocation(BottomRight, this.Icon, TopLeft);
}
///
/// Create and Add a port to this node at a particular position
/// relative to the icon.
///
///
/// the property is set to this value
/// in order to help distinguish between different ports
///
/// relative to the top-left corner of the icon
///
/// initial values for the port's
/// and properties
///
/// the result of the call to
public GoPort AddPort(int id, SizeF iconoffset, int linkspot) {
GoPort p = CreatePort();
if (p != null) {
p.UserFlags = id;
PointF pnt = this.SelectionObject.Position;
p.Center = new PointF(pnt.X + iconoffset.Width, pnt.Y + iconoffset.Height);
p.FromSpot = linkspot;
p.ToSpot = linkspot;
Add(p);
}
return p;
}
///
/// Update the positions of all the other ports before performing the
/// standard call to
///
///
///
protected override void OnChildBoundsChanged(GoObject child, RectangleF old) {
if (child != null && child == this.Icon && old.Width > 0 && old.Height > 0) {
RectangleF thisRect = this.Icon.Bounds;
float scaleFactorX = thisRect.Width / old.Width;
float scaleFactorY = thisRect.Height / old.Height;
foreach (GoObject obj in this) {
if (obj == this.Icon) continue;
RectangleF childRect = obj.Bounds;
float newRectx = thisRect.X + ((childRect.X - old.X) * scaleFactorX);
float newRecty = thisRect.Y + ((childRect.Y - old.Y) * scaleFactorY);
float newRectwidth = childRect.Width;
float newRectheight = childRect.Height;
if (obj.AutoRescales) {
newRectwidth *= scaleFactorX;
newRectheight *= scaleFactorY;
}
obj.Bounds = new RectangleF(newRectx, newRecty, newRectwidth, newRectheight);
}
}
base.OnChildBoundsChanged(child, old); // this will call LayoutChildren
}
public void PortHasChanged() {
// called by the port when a link is added or removed
// takes the place of the port observer in TreeAppNode.
SetExpanded(!HasAnyChildrenUnseen());
UpdateHandle();
}
protected void UpdateHandle() {
foreach (GoObject obj in this) {
GoCollapsibleHandle h = obj as GoCollapsibleHandle;
if (h != null) {
h.Visible = HasChildren();
h.Printable = h.Visible;
break;
}
}
}
public virtual bool HasChildren() {
//return true;
return this.Collapsible &&
(ChildrenAreDestinations ?
this.DestinationLinks.Count > 0 :
this.SourceLinks.Count > 0);
}
public virtual bool HasAnyChildrenUnseen() {
if (!this.Collapsible)
return false;
bool unseen = false;
foreach (IGoNode n in (ChildrenAreDestinations ? this.Destinations : this.Sources)) {
if (!n.GoObject.CanView()) {
unseen = true;
break;
}
}
return unseen;
}
// next 2 methods taken from TreeDraggingTool
// extend the collection by recursively adding all of the destination links and nodes
// of all of the collection's nodes and links
public static void AddSubtrees(IGoCollection sel) {
Hashtable coll = new Hashtable();
foreach (GoObject obj in sel) {
AddReachable(coll, obj as IGoNode);
}
foreach (GoObject obj in coll.Keys) {
sel.Add(obj);
}
}
// recurse through graph
private static void AddReachable(Hashtable coll, IGoNode inode) {
if (inode == null) return;
GoObject obj = inode.GoObject;
if (!coll.ContainsKey(obj)) {
coll.Add(obj, obj);
foreach (IGoLink ilink in inode.DestinationLinks) {
GoObject link = ilink.GoObject;
if (!coll.ContainsKey(link))
coll.Add(link, link);
AddReachable(coll, ilink.ToNode);
}
}
}
///
/// Make all child links and nodes not Visible.
///
///
/// This calls
/// to determine what should be made not visible.
///
public virtual void Collapse() {
if (!this.Collapsible) return;
SetExpanded(false);
GoCollection coll = new GoCollection();
coll.Add(this);
//TreeDraggingTool.AddSubtrees(coll);
AddSubtrees(coll);
foreach (GoObject obj in coll) {
if (obj == this) continue;
CollapsibleMultiPortNode tn = obj as CollapsibleMultiPortNode;
if (tn != null) {
// make this child not visible
tn.Visible = false;
tn.Printable = false;
// also update the visibility of any links coming into TN
foreach (IGoLink inlink in tn.SourceLinks) {
inlink.GoObject.Visible = false;
inlink.GoObject.Printable = false;
}
}
}
}
///
/// Make child links and nodes visible.
///
///
/// Edit the code to govern how many/which children should become visible.
///
public virtual void Expand() {
if (!this.Collapsible) return;
SetExpanded(true);
/*
// Choice one--only display immediate children:
foreach (GoObject obj in (ChildrenAreDestinations ? this.Destinations : this.Sources)) {
if (obj == this) continue;
CollapsibleMultiPortNode tn = obj as CollapsibleMultiPortNode;
if (tn != null) {
// make this child visible
tn.Visible = true;
tn.Printable = true;
// also update the visibility of any links coming into TN
foreach (IGoLink inlink in tn.SourceLinks) {
inlink.GoObject.Visible = true;
inlink.GoObject.Printable = true;
}
}
}
*/
// Choice two--display all children that had been Expanded
// when this node was Collapsed:
foreach (GoObject obj in (ChildrenAreDestinations ? this.Destinations : this.Sources)) {
if (obj == this) continue;
CollapsibleMultiPortNode tn = obj as CollapsibleMultiPortNode;
if (tn != null) {
// make this child visible
tn.Visible = true;
tn.Printable = true;
// also update the visibility of any links coming into TN
foreach (IGoLink inlink in tn.SourceLinks) {
inlink.GoObject.Visible = true;
inlink.GoObject.Printable = true;
}
// expand children, if they were expanded when collapsed
if (tn.IsExpanded)
tn.Expand();
}
}
/*
// Choice three--display all children
GoCollection coll = new GoCollection();
coll.Add(this);
TreeDraggingTool.AddSubtrees(coll);
foreach (GoObject obj in coll) {
if (obj == this) continue;
CollapsibleMultiPortNode tn = obj as CollapsibleMultiPortNode;
if (tn != null) {
// make this child visible
tn.Visible = true;
tn.Printable = true;
// also update the visibility of any links coming into TN
foreach (IGoLink inlink in tn.SourceLinks) {
inlink.GoObject.Visible = true;
inlink.GoObject.Printable = true;
}
}
}
*/
}
///
/// If , then , else .
///
public virtual void Toggle() {
if (HasAnyChildrenUnseen())
Expand();
else
Collapse();
}
///
/// Gets whether has been called.
///
public bool IsExpanded {
get { return myExpanded; }
}
protected void SetExpanded(bool e) {
bool old = myExpanded;
if (old != e) {
myExpanded = e;
Changed(ChangedExpanded, 0, old, NullRect, 0, e, NullRect);
UpdateHandle();
}
}
///
/// Gets or sets disabling of the collapse/expand behavior.
///
public virtual bool Collapsible {
get { return myCollapsible; }
set {
bool old = myCollapsible;
if (old != value) {
myCollapsible = value;
Changed(ChangedCollapsible, 0, old, NullRect, 0, value, NullRect);
UpdateHandle();
}
}
}
public override void ChangeValue(GoChangedEventArgs e, bool undo) {
switch (e.SubHint) {
case ChangedCollapsible:
this.Collapsible = (bool)e.GetValue(undo);
return;
case ChangedExpanded:
SetExpanded((bool)e.GetValue(undo));
return;
default:
base.ChangeValue(e, undo);
return;
}
}
public const int ChangedCollapsible = 1235;
public const int ChangedExpanded = 1236;
// change this from true to false if the tree goes in the opposite direction
private static bool ChildrenAreDestinations = true;
private bool myExpanded = true;
private bool myCollapsible = true;
}
///
///
///
[Serializable]
public class CollapsibleMultiPortNodePort : GoPort {
public CollapsibleMultiPortNodePort() {
this.Style = GoPortStyle.Ellipse;
this.Brush = null;
this.Size = new SizeF(8, 8);
this.FromSpot = NoSpot;
this.ToSpot = NoSpot;
this.AutoRescales = false;
}
///
/// This is a convenience property for treating the parent node as a .
///
public MultiPortNode MultiPortNode {
get { return this.Parent as MultiPortNode; }
}
///
/// This is a convenience property when the port's appearance is actually a .
///
///
/// Setting this to a non-null value will change the appropriately.
///
public GoImage PortImage {
get { return this.PortObject as GoImage; }
set {
this.PortObject = value;
if (value == null)
this.Style = GoPortStyle.Ellipse;
else
this.Style = GoPortStyle.Object;
}
}
///
/// Gets or sets the maximum number of links the user may draw to this port.
///
public int MaxLinks {
get { return myMaxLinks; }
set {
int old = myMaxLinks;
if (old != value && value >= 0) {
myMaxLinks = value;
Changed(ChangedMaxLinks, old, null, NullRect, value, null, NullRect);
}
}
}
///
/// This override also checks to see if is less
/// than the permissible value.
///
///
public override bool CanLinkFrom() {
return base.CanLinkFrom() &&
this.LinksCount < this.MaxLinks;
}
///
/// This override also checks to see if is less
/// than the permissible value.
///
///
public override bool CanLinkTo() {
return base.CanLinkTo() &&
this.LinksCount < this.MaxLinks;
}
public override void ChangeValue(GoChangedEventArgs e, bool undo) {
switch (e.SubHint) {
case ChangedMaxLinks:
this.MaxLinks = e.GetInt(undo);
return;
default:
base.ChangeValue(e, undo);
return;
}
}
public override void OnLinkChanged(IGoLink l, int subhint, int oldI, object oldVal, RectangleF oldRect, int newI, object newVal, RectangleF newRect) {
base.OnLinkChanged(l, subhint, oldI, oldVal, oldRect, newI, newVal, newRect);
CollapsibleMultiPortNode node = this.Parent as CollapsibleMultiPortNode;
if (node != null) node.PortHasChanged();
}
public const int ChangedMaxLinks = 2121;
// State
private int myMaxLinks = 999999;
}
}
[/code]