Possible Bug in DragSelectionTool?

Hi,
I have a Diagram with 8 Nodes. Now I select the first row of the nodes (results in 4 Nodes selected).
Than I press the crtl-key to toggle the selection of the lower 4 Nodes (all with rubberband), but every single pixel the Rubberband moves, the included Node selection toggles. So the result depends on the mouse position when mouseup was triggered.
Just to mention I’m using the RealTimeDragSelection implemented as I found it in the documentation.

public class RealTimeDragSelectingTool : DragSelectingTool
{
    public override void DoMouseMove()
    {
        base.DoMouseMove();
        if (Active) SelectInRect(ComputeBoxBounds());
    }
}

That’s not a bug in the DragSelectingTool, but it looks like undesirable behavior in that example class.

The point of that RealTimeDragSelectingTool example is to select Parts immediately during the drag, rather than at the end (i.e. at the mouse-up). As far as I can tell, it works as expected without any keyboard modifiers.

But the Control modifier is supposed to toggle each Part’s IsSelected property. Combining that with selection during mouse-move events means that Part.IsSelected is being toggled continually.

I suppose the solution would be to keep track of the original value of Part.IsSelected for all Parts, so that all behavior reflects the result as if a mouse-up were happening continually.

Ok,
but that wouldn’t change that the Nodes get selected and unselected during the MouseMove which looks very awful especially if many Nodes are included.

I tried to override the DoMouseDown method in my RealTimeDragSelectingTool, to save the IsSelected State of all Parts, but the code in there never runs. Then I tried to override OnPreviewMouseDown, but it’s the same.

None of the Preview events apply to any of the Tools.

Override DoActivate to save the IsSelected state. And override DoDeactivate in order to clear any references to Parts that you may have saved.

Here’s the definition of DragSelectingTool.SelectInRect:

    public virtual void SelectInRect(Rect r) {
      Diagram diagram = this.Diagram;
      if (diagram == null) return;
      
      // choose the selectable Parts within the rectangle
      HashSet<Part> parts = new HashSet<Part>(diagram.Panel.FindPartsIn<Part>(r, SearchFlags.SelectableParts, this.Include, SearchLayers.Parts));

      if (IsControlKeyDown()) {  // toggle or deselect
        if (IsShiftKeyDown()) {  // deselect only
          foreach (Part p in parts) {
            if (p.IsSelected) p.IsSelected = false;
          }
        } else {  // toggle selectedness of parts
          foreach (Part p in parts) {
            p.IsSelected = !p.IsSelected;
          }
        }
      } else if (IsShiftKeyDown()) {  // extend selection only
        foreach (Part p in parts) {
          if (!p.IsSelected) p.IsSelected = true;
        }
      } else {  // select parts, and unselect all other previously selected parts
        // this tries to avoid deselecting and then reselecting any Part
        List<Part> tounselect = new List<Part>();
        foreach (Part p in diagram.SelectedParts) {
          if (!parts.Contains(p)) tounselect.Add(p);
        }
        foreach (Part p in tounselect) {
          p.IsSelected = false;
        }
        foreach (Part p in parts) {
          if (!p.IsSelected) p.IsSelected = true;
        }
      }
    }

Hi Walter,
thank you this is working.

Here some code:

private Dictionary<Part, bool> oldSelectionDictionary;

public override void DoActivate()
{
    base.DoActivate();
    oldSelectionDictionary = Diagram.Panel.FindPartsIn<Part>(Diagram.Panel.DiagramBounds, SearchFlags.SelectableParts, SearchInclusion.Intersects, SearchLayers.Parts).ToDictionary(part => part, part => part.IsSelected);
}

public override void DoDeactivate()
{
    base.DoDeactivate();
    oldSelectionDictionary.Clear();
    oldSelectionDictionary = null;
}

and in SelecInRect the place where IsControlKeyDown:

// toggle selectedness of parts
foreach (Part p in parts)
{
    bool oldIsSelected;
    if (oldSelectionDictionary.TryGetValue(p, out oldIsSelected))
    {
        p.IsSelected = !oldIsSelected;
    }
}
foreach (var kvp in oldSelectionDictionary.Where(kvp => !parts.Contains(kvp.Key)))
{
    kvp.Key.IsSelected = kvp.Value;
}

This would work in a “normal” DragSelectingTool and in RealTimeDragSelectingTool. Wouldn’t it be nice to optimize it a bit and put it in the next release?

Thanks. Yes, we’ll consider it.

A few minor points: Shift drag still doesn’t work in the way I’d expect, the call to FindPartsIn should pass in this.Include (not assuming Intersects), and setting oldSelectionDictionary = null is sufficient for cleanup.

Hi Walter,
I repaired this, but if I use this.Include the Collection is empty always - any hint?

It would probably be more efficient to call Diagram.Nodes.Concat(Diagram.Links).ToDictionary(…).
In other words, just save the value of IsSelected for all Parts. I guess you could filter by Selectable.