Problems with Drag And Drop

Hi Support!

I create Pool Object and Lane Object, based on Planogrammer example ( as 2 racks objects ) , but there is something that I understood, I can´t move a Lane object inside Pool in a correct margin. The Planogrammer example has a Rack object with a label text in top of object. My problem is because Object Label is another position?

My code and a image:

//Diagram Lane
[Serializable]
public class Lane : LaneDisplay
{
//Variables
private static readonly Pen StandardPen = new Pen(Color.Black, 2);
private static readonly Pen HighlightPen = new Pen(Color.Green, 2);
private int CellGridSize = 3;

    //Constuctor
    public Lane()
    {
       this.Label.Text = "Lane";
    }

    //Create Grid
    protected override GoGrid CreateGrid()
    {
        GoGrid grid = new LaneGrid();
        grid.Selectable = false;
        grid.AutoRescales = true;
        grid.Resizable = true;
        grid.ResizesRealtime = true;
        grid.Bounds = new RectangleF(0, 0, 50, 50);
        grid.SnapDrag = GoViewSnapStyle.Jump;
        grid.SnapResize = GoViewSnapStyle.Jump;
        grid.CellSize = new SizeF(CellGridSize, CellGridSize);
        grid.Style = GoViewGridStyle.None;
        grid.Pen = new Pen(Color.Black, 2);
        grid.BrushColor = Color.White;

        return grid;
    }

    //Make sure the Lane's Label is positioned in left corner of the Grid; this makes it potentially useful 
    //for selection and dragging even if all of the grid is covered by Items
    public override void LayoutChildren(GoObject childchanged)
    {
        if (this.Count >= 2)
        {
            GoGrid grid = this.Grid;
            GoText label = this.Label;

            if (grid != null && label != null)
            {
                //Label in Left side
                label.Width = grid.Height;
                PointF center = grid.GetSpotLocation(MiddleLeft) - new SizeF(label.Height / 2 + 1, 0);
                label.Center = center;
            }
        }
    } 

    //The Location of a Lane is actually the Position of its Grid
    public override PointF Location
    {
        get { return this.Grid.Position; }
        set
        {
            SizeF off = GoTool.SubtractPoints(this.Grid.Position, this.Position);
            SizeF pos = GoTool.SubtractPoints(value, off);
            this.Position = new PointF(pos.Width, pos.Height);
        }
    }

    //Show a differente color when node is dragged into lane
    public override void SetHighlight(bool show)
    {
        GoGrid grid = this.Grid;
        if (grid != null)
        {
            if (show)
            {
                grid.Pen = Lane.HighlightPen;
            }
            else
            {
                grid.Pen = Lane.StandardPen;
            }
        }
    }

}

//Diagram Lane Grid
[Serializable]
public class LaneGrid : GoGrid
{
    //Variables
    private int CellGridSize = 3;

    //Constructor
    public LaneGrid() 
    {
    }

    //The grid in a Lane only snaps Items
    public override bool CanSnapPoint(PointF p, GoObject obj, GoView view)
    {
        if (!(obj is GraphNode))
        {
            return false;
        }
            
        // do standard checks, such as whether the grid is a child of the OBJ,
        // the value of the grid's snap style, whether the OBJ intersects with the grid,
        // and whether the mouse point is in the grid
        return base.CanSnapPoint(p, obj, view);

    }

    //It can be resized only in positive multiples of Item.UnitSize
    public override RectangleF ComputeResize(RectangleF origRect, PointF newPoint, int handle, SizeF min, SizeF max, bool reshape)
    {
        RectangleF r = base.ComputeResize(origRect, newPoint, handle, min, max, reshape);
        Lane lane = this.Parent as Lane;
        if (lane != null)
        {
            RectangleF rMin = new RectangleF();
            bool any = false;
            foreach (GoObject obj in lane.GetEnumerator())
            {
                if (obj is GraphNode)
                {
                    if (!any)
                    {
                        rMin = obj.Bounds;
                        any = true;
                    }
                    else
                    {
                        // add the object's bounding rect to this one
                        rMin = UnionRect(rMin, obj.Bounds);
                    }
                }
            }
            if (any) r = UnionRect(r, rMin);
        }

        r.Width = Math.Max(1, (float)Math.Round(r.Width / CellGridSize)) * CellGridSize;
        r.Height = Math.Max(1, (float)Math.Round(r.Height / CellGridSize)) * CellGridSize;

        return r;
    }

    static RectangleF UnionRect(RectangleF a, RectangleF b)
    {
        float minx = Math.Min(a.X, b.X);
        float miny = Math.Min(a.Y, b.Y);
        float maxr = Math.Max(a.X + a.Width, b.X + b.Width);
        float maxb = Math.Max(a.Y + a.Height, b.Y + b.Height);
        return new RectangleF(minx, miny, maxr - minx, maxb - miny);
    } 

}


//Diagram Lane Group Display
[Serializable]
public class LaneDisplay : GoGroup, IGoLabeledNode
{

    //Variables
    private int CellGridSize = 3;

    //Constructor
    protected LaneDisplay()
    {

        //The Grid is always first, in the background
        Add(CreateGrid());
        Add(CreateLabel());
    }

    //Create Grid
    protected virtual GoGrid CreateGrid()
    {
        GoGrid grid = new LaneGrid();
        grid.Selectable = false;
        grid.AutoRescales = true;
        grid.Resizable = true;
        grid.ResizesRealtime = true;
        grid.Bounds = new RectangleF(0, 0, 50, 50);
        grid.SnapDrag = GoViewSnapStyle.Jump;
        grid.SnapResize = GoViewSnapStyle.Jump;
        grid.CellSize = new SizeF(CellGridSize, CellGridSize);
        grid.Style = GoViewGridStyle.None;
        grid.Pen = new Pen(Color.Black, 2);
        grid.BrushColor = Color.White;
        return grid;
    }

    //Create label
    protected virtual GoText CreateLabel()
    {
        ////Label in Top Left
        //GoText label = new GoText();
        //    label.Selectable = false;
        //    label.Multiline = true;
        //    label.Editable = true;
        //    label.FontSize = 11;
        //    label.TransparentBackground = false;
        //    label.BackgroundColor = Color.White;
        //    label.Bordered = true;
        //    return label;




        //Label Left Side
        RotatedText label = new RotatedText();
        label.Selectable = false;
        label.Multiline = true;
        label.Editable = true;
        label.FontSize = 11;
        label.TransparentBackground = false;
        label.BackgroundColor = Color.White;
        label.Bordered = true;
        // settings for rotation. 
        label.AutoResizes = false;
        label.Alignment = Middle;
        label.Angle = 270;
        return label;
    } 

    public virtual GoText Label
    {
        get { return this[1] as GoText; }
    }

    //Get Grid
    public virtual GoGrid Grid
    {
        get { return this[0] as GoGrid; }
    }

    //IGoLabeledNode:
    //Get Label
    //Set and Get Label text
    public virtual String Text
    {
        get
        {
            GoText lab = this.Label;
            if (lab != null)
                return lab.Text;
            return null;
        }
        set
        {
            GoText lab = this.Label;
            if (lab != null)
                lab.Text = value;
        }
    }

    public override void DoBeginEdit(GoView view)
    {
        if (this.Label != null)
        {
            this.Label.DoBeginEdit(view);
        }
            
    }

    public override bool CanEdit()
    {
        return base.CanEdit();
    }

    //if the Label is not visible or if the view has a small DocScale,it's handy to be able to see the name of the lane
    public override String GetToolTip(GoView view)
    {
        return this.Text;
    }

    //Selection:

    //let users resize the grid, not the whole Lane
    public override GoObject SelectionObject
    {
        get { return this.Grid; }
    }

