Moving the copy frame with mouse location

Hi,

I have a feature to be implemented for copy paste scenario.It is like there are number of nodes and links and when I select a bunch of them and Ctrl+C it should copy the selected ones and when I Ctrl+V a frame should be formed which will be sticking to my mouse location and the place I click on diagram it should paste the nodes and links.

Does GoXam has this kind of behavior ?

I have implemented the copy-paste scenario but I want to have the behavior for copy-paste as mentioned above.

Can you suggest something about this? I tried to make use of UnboundNodes but it didn’t help as I am creating diagram object programmatically in C#.

Would you mind if the paste actually added the copied Nodes and Links immediately, and that the command would automatically immediately start a DraggingTool dragging around those new nodes and links? Or something like that? Here’s an example:

  public class CustomCommandHandler : CommandHandler {
    public override void Paste() {
      base.Paste();
      if (this.Diagram.SelectedParts.Count > 0) {
        var tool = new CustomDraggingTool();
        // start and activate this special dragging tool
        this.Diagram.CurrentTool = tool;
        tool.DoActivate();
      }
    }
  }

  public class CustomDraggingTool : DraggingTool {
    private Boolean InitialMove = false;
    protected override void StandardMouseSelect() {
      // don't do the usual selection behavior;
      // just pick any selected Part as the CurrentPart
      this.CurrentPart = this.Diagram.SelectedParts.First();
    }

    public override void DoMouseMove() {
      if (!this.Active) return;
      if (!InitialMove) {
        InitialMove = true;
        // the mouse might not be anywhere near the pasted parts
        var bounds = this.Diagram.Panel.ComputeBounds(this.Diagram.SelectedParts);
        MoveParts(this.DraggedParts, new Point(this.Diagram.LastMousePointInModel.X - (bounds.X + bounds.Width/2),
                                               this.Diagram.LastMousePointInModel.Y - (bounds.Y + bounds.Height/2)));
        this.StartPoint = this.Diagram.LastMousePointInModel;
      }
      base.DoMouseMove();
    }

    public override void DoMouseDown() {
      // finish tool on mouse down, not on the usual mouse up
      DoMouseUp();
    }

    public override void DoCancel() {
      base.DoCancel();
      // this tool is invoked only by CommandHandler.Paste,
      // so we need to undo the result of the paste
      this.Diagram.PartManager.DeleteParts(this.Diagram.SelectedParts);
    }
  }

Install with:

myDiagram.CommandHandler = new CustomCommandHandler();

Thanks Walter. The solution provided worked perfectly for the first copy paste of single node. But the consecutive paste doesn’t move the pasted node to the mouse position(LastMousePointInModel). Also in case of multiple nodes and links the mouse position get the left top corner of the bounds and in this case also if I paste it consecutively the locations of the elements is not consistent. Also I would like the mouse be at the center of bounds. I have changed the solution a little and its as follows:

    public override void DoMouseMove()
    {
        if (!this.Active)
        {
            return;
        }
        MoveParts(this.DraggedParts, new Point(this.Diagram.LastMousePointInModel.X, this.Diagram.LastMousePointInModel.Y));
        base.DoMouseMove();
    }

Also, I have binded CustomDraggingTool in the diagram and want to make use of the same tool rather than creating new DraggingTool every time. This change I have done and its working.

Hi,

When I paste consecutively the location of the pasted elements is not consistent. Can you provide some solution to it? I used the exact same code that you provided.

Thanks

Sorry, but you have the code, so you can customize it to suit your needs.

Hi Walter,

Its not about customization. My question is with the code sample you gave the paste works only when the source node is selected while pasting. If I do not select the source node the pasted elements are pasted randomly
in the diagram though the code says to move to the diagram.LastMousePointInModel. Just want to know the reason for the same

Thanks

Hi,

I had a requirement where once I paste the pasted element should be sticking to the mouse location and should move along with it. With some customization over the code provided by you I was able to get it to stick to the mouse location but,

The pasted node is put at the right place only when the copied node is selected. Follow the Gif below:
com-video-to-gif
Here I am selecting the node and doing Ctrl+C and pasting it and the pasted node moves with mouse and is pasted to the same location where the click is made.

However, when I don’t select the copied node and do Ctrl +V, the node is pasted at random place. Refer the gif below:
com-video-to-gif%20(1)
Here I select a node to copy and after Ctrl+C I click on empty place on diagram and de-select it. Then when I do Ctrl + V the pasted node sticks to the mouse but when I click it goes to random place. This is really annoying.

Why there is different behavior in these two cases and how does the behavior change with the node selected.

Also I was able to reproduce the same in your sample apps(Draggable Link) too. I have attached the code for the same.

class CustomDraggingTool : DraggingTool
{

