Example IGoDragSnapper

The power, but not any appearance, of grids is actually in the IGoDragSnapper interface. GoGrid implements this interface, of course. But you can too. Here’s an example class deriving from GoSubGraph that limits how far the user can drag the subgraph children.
// A GoSubGraph is defined to have a border that surrounds all of its
// child objects, except for a few special children such as the Handle
// or the Label. So if a child is moved (whether interactively or
// programmatically) anywhere, the subgraph border is extended or contracted
// to just cover the needed area, plus margins.
// But sometimes you want to limit how far the children can be moved.
// This example class implements such behavior by implementing IGoDragSnapper,
// which in this case has SnapPoint making sure the position of the child
// object is always within the subgraph border.
// However, the user can expand the borders at will by moving one or both
// of the special “marker” child objects (LimitingSubGraphMarker), since
// CanSnapPoint is always false for such marker objects.
public class LimitingSubGraph : GoSubGraph, IGoDragSnapper {
public LimitingSubGraph() {
this.BorderPen = Pens.Black;
GoObject obj = new LimitingSubGraphMarker();
obj.Position = new PointF(10, 10);
obj = new LimitingSubGraphMarker();
obj.Position = new PointF(310, 310);
public bool CanSnapPoint(PointF p, GoObject obj, GoView view) {
// surprisingly, don’t care about the point P;
// just if it’s a child of this subgraph
return CanView() && obj.IsChildOf(this) && !(obj is LimitingSubGraphMarker);
public PointF SnapPoint(PointF p, GoObject obj, GoView view) {
// normalize the object’s Bounds as if the Location were always the Position
PointF loc = obj.Location;
RectangleF b = obj.Bounds;
b.X += (p.X-loc.X);
b.Y += (p.Y-loc.Y);
// position the object so that it fits inside the margins
RectangleF r = ComputeInsideMargins(this.Label);
if (b.Right > r.Right)
p.X -= (b.Right - r.Right);
if (b.Left < r.Left)
p.X += (r.Left - b.Left);
if (b.Bottom > r.Bottom)
p.Y -= (b.Bottom - r.Bottom);
if (b.Top < r.Top)
p.Y += (r.Top - b.Top);
return p;
public bool SnapOpaque {
get { return true; } // don’t care about any grids that might be underneath
public class LimitingSubGraphMarker : GoRectangle {
public LimitingSubGraphMarker() {
this.Printable = false;
this.Resizable = false;
this.Deletable = false;
this.Copyable = false;
this.Brush = null;
this.Pen = Pens.LightGray;
// can’t get any selection handles
public override void AddSelectionHandles(GoSelection sel, GoObject selectedObj) {}
// only seen when the mouse is over it
public override bool OnEnterLeave(GoObject from, GoObject to, GoView view) {
if (from == this)
this.Brush = null;
if (to == this)
this.Brush = Brushes.Gold;
return true;