Best way to handle user-initiated movement of any shape on my diagram

Oct 24, 2012 at 11:48 AM
Edited Oct 24, 2012 at 12:56 PM

What is the best way to handle user-initiated movement of any shape on my diagram?

I couldn't find any appropriate event in any of the components, so currently I have the following code in the base class all my shapes are inherited from:

protected override bool EndMove(int deltaX, int deltaY)
{
    if (Control.MouseButtons == MouseButtons.Left)
    {
        ShapeMovementHandler.Handle(this, deltaX, deltaY);
    }
    return base.EndMove(deltaX, deltaY);
}

But I think it's an ugly solution.

[UPDATE] Besides, this method is called multiple times, and I need to detect the actual completion of movement, i.e. when the user depresses the left mouse button.

[UPDATE] And it's not correct to rely upon the state of the left mouse button: we can place a shape to the diagram by clicking shape thumbnail on the toolbox and then moving mouse - with no buttons pressed! - to the desired position on the diagram. Well, as I said, this is an ugly solution. Seems it's not a solution at all :((

Coordinator
Oct 24, 2012 at 1:34 PM

I assume you want to distinguish between the shape movements performed by your application and the movements performed by the user?

Instead of trying to find out when the user interacts with the diagram, you should consider assuming that all shape movements were performed by the user except the ones performed by your application:
Handle the History.CommandExecuted or the IRepository.ShapeInserted/Updated/Deleted (etc.) event(s) in order to detect manipulations. Every time your application modifies shapes, detach the event handler and re-attach it as soon as your shape manipulation routine is done (or something like that).

Is this an acceptable solution for your purposes?

Oct 24, 2012 at 1:52 PM

Thank You for the reply!

However, my main problem is to detect when shape's movement finishes. There are 3 scenarios how this may happen:

1. User clicks (MouseDown + MouseUp) left mouse button on a shape's thumbnail in the toolbox, moves mouse cursor (with buttons NOT pressed) over the diagram and then clicks (MouseDown + MouseUp) left mouse button again to place shape in the desired position. I need to catch this last moment, i.e. when the shape occupies its final position after movement.

2. User presses left mouse button (MouseDown) on a shape's thumbnail in the toolbox, moves mouse cursor (with left button PRESSED) over the diagram and then depresses left mouse button (MouseUp) to place shape in the desired position. I need to catch this last moment, i.e. when the shape occupies its final position after movement.

3. (There is already a shape on the diagram, and the user just wants to move it to another position). User presses left mouse button (MouseDown) on the shape, moves mouse cursor (with left button PRESSED) over the diagram and then depresses left mouse button (MouseUp) to place shape in the desired position. I need to catch this last moment, i.e. when the shape occupies its final position after movement.

The problem is that I do not see any suitable event (like, say, ShapeMoveComplete or ShapePositionChanged) in any of your components! Shape has EndMove and MoveByCore virtual methods, but they do not help in all of the three above mentioned scenarios, and besides they are invoked multiple times while user moves the mouse, not just in the final moment.

Distinguishing between user-initiated and programmatic movement is another story; at first I'd be happy to solve the problem described above :)

Thank You.

Coordinator
Oct 24, 2012 at 2:29 PM

There is no chance to detect a "movement end position" at shape level:
Shapes are not supposed to know anything about where they are moved, why they are moved and by whom they are moved.

The right place to detect this would be somewhere in the Tool classes (SelectionTool, LinearShapeCreationTool and PlanarShapeCreationTool), e.g.

    protected virtual void StartToolAction(IDiagramPresenter diagramPresenter, int action, MouseState mouseState, bool wantAutoScroll)
    protected virtual void EndToolAction()

But - as you probably know, the EndToolAction method will be called when the shape movement is already finished, just like the Tool.ToolExecuted events.


May I ask what's the greater aim behind this problem? Why do you need to know the final position of the shape?

Oct 24, 2012 at 2:37 PM
Edited Oct 25, 2012 at 4:38 AM

In fact I'm trying to avoid using grouping in my application. I want to detect the shape's "movement final position", and if the final rectangle of this shape intersects with another shape's rectangle - then assume that the user wants to "place shape into a container"; the latter means that I need to create a certain binding between models of these shapes (kind of "parent-child" relation). After that, if the user moves the "container", I want to also move all "children" of this container (which is another task).

[UPDATE] Besides, if the "movement final position" is not valid (e.g. user tried to place shape into another shape, the latter being not a valid container), then I want to cancel such movement.

[UPDATE] Regarding EndToolAction() method: in order to understand which tool action has ended, I need to write smth. like this:

protected override void EndToolAction()
{
    if ((SelectionTool.Action)this.CurrentToolAction.Action == SelectionTool.Action.MoveShape)
    {
        // ...
    }
    base.EndToolAction();
}

But SelectionTool.Action is a private enum! I am not sure there are strong reasons to have it private. Well, it seems I have to use the corresponding int value (MoveShape = 4), although it's not very nice :((

Coordinator
Oct 26, 2012 at 8:06 AM

Better make it protected and merge the change when updating to the next version.

We see the problem and we have to discuss what's the best solution for getting notified when (and how) shapes are modified. For now, stay with this solution if i works for you.

Oct 26, 2012 at 8:20 AM

Thank You! It will be great if in the next release we get easier ways to detect shape modification. Although I cannot say that implementing a custom SelectionTool is such a hard task, having some alternative would be nice, e.g. events in the Diagram object etc.