    protected override void StandardMouseSelect()
    {
        // don't do the usual selection behavior;
        // just pick any selected Part as the CurrentPart

        if (this.Diagram.SelectedParts.Count > 0)
        {
            this.CurrentPart = this.Diagram.SelectedParts.First();
        }
    }

    public override void DoMouseMove()
    {
        base.DoMouseMove();
        this.Diagram.Cursor = System.Windows.Input.Cursors.SizeAll;
        //var a = this.Diagram.InitialDiagramBoundsSpot;

        //this.Diagram.Cursor = Cursors.Arrow;
        if (!this.Active)
        {
            return;
        }
        // the mouse might not be anywhere near the pasted parts

        if (this.DraggedParts.Any())
        {
            var nodeLoc = this.DraggedParts.First().Value.Point;
            MoveParts(this.DraggedParts, new Point(this.Diagram.LastMousePointInModel.X - nodeLoc.X, this.Diagram.LastMousePointInModel.Y - nodeLoc.Y));
            //System.Diagnostics.Debug.WriteLine("######################---Before");
            //System.Diagnostics.Debug.WriteLine(this.DraggedParts.First().Value.Point.ToString(), this.Diagram.LastMousePointInModel.ToString());
            //System.Diagnostics.Debug.WriteLine("######################---After");
            //System.Diagnostics.Debug.WriteLine(this.DraggedParts.First().Value.Point.ToString());
            //System.Diagnostics.Debug.WriteLine("######################---Pasted");
            //System.Diagnostics.Debug.WriteLine(this.Diagram.SelectedNode.Location.ToString());
        }
    }

    public override void DoMouseDown()
    {
        // finish tool on mouse down, not on the usual mouse up
        base.DoMouseUp();
    }

    public override void DoCancel()
    {
        base.DoCancel();
        // this tool is invoked only by CommandHandler.Paste,
        // so we need to undo the result of the paste
        this.Diagram.PartManager.DeleteParts(this.Diagram.SelectedParts);
    }

}
public class CustomCommandHandler : CommandHandler
{
    public override void Paste()
    {
        base.Paste();
        if (this.Diagram.SelectedParts.Count > 0)
        {
            var tool = this.Diagram.DraggingTool as CustomDraggingTool;
            // start and activate this special dragging tool
            this.Diagram.CurrentTool = tool;
            tool.DoActivate();
        }
    }
}

CustomCommandHandler and the CustomDraggingTool to be installed in the xaml

Try this:

  class CustomDraggingTool : DraggingTool {
    public bool IsPasting = false;
    private bool AllowDragOut = false;

    public override void DoActivate() {
      this.AllowDragOut = this.Diagram.AllowDragOut;
      this.Diagram.AllowDragOut = false;
      base.DoActivate();
    }
    protected override bool MayCopy() {
      if (this.IsPasting) return false;
      return base.MayCopy();
    }
    public override void DoDeactivate() {
      base.DoDeactivate();
      this.IsPasting = false;
      this.Diagram.AllowDragOut = this.AllowDragOut;
    }
  }

  public class CustomCommandHandler : CommandHandler {
    public override void Paste() {
      base.Paste();
      var node = this.Diagram.SelectedParts.OfType<Node>().First();
      if (node != null) {
        var pt = new Point(node.Bounds.X + node.Bounds.Width/2, node.Bounds.Y + node.Bounds.Height/2);
        this.Diagram.FirstMousePointInModel = pt;
        this.Diagram.LastMousePointInModel = pt;
        var tool = this.Diagram.DraggingTool as CustomDraggingTool;
        tool.IsPasting = true;
        // start and activate this special dragging tool
        this.Diagram.CurrentTool = tool;
        tool.DoActivate();
      }
    }
  }

There is a issue here. When there are multiple nodes the mouse cursor position is not consistent.
Consider the following:

  1. Here I have 2 nodes and I select them by starting from left top corner. and Ctrl C+V and the top element(Gamma) will be sticking to the mouse.

  1. Consider I select the pasted element from bottom to top. and do Ctrl+C+V, the bottom element(Beta) will be sticking to the mouse.

It would good if the mouse sticks to the center of the rectangle that pasted nodes form.

I think that requires some modification to the CustomCommandHandler.Paste method. Currently the first Node of the copied Parts is what follows the mouse around, as if the user had started dragging that Node.

Hi Walter,

we are able to made to work with the following changes:

    public override void Paste()
    {
        base.Paste(); Rect nodesRect = this.Diagram.Panel.ComputeBounds(this.Diagram.SelectedParts);
        Point point = new Point(nodesRect.Left + nodesRect.Width / 2, nodesRect.Top + nodesRect.Height / 2);
        this.Diagram.FirstMousePointInModel = point;
        this.Diagram.LastMousePointInModel = point;

        var tool = this.Diagram.DraggingTool as CustomDraggingTool;
        this.Diagram.CurrentTool = tool;
        tool.DoActivate();
    }

No other changes are needed in CustomDraggingTool.

Thanks,