    public override bool CanSelect()
    {
        return base.CanSelect();
    }

    //Unlike normal GoGroups, PickObjects picks all children at a particular PointF, not just the "top" one
    public override IGoCollection PickObjects(PointF p, bool selectableOnly, IGoCollection coll, int max)
    {
        if (coll == null) coll = new GoCollection();
        if (coll.Count >= max) return coll;
        if (!CanView()) return coll;
        foreach (GoObject child in this.Backwards)
        {
            LaneDisplay disp = child as LaneDisplay;
            if (disp != null)
            {
                disp.PickObjects(p, selectableOnly, coll, max);
            }
            else
            {
                GoObject picked = child.Pick(p, selectableOnly);
                if (picked != null)
                {
                    coll.Add(picked);
                    if (coll.Count >= max) return coll;
                }
            }
        }
        return coll;
    }

    //Dragging:

    //when dragging a non-Display onto a Display, highlight it to indicate that the dropped object(s) will be added to the Display
    public override bool OnEnterLeave(GoObject from, GoObject to, GoView view)
    {
        if (from is LaneDisplay && from != this)
        {
            ((LaneDisplay)from).SetHighlight(false);
        }

        SetHighlight(view.Tool is GoToolDragging && to == this && !view.Selection.IsEmpty && IsAddable(view.Selection.Primary) && view.Selection.Primary.Parent != this);

        return true;
    }

    //Called after a GoToolDragging drop
    public override bool OnSelectionDropped(GoObjectEventArgs evt, GoView view)
    {
        view.Selection.AddRange(AddItems(view.Selection));
        return true;
    }

    //All ports on all nodes are hidden when the mouse hovers over the lane background.
    public override bool OnHover(GoInputEventArgs evt, GoView view)
    {
        foreach (GoObject obj in this.Backwards)
        {
            if (obj is GraphNode)
            {
                GraphNode n = obj as GraphNode;
                if (n != null)
                {
                    foreach (GoPort p in n.Ports)
                    {
                        p.Style = GoPortStyle.None;
                    }
                }
            }
        }

        return true;
    }

    //Don't add Lanes as children of this Display
    public virtual bool IsAddable(GoObject obj)
    {
        if (obj is Lane)
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    //Return a collection of objects that actually are added to this Lane
    public virtual IGoCollection AddItems(IGoCollection coll)
    {
        //Skip all dropped Lanes -- just include Items and GoText labels
        GoCollection items = new GoCollection();
        
        foreach (GoObject obj in coll)
        {
            if (IsAddable(obj))
            {
                items.Add(obj);
            } 
        }

        if (items.IsEmpty)
        {
            return items;
        } 

        IGoCollection added = AddCollection(items, false);

        return added;
    }

    //Visually distinguish this Display from others, to indicate that it can (or just did) add an Item
    public virtual void SetHighlight(bool show) 
    { 
    }

}


//Diagram Pool
[Serializable]
public class Pool : PoolDisplay
{
    //Variables
    private static readonly Pen StandardPen = new Pen(Color.Black, 2);
    private static readonly Pen HighlightPen = new Pen(Color.Green, 2);
    private int CellGridSize = 3;

    //Constuctor
    public Pool()
    {
        this.Label.Text = "Pool";
    }

    //Create Grid
    protected override GoGrid CreateGrid()
    {
        GoGrid grid = new PoolGrid();
        grid.Selectable = false;
        grid.AutoRescales = true;
        grid.Resizable = true;
        grid.ResizesRealtime = true;
        grid.Bounds = new RectangleF(0, 0, 50, 50);
        grid.SnapDrag = GoViewSnapStyle.Jump;
        grid.SnapResize = GoViewSnapStyle.Jump;
        grid.CellSize = new SizeF(CellGridSize, CellGridSize);
        grid.Style = GoViewGridStyle.None;
        grid.Pen = new Pen(Color.Black, 2);
        grid.BrushColor = Color.White;

        return grid;
    }

    //Make sure the Lane's Label is positioned in left corner of the Grid; this makes it potentially useful 
    //for selection and dragging even if all of the grid is covered by Items
    public override void LayoutChildren(GoObject childchanged)
    {
        if (this.Count >= 2)
        {
            GoGrid grid = this.Grid;
            GoText label = this.Label;

            if (grid != null && label != null)
            {
                //Label in Left side
                label.Width = grid.Height;
                PointF center = grid.GetSpotLocation(MiddleLeft) - new SizeF(label.Height / 2 + 1, 0);
                label.Center = center;
            }
        }
    }

    //The Location of a Lane is actually the Position of its Grid
    public override PointF Location
    {
        get { return this.Grid.Position; }
        set
        {
            SizeF off = GoTool.SubtractPoints(this.Grid.Position, this.Position);
            SizeF pos = GoTool.SubtractPoints(value, off);
            this.Position = new PointF(pos.Width, pos.Height);
        }
    }

    //Show a differente color when node is dragged into lane
    public override void SetHighlight(bool show)
    {
        GoGrid grid = this.Grid;
        if (grid != null)
        {
            if (show)
            {
                grid.Pen = Pool.HighlightPen;
            }
            else
            {
                grid.Pen = Pool.StandardPen;
            }
        }
    }

}


//Diagram Pool Grid
[Serializable]
public class PoolGrid : GoGrid
{
    //Variables
    private int CellGridSize = 3;

    //Constructor
    public PoolGrid()
    {
    }

    //The grid in a Poll only snaps Lanes
    public override bool CanSnapPoint(PointF p, GoObject obj, GoView view)
    {
        if (!(obj is Lane))
        {
            return false;
        }

        // do standard checks, such as whether the grid is a child of the OBJ,
        // the value of the grid's snap style, whether the OBJ intersects with the grid,
        // and whether the mouse point is in the grid
        return base.CanSnapPoint(p, obj, view);

    }

    public override RectangleF ComputeResize(RectangleF origRect, PointF newPoint, int handle, SizeF min, SizeF max, bool reshape)
    {
        RectangleF r = base.ComputeResize(origRect, newPoint, handle, min, max, reshape);
        Pool pool = this.Parent as Pool;
        if (pool != null)
        {
            RectangleF rMin = new RectangleF();
            bool any = false;
            foreach (GoObject obj in pool.GetEnumerator())
            {
                if (obj is Lane)
                {
                    if (!any)
                    {
                        rMin = obj.Bounds;
                        any = true;
                    }
                    else
                    {
                        // add the object's bounding rect to this one
                        rMin = UnionRect(rMin, obj.Bounds);
                    }
                }
            }
            if (any) r = UnionRect(r, rMin);
        }

        r.Width = Math.Max(1, (float)Math.Round(r.Width / CellGridSize)) * CellGridSize;
        r.Height = Math.Max(1, (float)Math.Round(r.Height / CellGridSize)) * CellGridSize;

        return r;
    }

    static RectangleF UnionRect(RectangleF a, RectangleF b)
    {
        float minx = Math.Min(a.X, b.X);
        float miny = Math.Min(a.Y, b.Y);
        float maxr = Math.Max(a.X + a.Width, b.X + b.Width);
        float maxb = Math.Max(a.Y + a.Height, b.Y + b.Height);
        return new RectangleF(minx, miny, maxr - minx, maxb - miny);
    } 

}



//Diagram Pool Group Display
[Serializable]
public class PoolDisplay : GoGroup, IGoLabeledNode
{

    //Variables
    private int CellGridSize = 3;

    //Constructor
    protected PoolDisplay()
    {
        //The Grid is always first, in the background
        Add(CreateGrid());
        Add(CreateLabel());
    }

