Class DraggingTool
The DraggingTool is used to move or copy selected parts with the mouse. This sets the Location property; you may want to save the location to the model by using a TwoWay Binding on the "Location" property in your Parts/Nodes/Groups templates.
Namespace: Northwoods.Go.Tools
Assembly: Northwoods.GoDiagram.WinForms.dll
Syntax
public class DraggingTool : Tool
Remarks
Dragging the selection moves parts for which CanMove() is true. If the user holds down the Control key (Option key on Mac), this tool will make a copy of the parts being dragged, for those parts for which CanCopy() is true.
When the drag starts it calls ComputeEffectiveCollection(IEnumerable<Part>, DraggingOptions) to find the actual collection of Parts to be dragged. Normally this collection includes not only the Selection, but also parts that belong to those selected parts, such as members of groups. If DragsTree is true, the effective collection also includes all of the nodes and links that constitute the subtree starting from selected nodes. The result of ComputeEffectiveCollection(IEnumerable<Part>, DraggingOptions) is a dictionary which remembers the original Location for all of the dragged parts. This dictionary is saved as the value of DraggedParts.
During the drag if the user holds down the Control/Option key this tool makes a copy of the DraggedParts and proceeds to drag it around. (It only copies the Selection, not the whole effective collection, if CopiesEffectiveCollection is false.) The collection of copied parts is held by CopiedParts. It too is a dictionary remembering the original locations of the parts. CopiedParts will be null when this tool is moving (not copying) at the moment.
Each Part's movement is limited by the ComputeMove(Part, Point, DraggingOptions) method. By default it limits the Location to be within the bounds given by MinLocation and MaxLocation. (Those default to double.NegativeInfinity to double.PositiveInfinity.) As a further convenience, the value of NaN in MinLocation and MaxLocation cause ComputeMove(Part, Point, DraggingOptions) to use the part's current location. So, for example, an easy way to declare that the user may only drag a node horizontally is to just set:
new Node {
. . .
MinLocation: new Point(double.NegativeInfinity, double.NaN), MaxLocation: new Point(double.PositiveInfinity, double.NaN) }
. . .
}
If you set IsGridSnapEnabled to true, dragged or copied parts will be snapped to points on a grid. The snapping occurs continuously during a drag unless you set IsGridSnapRealtime to false. Normally the grid points come from the Grid, even if that grid is not Visible. However you can override those grid's properties for the snapping grid cell size and offset by setting the properties here: GridSnapCellSize and GridSnapOrigin. This computes the point to snap to for each dragged part. The resulting point is used as the new Location.
For the most general control over where a part may be dragged, either set the DragComputation property or override ComputeMove(Part, Point, DraggingOptions). For the common case of wanting to keep member nodes within the Group that they are members of, you can do something like:
// this is a Part.DragComputation function for limiting where a Node may be dragged
static Point stayInGroup(Part part, Point pt, Point gridpt) {
// don't constrain top-level nodes
var grp = part.ContainingGroup;
if (grp == null) return pt;
// try to stay within the background Shape of the Group
var back = grp.ResizeElement;
if (back == null) return pt;
// allow dragging a Node out of a Group if the Shift key is down
if (part.Diagram.LastInput.Shift) return pt;
var p1 = back.GetDocumentPoint(Spot.TopLeft);
var p2 = back.GetDocumentPoint(Spot.BottomRight);
var b = part.ActualBounds;
var loc = part.Location;
// find the padding inside the group's placeholder that is around the member parts
var m = grp.Placeholder.Padding;
// now limit the location appropriately, assuming no grid-snapping
var x = Math.Max(p1.X + m.Left, Math.Min(pt.X, p2.X - m.Right - b.Width - 1)) + (loc.X - b.X);
var y = Math.Max(p1.Y + m.Top, Math.Min(pt.Y, p2.Y - m.Bottom - b.Height - 1)) + (loc.Y - b.Y);
return new Point(x, y);
}
Note that this expects there to be a "SHAPE" object within the Group's visual tree that delimits where the part may be dragged within the group. This also expects that ComputesBoundsIncludingLinks is false. Then in your node template(s), just set:
new Node {
. . .
DragComputation = stayInGroup
. . .
}
This tool does not utilize any Adornments or tool handles. If the drag is successful, it raises the "SelectionMoved" or "SelectionCopied" DiagramEvent and produces a "Move" or a "Copy" transaction.
If you want to programmatically start a new user's dragging of a particular existing node, you can make sure that node is selected, set the CurrentPart property, and then start and activate the tool.
var node = ...;
myDiagram.Select(node); // in this case the only selected node
var tool = myDiagram.ToolManager.DraggingTool;
tool.CurrentPart = node; // the DraggingTool will not call StandardMouseSelect
myDiagram.CurrentTool = tool; // starts the DraggingTool
tool.DoActivate(); // activates the DraggingTool
Constructors
DraggingTool()
You do not normally need to create an instance of this tool because one already exists as the DraggingTool, which you can modify.
The Name of this tool is "Dragging".
Declaration
public DraggingTool()
Properties
CopiedParts
Gets or sets the collection of Parts that this tool has copied.
Declaration
public IDictionary<Part, DraggingInfo> CopiedParts { get; set; }
Property Value
Type | Description |
---|---|
IDictionary<Part, DraggingInfo> |
Remarks
The value is a dictionary mapping Parts to DraggingInfo Objects that have a "Point" property remembering the original location of that Part. The value is null when moving instead of copying.
DraggedParts provides the dictionary of Parts that are being moved and from which this collection was copied.
CopiesEffectiveCollection
Gets or sets whether for a copying operation the extended selection is copied or only the selected parts.
Declaration
public bool CopiesEffectiveCollection { get; set; }
Property Value
Type | Description |
---|---|
bool |
Remarks
The default value is true. Setting this property does not raise any events.
The CopiesConnectedLinks property serves a similar role for the CopySelection() command, when the user types control-C to copy the currently selected parts.
CopyCursor
The cursor to show when a drop is allowed and will result in a copy.
Declaration
public string CopyCursor { get; set; }
Property Value
Type | Description |
---|---|
string |
Remarks
This defaults to "copy".
CurrentPart
Gets or sets the Part found at the mouse point.
Declaration
public Part CurrentPart { get; set; }
Property Value
Type | Description |
---|---|
Part |
Remarks
This is normally set by a call to StandardMouseSelect().
Delay
On touch gestures only, this property gets or sets the TimeSpan for which the mouse must be stationary before this tool can be started.
Declaration
public TimeSpan Delay { get; set; }
Property Value
Type | Description |
---|---|
TimeSpan |
Remarks
The default value is 100 milliseconds. Setting this property does not raise any events.
DraggedParts
Gets or sets the collection of Parts being moved.
Declaration
public IDictionary<Part, DraggingInfo> DraggedParts { get; set; }
Property Value
Type | Description |
---|---|
IDictionary<Part, DraggingInfo> |
Remarks
The value is a dictionary mapping Parts to DraggingInfo Objects that have a "Point" property remembering the original location of that Part.
CopiedParts provides the dictionary of Parts that have been copied during a copying operation, if any.
DragOptions
Gets or sets the DraggingTool's DraggingOptions instance, which controls several dragging properties.
Declaration
public DraggingOptions DragOptions { get; set; }
Property Value
Type | Description |
---|---|
DraggingOptions |
Remarks
Several DraggingTool properties are just convenience properties:
- IsGridSnapEnabled
- IsGridSnapRealtime
- GridSnapCellSize
- GridSnapCellSpot
- GridSnapOrigin
- DragsLink
- DragsTree
Setting any of these properties really sets the corresponding dragOptions property.
Setting this property does not raise any events.
DragsLink
Gets or sets whether the user can drag a single Link, disconnecting it from its connected nodes and possibly connecting it to valid ports when the link is dropped.
Declaration
public bool DragsLink { get; set; }
Property Value
Type | Description |
---|---|
bool |
Remarks
The default value is false. Setting this property does not raise any events.
In order to avoid too many cases of having both ends of a dragged Link connect to the same node (if allowed), it is commonplace to decrease the PortGravity to a smaller value such as 10 or 20.
This property is a convenience getter/setter, and sets a value on DragOptions.
DragsTree
Gets or sets whether moving or copying a node also includes all of the node's tree children and their descendants, along with the links to those additional nodes.
Declaration
public bool DragsTree { get; set; }
Property Value
Type | Description |
---|---|
bool |
Remarks
The default value is false. Setting this property does not raise any events.
The CopiesTree property serves a similar role for the CopySelection() command, when the user types control-C to copy the currently selected parts.
This property is a convenience getter/setter, and sets a value on DragOptions.
GridSnapCellSize
Gets or sets the size of the grid cell used when snapping during a drag if the value of IsGridSnapEnabled is true.
Declaration
public Size GridSnapCellSize { get; set; }
Property Value
Type | Description |
---|---|
Size |
Remarks
By default this property is the Size(NaN, NaN), which causes this tool to use the GridCellSize value of the Grid. Setting this property does not raise any events.
This property is a convenience getter/setter, and sets a value on DragOptions.
GridSnapCellSpot
Gets or sets the Spot that specifies what point in the grid cell dragged parts snap to, if the value of IsGridSnapEnabled is true.
Declaration
public Spot GridSnapCellSpot { get; set; }
Property Value
Type | Description |
---|---|
Spot |
Remarks
By default this property is TopLeft: node locations will snap exactly to the grid point. Setting this property does not raise any events.
This property is a convenience getter/setter, and sets a value on DragOptions.
GridSnapOrigin
Gets or sets the snapping grid's origin point, in document coordinates, if the value of IsGridSnapEnabled is true.
Declaration
public Point GridSnapOrigin { get; set; }
Property Value
Type | Description |
---|---|
Point |
Remarks
By default this property is the Point(NaN, NaN), which causes this tool to use the GridOrigin value from the Grid. Setting this property does not raise any events.
This property is a convenience getter/setter, and sets a value on DragOptions.
IsComplexRoutingRealtime
Gets or sets whether link routing takes some short-cuts during dragging.
Declaration
public bool IsComplexRoutingRealtime { get; set; }
Property Value
Type | Description |
---|---|
bool |
Remarks
When false, Links whose routing is AvoidsNodes are not routed to avoid Nodes, in order to improve dragging performance. The default value is true.
IsCopyEnabled
Gets or sets whether any internal copying operation is permitted by control-drag-and-drop.
Declaration
public bool IsCopyEnabled { get; set; }
Property Value
Type | Description |
---|---|
bool |
Remarks
This property affects the behavior of MayCopy(), but does not affect whether copied objects may be dropped into this diagram from a different diagram.
The default value is true. Setting this property does not raise any events.
IsGridSnapEnabled
Gets or sets whether the DraggingTool snaps objects to grid points.
Declaration
public bool IsGridSnapEnabled { get; set; }
Property Value
Type | Description |
---|---|
bool |
Remarks
Whether the snapping movement of the dragged parts occurs during the drag or only upon a drop is determined by the value of IsGridSnapRealtime.
This property does not affect dragging disconnected links, but those links to respect the DragComputation, which can be used to snap them.
By default this property is false. Setting this property does not raise any events.
This property is a convenience getter/setter, and sets a value on DragOptions.
IsGridSnapRealtime
Gets or sets whether the DraggingTool snaps objects to grid points during the drag.
Declaration
public bool IsGridSnapRealtime { get; set; }
Property Value
Type | Description |
---|---|
bool |
Remarks
This property is ignored unless IsGridSnapEnabled is true. By default this property is true; when false parts are only snapped to grid locations upon the drop (i.e. mouse-up). Setting this property does not raise any events.
This property is a convenience getter/setter, and sets a value on DragOptions.
MoveCursor
The cursor to show when a drop is allowed and will result in a move.
Declaration
public string MoveCursor { get; set; }
Property Value
Type | Description |
---|---|
string |
Remarks
This defaults to the empty string, which refers to the DefaultCursor.
NodropCursor
The cursor to show when a drop is not allowed.
Declaration
public string NodropCursor { get; set; }
Property Value
Type | Description |
---|---|
string |
Remarks
This defaults to "no-drop".
StartPoint
Gets or sets the mouse point from which parts start to move.
Declaration
public Point StartPoint { get; set; }
Property Value
Type | Description |
---|---|
Point |
Remarks
The value is a Point in document coordinates. This property is normally set to the diagram's mouse-down point in DoActivate(), but may be set to a different point if parts are being copied from a different control. Setting this property does not raise any events.
Methods
CanStart()
This tool can run if the diagram allows selection and moves/copies/dragging-out, if the mouse has moved far enough away to be a drag and not a click, and if FindDraggablePart() has found a selectable part at the mouse-down point.
Declaration
public override bool CanStart()
Returns
Type | Description |
---|---|
bool |
Overrides
Remarks
This method may be overridden. Please read the Introduction page on Extensions for how to override methods and how to call this base method.
ComputeEffectiveCollection(IEnumerable<Part>, DraggingOptions)
By default, this just calls ComputeEffectiveCollection(IEnumerable<Part>, DraggingOptions).
Declaration
public virtual IDictionary<Part, DraggingInfo> ComputeEffectiveCollection(IEnumerable<Part> parts, DraggingOptions options)
Parameters
Type | Name | Description |
---|---|---|
IEnumerable<Part> | parts | A collection of Parts. |
DraggingOptions | options | Potential options for the collection computation |
Returns
Type | Description |
---|---|
IDictionary<Part, DraggingInfo> | a dictionary mapping Parts to DraggingInfo Objects that have a "Point" property remembering the original location of that Part. |
Remarks
This method may be overridden. Please read the Introduction page on Extensions for how to override methods and how to call this base method.
DoActivate()
Start the dragging operation.
Declaration
public override void DoActivate()
Overrides
Remarks
This calls ComputeEffectiveCollection(IEnumerable<Part>, DraggingOptions) and saves the result as DraggedParts.
This starts a "Drag" transaction. Depending on what happens, the transaction may be finished as a "Move" or a "Copy" transaction, or it may be rolled-back if the tool is cancelled.
Normally when this method is called the value of CurrentPart will be null, in which case this will call StandardMouseSelect() which will set CurrentPart. But if when this method is called the value of CurrentPart has already been set because the programmer wants the user to start dragging that Part, then this method will not need to call StandardMouseSelect() because the Part(s) to be selected and dragged have already been determined by the caller.
DoCancel()
Abort any dragging operation.
Declaration
public override void DoCancel()
Overrides
DoDeactivate()
Stop the dragging operation by stopping the transaction and cleaning up any temporary state.
Declaration
public override void DoDeactivate()
Overrides
DoDragOver(Point, GraphObject)
Perform any additional side-effects during a drag, whether an internal move or copy or an external drag, that may affect the existing non-moved object(s).
Declaration
public virtual void DoDragOver(Point pt, GraphObject obj)
Parameters
Type | Name | Description |
---|---|---|
Point | pt | a Point in document coordinates. |
GraphObject | obj | the GraphObject at the point, excluding what is being dragged or temporary objects; the argument may be null if the drag is occurring in the background of the diagram. Use Part to get the Node or Part at the root of the visual tree of the stationary object. |
Remarks
This method may be overridden. Please read the Introduction page on Extensions for how to override methods and how to call this base method.
DoDropOnto(Point, GraphObject)
Perform any additional side-effects after a drop, whether an internal move or copy or an external drop, that may affect the existing non-moved object(s).
Declaration
public virtual void DoDropOnto(Point pt, GraphObject obj)
Parameters
Type | Name | Description |
---|---|---|
Point | pt | a Point in document coordinates. |
GraphObject | obj | the GraphObject where the drop occurred, excluding what was dropped or temporary objects; the argument may be null if the drop occurred in the background of the diagram. Use Part to get the Node or Part at the root of the visual tree of the stationary object. |
Remarks
This method may be overridden. Please read the Introduction page on Extensions for how to override methods and how to call this base method.
DoKeyDown()
Handle switching between copying and moving modes as the Control/Option key is pressed or released.
Declaration
public override void DoKeyDown()
Overrides
DoKeyUp()
Handle switching between copying and moving modes as the Control/Option key is pressed or released.
Declaration
public override void DoKeyUp()
Overrides
DoMouseMove()
Move the DraggedParts (or if copying, the CopiedParts) to follow the current mouse point.
Declaration
public override void DoMouseMove()
Overrides
Remarks
If this creates any temporary parts, by default it adds them to the Tool layer.
This calls DoDragOver(Point, GraphObject) for any side-effects on stationary parts.
DoMouseUp()
On a mouse-up, finish moving or copying the effective selection.
Declaration
public override void DoMouseUp()
Overrides
Remarks
This calls DoDropOnto(Point, GraphObject) for any side-effects on stationary parts.
This also updates the diagram's bounds, raises a "SelectionCopied" or "SelectionMoved" DiagramEvent, and stops this tool.
This method also raises the "ChangingSelection" and "ChangedSelection" diagram events. Changes are performed in a "Drag" transaction, but the "ChangedSelection" event is raised outside the transaction.
FindDraggablePart()
Return the selectable and movable/copyable Part at the mouse-down point.
Declaration
public virtual Part FindDraggablePart()
Returns
Type | Description |
---|---|
Part |
Remarks
This is called by CanStart() to decide if this tool is ready to run.
This method may be overridden. Please read the Introduction page on Extensions for how to override methods and how to call this base method.
MayCopy()
This predicate is true when the diagram allows objects to be copied and inserted, and some object in the selection is copyable, and the user is holding down the Control key (Option key on Mac).
Declaration
public virtual bool MayCopy()
Returns
Type | Description |
---|---|
bool |
Remarks
This method may be overridden, although in most cases it is easiest to set Copyable. Please read the Introduction page on Extensions for how to override methods and how to call this base method.
MayMove()
This predicate is true when the diagram allows objects to be moved, and some object in the selection is movable.
Declaration
public virtual bool MayMove()
Returns
Type | Description |
---|---|
bool |
Remarks
This method may be overridden, although in most cases it is easiest to set Movable. Please read the Introduction page on Extensions for how to override methods and how to call this base method.
MoveParts(IDictionary<Part, DraggingInfo>, Point, bool)
Move a collection Map of Parts by a given offset.
Declaration
public virtual void MoveParts(IDictionary<Part, DraggingInfo> parts, Point offset, bool check = false)
Parameters
Type | Name | Description |
---|---|---|
IDictionary<Part, DraggingInfo> | parts | a Map mapping Parts to DraggingInfo Objects that have a "Point" property remembering the original location of that Part. |
Point | offset | The offset, before snapping, to move parts. This offset reflects the total amount moved during tool operation, based on original Part locations remembered when the DraggingTool activated. |
bool | check | Whether to check CanMove() on each part; the default value is false. |
Remarks
If check is true this respects the CanMove() predicate for Nodes or simple Parts when this is the CurrentTool. It also respects IsGridSnapEnabled in order to try to automatically snap part locations to a grid. And it also uses the DragComputation function, if any, to determine the new location for each part.
The first argument is a dictionary as produced by ComputeEffectiveCollection(IEnumerable<Part>, DraggingOptions). Call MoveParts(IEnumerable<Part>, Point, bool, DraggingOptions) if you want to move a simple collection of Parts without having to create the argument Map.
StandardMouseSelect()
This override prevents the Control modifier unselecting an already selected part.
Declaration
public override void StandardMouseSelect()
Overrides
Remarks
This also remembers the selectable CurrentPart at the current mouse point.
This method may be overridden. Please read the Introduction page on Extensions for how to override methods and how to call this base method.
StopTransaction()
This calls the base StopTransaction() method, and if the result is true
,
attempts to optimize the transaction by removing all changes except the first and last
by calling Optimize().
Declaration
public override bool StopTransaction()
Returns
Type | Description |
---|---|
bool | the result of the call to rollback or commit the transaction. |
Overrides
Remarks
This method may be overridden. Please read the Introduction page on Extensions for how to override methods and how to call this base method.