Display.ShapeDoubleClick event does not work as expected

Nov 13, 2012 at 2:55 PM

Hi,

If I have two shapes such as one (smaller) lies "above and inside" another (bigger), and I double click on the smaller shape, then the ShapeDoubleClick event gets fired twice: first for the bigger shape, and then for the smaller one. Is it a bug?

As a workaround, I wanted to check shape's z-order index in the beginning of the ShapeDoubleClick event handler: if it's not the topmost shape (index = zero) - then just ignore this event. However, I couldn't find a way to determine shape's z-oder index. So I had to use another event - Display.MouseDoubleClick:

private void display1_MouseDoubleClick(object sender, MouseEventArgs e)
{
    if (this.display1.Diagram != null)
    {
        Point pt = Point.Empty;
        this.display1.ControlToDiagram(e.Location, out pt);
        Shape shape = this.display1.Diagram.FindShape<MyBaseShape>(pt);
        if (shape != null)
        {
            // do something...
        }
    }
}

Here Diagram.FindShape<T> is an extension method:

public static Shape FindShape<T>(this Diagram diagram, Point pt)
{
    foreach (Shape shape in diagram.Shapes)
    {
        // NOTE: Help says that IShapeCollection "encapsulates a collection of
        // shapes sorted by z-order", so I do not check here that Z-Order = Top.
        // By the way, I simply do not know how to check this :)
        if (shape is T && shape.ContainsPoint(pt.X, pt.Y))
            return shape;
    }
    return null;
}

This workaround works, but... it would be much better to use the ShapeDoubleClick event (should it work as expected).

Coordinator
Nov 14, 2012 at 3:10 PM
Comanche wrote:
As a workaround, I wanted to check shape's z-order index in the beginning of the ShapeDoubleClick event handler: if it's not the topmost shape (index = zero) - then just ignore this event. However, I couldn't find a way to determine shape's z-oder index.

The shapes of an IShapeCollection are always sorted by ZOrder. This means if you are only interested in the topmost shapes, process them using the "TopDown" enumerator:

public static Shape FindShape<T>(this Diagram diagram, Point pt)
{
    foreach (Shape shape in diagram.Shapes.TopDown)
    {
        // As the shapes are sorted by ZOrder, we can take the first shape 
        // containing the given point.
        if (shape is T && shape.ContainsPoint(pt.X, pt.Y))
            return shape;
    }
    return null;
}

So much for your workaround. I will check the "ShapeDoubleClicked" issue and reply as soon as I know more.
This may take a few days - I'm quite busy at the moment.

Nov 14, 2012 at 3:48 PM

Thank You for Your reply.

1. As far as "IShapeCollection encapsulates a collection of shapes sorted by z-order", why should we use TopDown property? in other words - why should we need to sort what is already sorted?

2. If the only thing I have is an instance of a Shape object, how can I determine its ZOrder? am I supposed to search IShapeCollection for my shape and - when it's found - treat its position within this collection as its ZOrder index?

Coordinator
Nov 20, 2012 at 9:37 AM
Edited Nov 30, 2012 at 12:14 PM

First of all:
Yes, it's a bug. Here is the corrected version of Display.ProcessClickEvent:

private void ProcessClickEvent(MouseEventArgs eventArgs, bool isDoubleClickEvent) {
// Check if a shape has been clicked
    if (!ScrollBarContainsPoint(eventArgs.Location) && Diagram != null) {
    int mouseX, mouseY;
        ControlToDiagram(eventArgs.X, eventArgs.Y, out mouseX, out mouseY);
        // If a selected shape was clicked, the event will be raised for the selected shape (even if it is behind other shapes)...
        Shape clickedShape = null;
        foreach (Shape s in selectedShapes.FindShapes(mouseX, mouseY, ControlPointCapabilities.None, 0)) {
        clickedShape = s;
            break;
        }
        // ... otherwise, the event will be raised for the topmost shape containing the clicked coordinates
        if (clickedShape == null) {
        foreach (Shape s in Diagram.Shapes.FindShapes(mouseX, mouseY, ControlPointCapabilities.None, 0)) {
            clickedShape = s;
                break;
            }
        }
        // Raise event (if a clicked shape was found)
        if (clickedShape != null) {
        if (isDoubleClickEvent)
            OnShapeDoubleClick(new DiagramPresenterShapeClickEventArgs(clickedShape, WinFormHelpers.GetMouseEventArgs(MouseEventType.MouseUp, eventArgs)));
            else OnShapeClick(new DiagramPresenterShapeClickEventArgs(clickedShape, WinFormHelpers.GetMouseEventArgs(MouseEventType.MouseUp, eventArgs)));
        }
    }
}

Attention: The ShapeClicked event is raised before the MouseUp event. When clicking overlapping shapes, the selected shape changes after the the ShapeClicked event was raised for the selected shape.

Regarding question 1:
Don't worry, nothing will be sorted when using the TopDown or BottomUp. As the shapes are already sorted inside the ShapeCollection, the enumerators returned by the TopDown and BottomUp properties only differ in start position and processing direction. No lists/arrays will be filled and nothing will be sorted, all shapes will be yield return'ed if they match the search criteria.

Regarding question 2:
At the moment, you cannot access the ZOrder directly. We will change this (don't know yet whether it will become public or protected) but until that happens, the only way to determine the Z sorting is by enumerating them in the appropriate direction (via ShapeCollection.TopDown or ShapeCollection.BottomUp).
Btw: ZOrder has nothing to do with list indexes because...

  1. ...the ShapeCollection is not a simple list but a MultiHashMap with spatial index.
  2. ...the ZOrder is determined by the repository as diagrams will support partial loading in furture versions and the only component that can determine ZOrder values of not-yet-loaded shapes is the repository.
  3. ...the ZOrder values should be unique referring to the collection but this is not strictly manatory. It can happen that there are duplicate ZOrder values.
Nov 30, 2012 at 9:44 AM

Thank you very much for such a detailed reply!