    //Create Grid
    protected virtual GoGrid CreateGrid()
    {
        GoGrid grid = new PoolGrid();
        grid.Selectable = false;
        grid.AutoRescales = true;
        grid.Resizable = true;
        grid.ResizesRealtime = true;
        grid.Bounds = new RectangleF(0, 0, 50, 50);
        grid.SnapDrag = GoViewSnapStyle.Jump;
        grid.SnapResize = GoViewSnapStyle.Jump;
        grid.CellSize = new SizeF(CellGridSize, CellGridSize);
        grid.Style = GoViewGridStyle.None;
        grid.Pen = new Pen(Color.Black, 2);
        grid.BrushColor = Color.White;
        return grid;
    }

    //Create label
    protected virtual GoText CreateLabel()
    {

        //Label Left Side
        RotatedText label = new RotatedText();
        label.Selectable = false;
        label.Multiline = true;
        label.Editable = true;
        label.FontSize = 11;
        label.TransparentBackground = false;
        label.BackgroundColor = Color.White;
        label.Bordered = true;
        // settings for rotation. 
        label.AutoResizes = false;
        label.Alignment = Middle;
        label.Angle = 270;
        return label;
    }

    public virtual GoText Label
    {
        get { return this[1] as GoText; }
    }

    //Get Grid
    public virtual GoGrid Grid
    {
        get { return this[0] as GoGrid; }
    }

    //IGoLabeledNode:
    //Get Label
    //Set and Get Label text
    public virtual String Text
    {
        get
        {
            GoText lab = this.Label;
            if (lab != null)
                return lab.Text;
            return null;
        }
        set
        {
            GoText lab = this.Label;
            if (lab != null)
                lab.Text = value;
        }
    }

    public override void DoBeginEdit(GoView view)
    {
        if (this.Label != null)
        {
            this.Label.DoBeginEdit(view);
        }

    }

    public override bool CanEdit()
    {
        return base.CanEdit();
    }

    //if the Label is not visible or if the view has a small DocScale,it's handy to be able to see the name of the lane
    public override String GetToolTip(GoView view)
    {
        return this.Text;
    }

    //Selection:

    //let users resize the grid, not the whole Lane
    public override GoObject SelectionObject
    {
        get { return this.Grid; }
    }

    public override bool CanSelect()
    {
        return base.CanSelect();
    }

    //Unlike normal GoGroups, PickObjects picks all children at a particular PointF, not just the "top" one
    public override IGoCollection PickObjects(PointF p, bool selectableOnly, IGoCollection coll, int max)
    {
        if (coll == null) coll = new GoCollection();
        if (coll.Count >= max) return coll;
        if (!CanView()) return coll;
        foreach (GoObject child in this.Backwards)
        {
            PoolDisplay disp = child as PoolDisplay;
            if (disp != null)
            {
                disp.PickObjects(p, selectableOnly, coll, max);
            }
            else
            {
                GoObject picked = child.Pick(p, selectableOnly);
                if (picked != null)
                {
                    coll.Add(picked);
                    if (coll.Count >= max) return coll;
                }
            }
        }
        return coll;
    }

    //Dragging:

    //when dragging a non-Display onto a Display, highlight it to indicate that the dropped object(s) will be added to the Display
    public override bool OnEnterLeave(GoObject from, GoObject to, GoView view)
    {
        if (from is PoolDisplay && from != this)
        {
            ((PoolDisplay)from).SetHighlight(false);
        }

        SetHighlight(view.Tool is GoToolDragging && to == this && !view.Selection.IsEmpty && IsAddable(view.Selection.Primary) && view.Selection.Primary.Parent != this);

        return true;
    }

    //Called after a GoToolDragging drop
    public override bool OnSelectionDropped(GoObjectEventArgs evt, GoView view)
    {
        view.Selection.AddRange(AddItems(view.Selection));
        return true;
    }

    //Don't add pools as children of this Display
    public virtual bool IsAddable(GoObject obj)
    {
        if (obj is Lane)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    //Return a collection of objects that actually are added to this Pool
    public virtual IGoCollection AddItems(IGoCollection coll)
    {
        //Skip all dropped Lanes -- just include Items and GoText labels
        GoCollection items = new GoCollection();

        foreach (GoObject obj in coll)
        {
            if (IsAddable(obj))
            {
                items.Add(obj);
            }
        }

        if (items.IsEmpty)
        {
            return items;
        }

        IGoCollection added = AddCollection(items, false);

        return added;
    }

    //Visually distinguish this Display from others, to indicate that it can (or just did) add an Item
    public virtual void SetHighlight(bool show)
    {
    }

}

Grid snapping moves the Location of objects (when dragging) to a point in a grid cell. You can control which spot in the cell that is by setting the GridSnapCellSpot property.



Your Lane’s Location is defined to be this.Grid.Position.

Ok, but I should changes this code? I tried to modify Planogrammer example, but without sucess. In my cae, Pool object is Rack Object, and there is the same code:

  //The Location of a Pool is actually the Position of its Grid
    public override PointF Location
    {
        get { return this.Grid.Position; }
        set
        {
            SizeF off = GoTool.SubtractPoints(this.Grid.Position, this.Position);
            SizeF pos = GoTool.SubtractPoints(value, off);
            this.Position = new PointF(pos.Width, pos.Height);
        }
    }

But, I have another object, Lane, that I will insert into Pool, or not, and I don´t know the best code in Location for Lane. Can you help me? Thanks!

   //The Location of a Lane is actually the Position of its Grid
    public override PointF Location
    {
        get { return this.Grid.Position; }
        set
        {
            SizeF off = GoTool.SubtractPoints(this.Grid.Position, this.Position);
            SizeF pos = GoTool.SubtractPoints(value, off);
            this.Position = new PointF(pos.Width, pos.Height);
        }
    }

Sorry, we’re all out on Thanksgiving vacation… You should look at Swimlanes in NodeLinkDemo too. It may be easier than what you are trying to do here.

Hi Jake.

The problem appeared after use RotatedText class, to show Lane label in different position.

The Location code just work if my object Lane has Label in Top Left, but with this RotatedText label… I can´t drop in correct postion, near left Pool corner as my image:

//The Location of a Lane is actually the Position of its Grid
    public override PointF Location
    {
        get
        {
            return this.Grid.Position;
        }
        set
        {

            SizeF off = GoTool.SubtractPoints(this.Grid.Position, this.Position);
            SizeF pos = GoTool.SubtractPoints(value, off);
            this.Position = new PointF(pos.Width, pos.Height);
             
        }

    }

Try just removing the override for Location.

This is the first thing that I did ( commented Location of Lane class, and the same problem continnuous…

Now, I am not using Location override… in both case, with old code, Label in top left, perfect, I can move Lane object.

And without Location override too, but with new code, to show label in lane left side, and the problem appear...
See my code agai, just Lane code:
//Diagram Lane
[Serializable]
public class Lane : LaneDisplay
{
//Variables
private static readonly Pen StandardPen = new Pen(Color.Black, 2);
private static readonly Pen HighlightPen = new Pen(Color.Green, 2);
private int CellGridSize = 3;
//Constuctor
public Lane()
{
this.Label.Text = "Lane";
}
//Create Grid
protected override GoGrid CreateGrid()
{
GoGrid grid = new LaneGrid();
grid.Selectable = false;
grid.AutoRescales = true;
grid.Resizable = true;
grid.ResizesRealtime = true;
grid.Bounds = new RectangleF(0, 0, 50, 50);
grid.SnapDrag = GoViewSnapStyle.Jump;
grid.SnapResize = GoViewSnapStyle.Jump;
grid.CellSize = new SizeF(CellGridSize, CellGridSize);
grid.Style = GoViewGridStyle.None;
grid.Pen = new Pen(Color.Black, 2);
grid.BrushColor = Color.White;

return grid;
}
//Make sure the Lane's Label is positioned in left corner of the Grid; this makes it potentially useful
//for selection and dragging even if all of the grid is covered by Items
public override void LayoutChildren(GoObject childchanged)
{
//ORIGINAL LABEL CODE
if (this.Count >= 2)
{
GoGrid grid = this.Grid;
GoText label = this.Label;
if (grid != null && label != null)
{
label.SetSpotLocation(BottomLeft, new PointF(grid.Left, grid.Top - 1));
}
}
//MY LABEL CODE WITH PROBLEM !!!!!!!!!!!!!
//if (this.Count >= 2)
//{
// GoGrid grid = this.Grid;
// GoText label = this.Label;
// if (grid != null && label != null)
// {
// //Label in Left side
// label.Width = grid.Height;
// PointF center = grid.GetSpotLocation(MiddleLeft) - new SizeF(label.Height / 2 + 1, 0);
// label.Center = center;
// }
//}
}
////The Location of a Lane is actually the Position of its Grid
//public override PointF Location
//{
// get
// {
// return this.Grid.Position;
// }
// set
// {
// SizeF off = GoTool.SubtractPoints(this.Grid.Position, this.Position);
// SizeF pos = GoTool.SubtractPoints(value, off);
// this.Position = new PointF(pos.Width, pos.Height);

// }
//}

//Show a differente color when node is dragged into lane
public override void SetHighlight(bool show)
{
GoGrid grid = this.Grid;
if (grid != null)
{
if (show)
{
grid.Pen = Lane.HighlightPen;
}
else
{
grid.Pen = Lane.StandardPen;
}
}
}
}
//Diagram Lane Grid
[Serializable]
public class LaneGrid : GoGrid
{
//Variables
private int CellGridSize = 3;
//Constructor
public LaneGrid()
{
}
//The grid in a Lane only snaps Items
public override bool CanSnapPoint(PointF p, GoObject obj, GoView view)
{
if (!(obj is GraphNode))
{
return false;
}

// do standard checks, such as whether the grid is a child of the OBJ,
// the value of the grid's snap style, whether the OBJ intersects with the grid,
// and whether the mouse point is in the grid
return base.CanSnapPoint(p, obj, view);
}
//It can be resized only in positive multiples of Item.UnitSize
public override RectangleF ComputeResize(RectangleF origRect, PointF newPoint, int handle, SizeF min, SizeF max, bool reshape)
{
RectangleF r = base.ComputeResize(origRect, newPoint, handle, min, max, reshape);
Lane lane = this.Parent as Lane;
if (lane != null)
{
RectangleF rMin = new RectangleF();
bool any = false;
foreach (GoObject obj in lane.GetEnumerator())
{
if (obj is GraphNode)
{
if (!any)
{
rMin = obj.Bounds;
any = true;
}
else
{
// add the object's bounding rect to this one
rMin = UnionRect(rMin, obj.Bounds);
}
}
}
if (any) r = UnionRect(r, rMin);
}
r.Width = Math.Max(1, (float)Math.Round(r.Width / CellGridSize)) * CellGridSize;
r.Height = Math.Max(1, (float)Math.Round(r.Height / CellGridSize)) * CellGridSize;
return r;
}
static RectangleF UnionRect(RectangleF a, RectangleF b)
{
float minx = Math.Min(a.X, b.X);
float miny = Math.Min(a.Y, b.Y);
float maxr = Math.Max(a.X + a.Width, b.X + b.Width);
float maxb = Math.Max(a.Y + a.Height, b.Y + b.Height);
return new RectangleF(minx, miny, maxr - minx, maxb - miny);
}
}

//Diagram Lane Group Display
[Serializable]
public class LaneDisplay : GoGroup, IGoLabeledNode
{
//Variables
private int CellGridSize = 3;
//Constructor
protected LaneDisplay()
{
//The Grid is always first, in the background
Add(CreateGrid());
Add(CreateLabel());
}
//Create Grid
protected virtual GoGrid CreateGrid()
{
GoGrid grid = new LaneGrid();
grid.Selectable = false;
grid.AutoRescales = true;
grid.Resizable = true;
grid.ResizesRealtime = true;
grid.Bounds = new RectangleF(0, 0, 50, 50);
grid.SnapDrag = GoViewSnapStyle.Jump;
grid.SnapResize = GoViewSnapStyle.Jump;
grid.CellSize = new SizeF(CellGridSize, CellGridSize);
grid.Style = GoViewGridStyle.None;
grid.Pen = new Pen(Color.Black, 2);
grid.BrushColor = Color.White;
return grid;
}
//Create label
protected virtual GoText CreateLabel()
{
//ORIGINAL LABEL CODE!!!
//Label in Top Left
GoText label = new GoText();
label.Selectable = false;
label.Multiline = true;
label.Editable = true;
label.FontSize = 11;
label.TransparentBackground = false;
label.BackgroundColor = Color.White;
label.Bordered = true;
return label;
//MY CODE WITH PROBLEM !!!!!!!!!!!!!

////Label Left Side
//RotatedText label = new RotatedText();
//label.Selectable = false;
//label.Multiline = true;
//label.Editable = true;
//label.FontSize = 11;
//label.TransparentBackground = false;
//label.BackgroundColor = Color.White;
//label.Bordered = true;
////settings for rotation.
//label.AutoResizes = false;
//label.Alignment = Middle;
//label.Angle = 270;
//return label;
}
public virtual GoText Label
{
get { return this[1] as GoText; }
}
//Get Grid
public virtual GoGrid Grid
{
get { return this[0] as GoGrid; }
}
//IGoLabeledNode:
//Get Label
//Set and Get Label text
public virtual String Text
{
get
{
GoText lab = this.Label;
if (lab != null)
return lab.Text;
return null;
}
set
{
GoText lab = this.Label;
if (lab != null)
lab.Text = value;
}
}
public override void DoBeginEdit(GoView view)
{
if (this.Label != null)
{
this.Label.DoBeginEdit(view);
}

}
public override bool CanEdit()
{
return base.CanEdit();
}
//if the Label is not visible or if the view has a small DocScale,it's handy to be able to see the name of the lane
public override String GetToolTip(GoView view)
{
return this.Text;
}
//Selection:
//let users resize the grid, not the whole Lane
public override GoObject SelectionObject
{
get { return this.Grid; }
}
public override bool CanSelect()
{
return base.CanSelect();
}
//Unlike normal GoGroups, PickObjects picks all children at a particular PointF, not just the "top" one
public override IGoCollection PickObjects(PointF p, bool selectableOnly, IGoCollection coll, int max)
{
if (coll == null) coll = new GoCollection();
if (coll.Count >= max) return coll;
if (!CanView()) return coll;
foreach (GoObject child in this.Backwards)
{
LaneDisplay disp = child as LaneDisplay;
if (disp != null)
{
disp.PickObjects(p, selectableOnly, coll, max);
}
else
{
GoObject picked = child.Pick(p, selectableOnly);
if (picked != null)
{
coll.Add(picked);
if (coll.Count >= max) return coll;
}
}
}
return coll;
}
//Dragging:
//when dragging a non-Display onto a Display, highlight it to indicate that the dropped object(s) will be added to the Display
public override bool OnEnterLeave(GoObject from, GoObject to, GoView view)
{
if (from is LaneDisplay && from != this)
{
((LaneDisplay)from).SetHighlight(false);
}
SetHighlight(view.Tool is GoToolDragging && to == this && !view.Selection.IsEmpty && IsAddable(view.Selection.Primary) && view.Selection.Primary.Parent != this);
return true;
}
//Called after a GoToolDragging drop
public override bool OnSelectionDropped(GoObjectEventArgs evt, GoView view)
{
view.Selection.AddRange(AddItems(view.Selection));
return true;
}
//All ports on all nodes are hidden when the mouse hovers over the lane background.
public override bool OnHover(GoInputEventArgs evt, GoView view)
{
foreach (GoObject obj in this.Backwards)
{
if (obj is GraphNode)
{
GraphNode n = obj as GraphNode;
if (n != null)
{
foreach (GoPort p in n.Ports)
{
p.Style = GoPortStyle.None;
}
}
}
}
return true;
}
//Don't add Lanes as children of this Display
public virtual bool IsAddable(GoObject obj)
{
if (obj is Lane)
{
return false;
}
else
{
return true;
}
}
//Return a collection of objects that actually are added to this Lane
public virtual IGoCollection AddItems(IGoCollection coll)
{
//Skip all dropped Lanes -- just include Items and GoText labels
GoCollection items = new GoCollection();

foreach (GoObject obj in coll)
{
if (IsAddable(obj))
{
items.Add(obj);
}
}
if (items.IsEmpty)
{
return items;
}
IGoCollection added = AddCollection(items, false);
return added;
}
//Visually distinguish this Display from others, to indicate that it can (or just did) add an Item
public virtual void SetHighlight(bool show)
{
}
}

Hi Support!

Do you have some idea about this problem?

Try overriding Location to be Grid.Position - Width of the text label (so Location is the topleft point of the label).

Hi Jake, thank you for information, but I am having problems yet. Unhappy

Do you have a way to create a simple project and use my 2 classes to see the problem?

public class Lane : LaneDisplay
{
    //Variables
    private static readonly Pen StandardPen = new Pen(Color.Black, 1);
    private static readonly Pen HighlightPen = new Pen(Color.Green, 1);
    private int CellGridSize = 3;

    //Constuctor
    public Lane()
    {


        this.Label.Text = "Lane";
    }

    //Create Grid
    protected override GoGrid CreateGrid()
    {
        GoGrid grid = new LaneGrid();
        grid.Selectable = false;
        grid.AutoRescales = true;
        grid.Resizable = true;
        grid.ResizesRealtime = true;
        grid.Bounds = new RectangleF(0, 0, 50, 50);
        grid.SnapDrag = GoViewSnapStyle.Jump;
        grid.SnapResize = GoViewSnapStyle.Jump;
        grid.CellSize = new SizeF(CellGridSize, CellGridSize);
        grid.Style = GoViewGridStyle.None;
        grid.Pen = new Pen(Color.Black, 1);
        grid.BrushColor = Color.White;

        return grid;
    }

    //Make sure the Lane's Label is positioned above the top-left corner of the Grid; this makes it potentially useful 
    //for selection and dragging even if all of the grid is covered by Items
    public override void LayoutChildren(GoObject childchanged)
    {
        if (this.Count >= 2)
        {
            GoGrid grid = this.Grid;
            GoText label = this.Label;

            if (grid != null && label != null)
            {
                //Label in Left side
                label.Width = grid.Height;
                PointF center = grid.GetSpotLocation(MiddleLeft) - new SizeF(label.Height / 2 + 1, 0);
                label.Center = center;
            }
        }
    }

    //The Location of a Lane is actually the Position of its Grid
    public override PointF Location
    {

        get { return this.Grid.Position; }
        set
        {
            SizeF off = GoTool.SubtractPoints(this.Grid.Position, this.Position);
            SizeF pos = GoTool.SubtractPoints(value, off);
            this.Position = new PointF(pos.Width, pos.Height);
        }

    }

    //Show a differente color when node is dragged into lane
    public override void SetHighlight(bool show)
    {
        GoGrid grid = this.Grid;
        if (grid != null)
        {
            if (show)
            {
                grid.Pen = Lane.HighlightPen;
            }
            else
            {
                grid.Pen = Lane.StandardPen;
            }
        }
    }

}

//Diagram Lane Grid
public class LaneGrid : GoGrid
{
    //Variables
    private int CellGridSize = 3;
    private int MinSizeW = 50;
    private int MinSizeH = 50;


    //Constructor
    public LaneGrid()
    {

    }

    //The grid in a Lane only snaps Items
    public override bool CanSnapPoint(PointF p, GoObject obj, GoView view)
    {
        if (!(obj is ActivityProcessItem))
        {
            return false;
        }

        // do standard checks, such as whether the grid is a child of the OBJ,
        // the value of the grid's snap style, whether the OBJ intersects with the grid,
        // and whether the mouse point is in the grid
        return base.CanSnapPoint(p, obj, view);

    }

    // it can be resized only in positive multiples of Item.UnitSize
    public override RectangleF ComputeResize(RectangleF origRect, PointF newPoint, int handle, SizeF min, SizeF max, bool reshape)
    {
        RectangleF r = base.ComputeResize(origRect, newPoint, handle, min, max, reshape);
        Lane lane = this.Parent as Lane;
        //bool haschildren = false;

        if (lane != null)
        {
            RectangleF rMin = new RectangleF();
            bool any = false;
            foreach (GoObject obj in lane.GetEnumerator())
            {
                if (obj is ActivityProcessItem)
                {
                    //haschildren = true;
                    if (!any)
                    {
                        rMin = obj.Bounds;
                        any = true;
                    }
                    else
                    {
                        // add the object's bounding rect to this one
                        rMin = UnionRect(rMin, obj.Bounds);
                    }
                }
            }

            if (any) r = UnionRect(r, rMin);
        }

        r.Width = Math.Max(1, (float)Math.Round(r.Width / CellGridSize)) * CellGridSize;
        r.Height = Math.Max(1, (float)Math.Round(r.Height / CellGridSize)) * CellGridSize;

        //if (haschildren)
        //{
        //    r.Width = Math.Max(1, (float)Math.Round(r.Width / CellGridSize)) * CellGridSize;
        //    r.Height = Math.Max(1, (float)Math.Round(r.Height / CellGridSize)) * CellGridSize;
        //}
        //else
        //{

        //    foreach (GoObject obj in lane.GetEnumerator())
        //    {
        //        if (obj is RotatedText)
        //        {
        //            r.Height = obj.Width;

        //        }
        //    }
        //}

        return r;
    }

    static RectangleF UnionRect(RectangleF a, RectangleF b)
    {
        float minx = Math.Min(a.X, b.X);
        float miny = Math.Min(a.Y, b.Y);
        float maxr = Math.Max(a.X + a.Width, b.X + b.Width);
        float maxb = Math.Max(a.Y + a.Height, b.Y + b.Height);
        return new RectangleF(minx, miny, maxr - minx, maxb - miny);
    }

    // specify minimum resizing Size
    public override void DoResize(GoView view, RectangleF origRect, PointF newPoint, int whichHandle, GoInputState evttype, SizeF min, SizeF max)
    {
        base.DoResize(view, origRect, newPoint, whichHandle, evttype, new SizeF(MinSizeW, MinSizeH), max);
    }

}

//Diagram Lane Group Display
public class LaneDisplay : GoGroup, IGoLabeledNode
{

    //Variables
    private int CellGridSize = 3;

    //Constructor
    protected LaneDisplay()
    {
        //The Grid is always first, in the background
        Add(CreateGrid());
        Add(CreateLabel());
    }

    //Create Grid
    protected virtual GoGrid CreateGrid()
    {
        GoGrid grid = new LaneGrid();
        grid.Selectable = false;
        grid.AutoRescales = true;
        grid.Resizable = true;
        grid.ResizesRealtime = true;
        grid.Bounds = new RectangleF(0, 0, 50, 50);
        grid.SnapDrag = GoViewSnapStyle.Jump;
        grid.SnapResize = GoViewSnapStyle.Jump;
        grid.CellSize = new SizeF(CellGridSize, CellGridSize);
        grid.Style = GoViewGridStyle.None;
        grid.Pen = new Pen(Color.Black, 2);
        grid.BrushColor = Color.White;
        return grid;
    }

    //Create label
    protected virtual GoText CreateLabel()
    {
        //Label Left Side
        RotatedText label = new RotatedText();
        label.Selectable = false;
        label.Multiline = true;
        label.Editable = true;
        label.FontSize = 11;
        label.TransparentBackground = false;
        label.BackgroundColor = Color.White;
        label.Bordered = true;
        // settings for rotation. 
        label.AutoResizes = false;
        label.Alignment = Middle;
        label.Angle = 270;
        return label;
    }

    public virtual GoText Label
    {
        get { return this[1] as GoText; }
    }

    //Get Grid
    public virtual GoGrid Grid
    {
        get { return this[0] as GoGrid; }
    }

    //IGoLabeledNode:
    //Get Label
    //Set and Get Label text
    public virtual String Text
    {
        get
        {
            GoText lab = this.Label;
            if (lab != null)
                return lab.Text;
            return null;
        }
        set
        {
            GoText lab = this.Label;
            if (lab != null)
                lab.Text = value;
        }
    }

    public override void DoBeginEdit(GoView view)
    {
        if (this.Label != null)
        {
            this.Label.DoBeginEdit(view);
        }

    }

    public override bool CanEdit()
    {
        return base.CanEdit();
    }


    //Selection:

    //let users resize the grid, not the whole Lane
    public override GoObject SelectionObject
    {
        get { return this.Grid; }
    }

    public override bool CanSelect()
    {
        return base.CanSelect();
    }

    //Unlike normal GoGroups, PickObjects picks all children at a particular PointF, not just the "top" one
    public override IGoCollection PickObjects(PointF p, bool selectableOnly, IGoCollection coll, int max)
    {
        if (coll == null) coll = new GoCollection();
        if (coll.Count >= max) return coll;
        if (!CanView()) return coll;
        foreach (GoObject child in this.Backwards)
        {
            LaneDisplay disp = child as LaneDisplay;
            if (disp != null)
            {
                disp.PickObjects(p, selectableOnly, coll, max);
            }
            else
            {
                GoObject picked = child.Pick(p, selectableOnly);
                if (picked != null)
                {
                    coll.Add(picked);
                    if (coll.Count >= max) return coll;
                }
            }
        }
        return coll;
    }

    //Dragging:

    //when dragging a non-Display onto a Display, highlight it to indicate that the dropped object(s) will be added to the Display
    public override bool OnEnterLeave(GoObject from, GoObject to, GoView view)
    {
        if (from is LaneDisplay && from != this)
        {
            ((LaneDisplay)from).SetHighlight(false);
        }

        SetHighlight(view.Tool is GoToolDragging && to == this && !view.Selection.IsEmpty && IsAddable(view.Selection.Primary) && view.Selection.Primary.Parent != this);

        return true;
    }

    //Called after a GoToolDragging drop
    public override bool OnSelectionDropped(GoObjectEventArgs evt, GoView view)
    {
        view.Selection.AddRange(AddItems(view.Selection));
        return true;
    }

    //All ports on all nodes are hidden when the mouse hovers over the lane background.
    public override bool OnHover(GoInputEventArgs evt, GoView view)
    {
        foreach (GoObject obj in this.Backwards)
        {
            if (obj is ActivityProcessItem)
            {
                ActivityProcessItem n = obj as ActivityProcessItem;
                if (n != null)
                {
                    foreach (GoPort p in n.Ports)
                    {
                        p.Style = GoPortStyle.None;
                    }
                }
            }
        }

        return true;
    }

    //Don't add Lanes/Pool as children of this Display
    public virtual bool IsAddable(GoObject obj)
    {
        if ((obj is Lane) || (obj is Pool))
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    //Return a collection of objects that actually are added to this Lane
    public virtual IGoCollection AddItems(IGoCollection coll)
    {
        //Skip all dropped Lanes -- just include Items and GoText labels
        GoCollection items = new GoCollection();

        foreach (GoObject obj in coll)
        {
            if (IsAddable(obj))
            {
                items.Add(obj);
            }
        }

        if (items.IsEmpty)
        {
            return items;
        }

        IGoCollection added = AddCollection(items, false);

        return added;
    }



}

Pool class:

//Diagram Pool
public class Pool : PoolDisplay
{
    //Variables
    private static readonly Pen StandardPen = new Pen(Color.Black, 1);
    private static readonly Pen HighlightPen = new Pen(Color.Green, 1);
    private int CellGridSize = 3;

    //Constuctor
    public Pool()
    {

        this.Label.Text = "Pool";
    }


    //Create Grid
    protected override GoGrid CreateGrid()
    {
        GoGrid grid = new PoolGrid();
        grid.Selectable = false;
        grid.AutoRescales = true;
        grid.Resizable = true;
        grid.ResizesRealtime = true;
        grid.Bounds = new RectangleF(0, 0, 50, 50);
        grid.SnapDrag = GoViewSnapStyle.Jump;
        grid.SnapResize = GoViewSnapStyle.Jump;
        grid.CellSize = new SizeF(CellGridSize, CellGridSize);
        grid.Style = GoViewGridStyle.None;
        grid.Pen = new Pen(Color.Black, 1);
        grid.BrushColor = Color.White;

        return grid;
    }

    //Make sure the Lane's Label is positioned above the top-left corner of the Grid; this makes it potentially useful 
    //for selection and dragging even if all of the grid is covered by Items
    public override void LayoutChildren(GoObject childchanged)
    {
        if (this.Count >= 2)
        {
            GoGrid grid = this.Grid;
            GoText label = this.Label;

            if (grid != null && label != null)
            {
                //Label in Left side
                label.Width = grid.Height;
                PointF center = grid.GetSpotLocation(MiddleLeft) - new SizeF(label.Height / 2 + 1, 0);
                label.Center = center;
            }
        }
    }

    //The Location of a Lane is actually the Position of its Grid
    public override PointF Location
    {
        get { return this.Grid.Position; }
        set
        {
            SizeF off = GoTool.SubtractPoints(this.Grid.Position, this.Position);
            SizeF pos = GoTool.SubtractPoints(value, off);
            this.Position = new PointF(pos.Width, pos.Height);
        }
    }

    //Show a differente color when node is dragged into lane
    public override void SetHighlight(bool show)
    {
        GoGrid grid = this.Grid;
        if (grid != null)
        {
            if (show)
            {
                grid.Pen = Pool.HighlightPen;
            }
            else
            {
                grid.Pen = Pool.StandardPen;
            }
        }
    }

}

//Diagram Pool Grid
public class PoolGrid : GoGrid
{
    //Variables
    private int CellGridSize = 3;
    private int MinSizeW = 50;
    private int MinSizeH = 50;

    //Constructor
    public PoolGrid()
    {
    }

    //The grid in a Pool only snaps Lanes
    public override bool CanSnapPoint(PointF p, GoObject obj, GoView view)
    {
        if (!(obj is Lane))
        {
            return false;
        }

        // do standard checks, such as whether the grid is a child of the OBJ,
        // the value of the grid's snap style, whether the OBJ intersects with the grid,
        // and whether the mouse point is in the grid
        return base.CanSnapPoint(p, obj, view);

    }

    public override RectangleF ComputeResize(RectangleF origRect, PointF newPoint, int handle, SizeF min, SizeF max, bool reshape)
    {
        RectangleF r = base.ComputeResize(origRect, newPoint, handle, min, max, reshape);
        Pool pool = this.Parent as Pool;
        if (pool != null)
        {
            RectangleF rMin = new RectangleF();
            bool any = false;
            foreach (GoObject obj in pool.GetEnumerator())
            {
                if (obj is Lane)
                {
                    if (!any)
                    {
                        rMin = obj.Bounds;
                        any = true;
                    }
                    else
                    {
                        // add the object's bounding rect to this one
                        rMin = UnionRect(rMin, obj.Bounds);
                    }
                }
            }
            if (any) r = UnionRect(r, rMin);
        }

        r.Width = Math.Max(1, (float)Math.Round(r.Width / CellGridSize)) * CellGridSize;
        r.Height = Math.Max(1, (float)Math.Round(r.Height / CellGridSize)) * CellGridSize;

        return r;
    }

    static RectangleF UnionRect(RectangleF a, RectangleF b)
    {
        float minx = Math.Min(a.X, b.X);
        float miny = Math.Min(a.Y, b.Y);
        float maxr = Math.Max(a.X + a.Width, b.X + b.Width);
        float maxb = Math.Max(a.Y + a.Height, b.Y + b.Height);
        return new RectangleF(minx, miny, maxr - minx, maxb - miny);
    }

    public override void DoResize(GoView view, RectangleF origRect, PointF newPoint, int whichHandle, GoInputState evttype, SizeF min, SizeF max)
    {
        base.DoResize(view, origRect, newPoint, whichHandle, evttype, new SizeF(MinSizeW, MinSizeH), max);  // specify minimum resizing Size
    }

}

//Diagram Pool Group Display
public class PoolDisplay : GoGroup, IGoLabeledNode
{

    //Variables
    private int CellGridSize = 3;

    //Constructor
    protected PoolDisplay()
    {
        //The Grid is always first, in the background
        Add(CreateGrid());
        Add(CreateLabel());
    }

    //Create Grid
    protected virtual GoGrid CreateGrid()
    {
        GoGrid grid = new PoolGrid();
        grid.Selectable = false;
        grid.AutoRescales = true;
        grid.Resizable = true;
        grid.ResizesRealtime = true;
        grid.Bounds = new RectangleF(0, 0, 50, 50);
        grid.SnapDrag = GoViewSnapStyle.Jump;
        grid.SnapResize = GoViewSnapStyle.Jump;
        grid.CellSize = new SizeF(CellGridSize, CellGridSize);
        grid.Style = GoViewGridStyle.None;
        grid.Pen = new Pen(Color.Black, 2);
        grid.BrushColor = Color.White;
        return grid;
    }

    //Create label
    protected virtual GoText CreateLabel()
    {

        //Label Left Side
        RotatedText label = new RotatedText();
        label.Selectable = false;
        label.Multiline = true;
        label.Editable = true;
        label.FontSize = 11;
        label.TransparentBackground = false;
        label.BackgroundColor = Color.White;
        label.Bordered = true;
        // settings for rotation. 
        label.AutoResizes = false;
        label.Alignment = Middle;
        label.Angle = 270;
        return label;
    }

    public virtual GoText Label
    {
        get { return this[1] as GoText; }
    }

    //Get Grid
    public virtual GoGrid Grid
    {
        get { return this[0] as GoGrid; }
    }

    //IGoLabeledNode:
    //Get Label
    //Set and Get Label text
    public virtual String Text
    {
        get
        {
            GoText lab = this.Label;
            if (lab != null)
                return lab.Text;
            return null;
        }
        set
        {
            GoText lab = this.Label;
            if (lab != null)
                lab.Text = value;
        }
    }

    public override void DoBeginEdit(GoView view)
    {
        if (this.Label != null)
        {
            this.Label.DoBeginEdit(view);
        }

    }

    public override bool CanEdit()
    {
        return base.CanEdit();
    }


    //Selection:

    //let users resize the grid, not the whole Lane
    public override GoObject SelectionObject
    {
        get { return this.Grid; }
    }

    public override bool CanSelect()
    {
        return base.CanSelect();
    }

    //Unlike normal GoGroups, PickObjects picks all children at a particular PointF, not just the "top" one
    public override IGoCollection PickObjects(PointF p, bool selectableOnly, IGoCollection coll, int max)
    {
        if (coll == null) coll = new GoCollection();
        if (coll.Count >= max) return coll;
        if (!CanView()) return coll;
        foreach (GoObject child in this.Backwards)
        {
            PoolDisplay disp = child as PoolDisplay;
            if (disp != null)
            {
                disp.PickObjects(p, selectableOnly, coll, max);
            }
            else
            {
                GoObject picked = child.Pick(p, selectableOnly);
                if (picked != null)
                {
                    coll.Add(picked);
                    if (coll.Count >= max) return coll;
                }
            }
        }
        return coll;
    }

    //Dragging:

    //when dragging a non-Display onto a Display, highlight it to indicate that the dropped object(s) will be added to the Display
    public override bool OnEnterLeave(GoObject from, GoObject to, GoView view)
    {

        if (from is PoolDisplay && from != this)
        {
            ((PoolDisplay)from).SetHighlight(false);
        }

        SetHighlight(view.Tool is GoToolDragging && to == this && !view.Selection.IsEmpty && IsAddable(view.Selection.Primary) && view.Selection.Primary.Parent != this);

        return true;
    }

    //Called after a GoToolDragging drop
    public override bool OnSelectionDropped(GoObjectEventArgs evt, GoView view)
    {
        view.Selection.AddRange(AddItems(view.Selection));
        return true;
    }

    //Don't add pools as children of this Display
    public virtual bool IsAddable(GoObject obj)
    {
        if (obj is Lane)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    //Return a collection of objects that actually are added to this Pool
    public virtual IGoCollection AddItems(IGoCollection coll)
    {
        //Skip all dropped Lanes -- just include Items and GoText labels
        GoCollection items = new GoCollection();

        foreach (GoObject obj in coll)
        {
            if (IsAddable(obj))
            {
                items.Add(obj);
            }
        }

        if (items.IsEmpty)
        {
            return items;
        }

        IGoCollection added = AddCollection(items, false);

        return added;
    }



}

RotatedText Class:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using Northwoods.Go;

namespace ProcessDiagram
{

[Serializable]
public class RotatedText : GoText
{

    //Variables
    private const int ChangedAngle = 1550;
    private float myAngle = 0;

    //Constructor
    public RotatedText()
    {
        this.Alignment = GoObject.Middle;
    }

    public override void Paint(Graphics g, GoView view)
    {
        GraphicsState before = g.Save();
        PointF c = this.Location;
        g.TranslateTransform(c.X, c.Y);
        g.RotateTransform(this.Angle);
        g.TranslateTransform(-c.X, -c.Y);
        base.Paint(g, view);
        g.Restore(before);
    }

    public override RectangleF ExpandPaintBounds(RectangleF rect, GoView view)
    {
        RectangleF b = GetRealBounds();
        return RectangleF.Union(rect, b);
    }

    public override bool ContainsPoint(PointF p)
    {
        return GetRealBounds().Contains(p);
    }

    public override bool ContainedByRectangle(RectangleF r)
    {
        RectangleF b = GetRealBounds();
        return (
          r.Width > 0 &&
          r.Height > 0 &&
          b.Width >= 0 &&
          b.Height >= 0 &&
          b.X >= r.X &&
          b.Y >= r.Y &&
          b.X + b.Width <= r.X + r.Width &&
          b.Y + b.Height <= r.Y + r.Height);
    }

    public RectangleF GetRealBounds()
    {
        RectangleF bounds = this.Bounds;
        double angle = this.Angle / 180 * Math.PI;
        PointF origin = this.Location;
        return RotateRectangle(bounds, origin, angle);
    }

    public static RectangleF RotateRectangle(RectangleF r, PointF origin, double angle)
    {
        PointF otl = new PointF(r.X, r.Y);
        PointF ntl = otl;
        if (otl != origin)
            ntl = RotatePoint(otl, origin, angle);

        PointF otr = new PointF(r.X + r.Width, r.Y);
        PointF ntr = otr;
        if (otr != origin)
            ntr = RotatePoint(otr, origin, angle);

        PointF obr = new PointF(r.X + r.Width, r.Y + r.Height);
        PointF nbr = obr;
        if (obr != origin)
            nbr = RotatePoint(obr, origin, angle);

        PointF obl = new PointF(r.X, r.Y + r.Height);
        PointF nbl = obl;
        if (obl != origin)
            nbl = RotatePoint(obl, origin, angle);

        float minx = (float)Math.Min(ntl.X, Math.Min(ntr.X, Math.Min(nbr.X, nbl.X)));
        float maxx = (float)Math.Max(ntl.X, Math.Max(ntr.X, Math.Max(nbr.X, nbl.X)));
        float miny = (float)Math.Min(ntl.Y, Math.Min(ntr.Y, Math.Min(nbr.Y, nbl.Y)));
        float maxy = (float)Math.Max(ntl.Y, Math.Max(ntr.Y, Math.Max(nbr.Y, nbl.Y)));

        return new RectangleF(minx, miny, maxx - minx, maxy - miny);
    }

    public static PointF RotatePoint(PointF p, PointF origin, double angle)
    {
        float dx = p.X - origin.X;
        float dy = p.Y - origin.Y;
        double pangle = GoStroke.GetAngle(dx, dy) / 180 * Math.PI;
        double dist = Math.Sqrt(dx * dx + dy * dy);
        float nx = (float)(dist * Math.Cos(pangle + angle));
        float ny = (float)(dist * Math.Sin(pangle + angle));
        return new PointF(origin.X + nx, origin.Y + ny);
    }

    public override void AddSelectionHandles(GoSelection sel, GoObject selectedObject)
    {
        RemoveSelectionHandles(sel);

        if (!CanResize())
        {
            sel.CreateBoundingHandle(this, selectedObject);
            return;
        }

        RectangleF rect = GetRealBounds();

        float x1 = rect.X;
        float x2 = rect.X + (rect.Width / 2);
        float x3 = rect.X + rect.Width;

        float y1 = rect.Y;
        float y2 = rect.Y + (rect.Height / 2);
        float y3 = rect.Y + rect.Height;

        // create the handles
        sel.CreateResizeHandle(this, selectedObject, new PointF(x1, y1), TopLeft, true);
        sel.CreateResizeHandle(this, selectedObject, new PointF(x3, y1), TopRight, true);
        sel.CreateResizeHandle(this, selectedObject, new PointF(x3, y3), BottomRight, true);
        sel.CreateResizeHandle(this, selectedObject, new PointF(x1, y3), BottomLeft, true);
        if (CanReshape())
        {
            sel.CreateResizeHandle(this, selectedObject, new PointF(x2, y1), MiddleTop, true);
            sel.CreateResizeHandle(this, selectedObject, new PointF(x3, y2), MiddleRight, true);
            sel.CreateResizeHandle(this, selectedObject, new PointF(x2, y3), MiddleBottom, true);
            sel.CreateResizeHandle(this, selectedObject, new PointF(x1, y2), MiddleLeft, true);
        }
    }

    public override IGoHandle CreateBoundingHandle()
    {
        GoHandle h = new GoHandle();
        RectangleF rect = GetRealBounds();
        // the handle rectangle should just go around the object
        rect.X--;
        rect.Y--;
        rect.Height += 2;
        rect.Width += 2;
        h.Bounds = rect;
        return h;
    }

    [Category("Appearance"), DefaultValue(0)]
    public float Angle
    {
        get
        {
            GoLabeledLink ll = this.Parent as GoLabeledLink;
            if (ll != null && ll.MidLabel == this)
            {
                GoLink rl = ll.RealLink;
                int midEnd = rl.PointsCount / 2;
                if (midEnd < 1) return myAngle;
                PointF a = rl.GetPoint(midEnd - 1);
                PointF b = rl.GetPoint(midEnd);
                float angle = GoStroke.GetAngle(b.X - a.X, b.Y - a.Y);
                if (angle > 90 && angle < 270)
                    angle -= 180;
                return angle;
            }
            else
            {
                return myAngle;
            }
        }
        set
        {
            float old = myAngle;
            if (old != value)
            {
                myAngle = value;
                Changed(ChangedAngle, 0, null, MakeRect(old), 0, null, MakeRect(value));
            }
        }
    }

    public override void ChangeValue(GoChangedEventArgs e, bool undo)
    {
        switch (e.SubHint)
        {
            case ChangedAngle:
                this.Angle = e.GetFloat(undo);
                return;
            default:
                base.ChangeValue(e, undo);
                return;
        }
    }


}

// A RotatedText's Bounds aren't the "real" bounds.
// This class implements an object that contains a RotatedText,
// and whose Bounds will be equal to the RotatedText's GetRealBounds(),
// The initial RotatedText object will also be not Selectable and
// will have a Middle Alignment.
// You can then use an instance of this class inside other groups, such as GoListGroups.
// You can access the RotatedText object through the RT property.
[Serializable]
public class RotatedTextHolder : GoGroup
{
    public RotatedTextHolder()
    {
        this.Selectable = false;
        RotatedText myRotatedText = new RotatedText();
        myRotatedText.Selectable = false;
        myRotatedText.Alignment = Middle;
        myRotatedText.AddObserver(this);  // notice whenever the RotatedText's Bounds changes
        Add(myRotatedText);
    }

    protected override void OnObservedChanged(GoObject observed, int subhint, int oldI, object oldVal, RectangleF oldRect, int newI, object newVal, RectangleF newRect)
    {
        if (subhint == GoObject.ChangedBounds)
        {
            this.InvalidBounds = true;
        }
    }

    protected override RectangleF ComputeBounds()
    {
        return this.RT.GetRealBounds();
    }

    public RotatedText RT
    {
        get { return (RotatedText)this[0]; }
    }

}

}

I’ll try to do that.

Ok, I will wait because I will show our software using GoDiagram component in our new BPM Module to some people in our company, and this detail is not good in a presentation…
We are very happy with the GoDiagram component result in our new module…I think this module will help a lot of people to design BPM.

Thanks!

OK… the root problem here is that GoGroup doesn’t understand RotatedText, and thus can do ComputeBounds correctly… which is why there is a RotatedTextHolder to wrap around RotatedText before sticking it in a group.



However… I tried that here and things got ever more weird.



So… since Lane is 2 objects, I brute forced the computing of Bounds by doing this in Lane:



protected override RectangleF ComputeBounds() {

RectangleF rect = Grid.Bounds;

if (this.Count >= 2) {

rect.X = rect.X - Label.Height;

rect.Width = rect.Width + Label.Height;

}

return rect;

}



and now the snapping works.



oh… Remove any override of Location too, since you really do what that topleft of the bounds to be the snapping point.



Now, resize of Lane is still broken, and I haven’t had time to look at that. But maybe that’s a result of the hacking I did to get your code to run in my sample app.



You are missing [Serializable] on most of your classes.



I’ll mail you my file in case I missed something here, you can do a diff and see what I did.

Hi Jake!

First, thank you very much for attention, and information.
Now, this class is good when I move some object into a pool object!
Thanks!