Diagram.Select performance issues


I’m getting serious performance penalties when the number of nodes and links increase as soon as I try to select multiple nodes via rubber band selection. I have a test case with 57 nodes and 41 links in my sources and when I try to select some of them (in my case 21) I do get a slight lag but it’s bearable. However, in another test case I have 472 nodes and 439 links and when I use rubber band selection (even when the area is completely empty) it takes about 12 seconds to actually do the select.

Now I wonder how the select function is implemented and why it’s taking so long. Can this be improved? And if, how?

It does sound like there’s something wrong in your app.

When I create a tree with 999 nodes and 998 links in the Tree Layout sample in GoWpfDemo, both SelectAll (via Ctrl-A) and a DragSelectingTool drag operate with minimal delay.

You may need to profile that operation to see what’s taking so much time.

I suppose a very complicated selection Adornment would slow it down. Also, if the nodes are resizable and/or rotatable and if the links are reshapable and/or relinkable, that would also slow things down. But not too much I would think – maybe adding a second or two to the total time.

Ctrl-A also takes time. How may I profile an operation?

Mh … the nodes consist of .png images and those are changed when selecting. Could this be the cause? They’re quite small though. The nodes are not resizable but rotatable and the links ar neither reshapable nor relinkable.

It also seems the selection is taking longer the bigger the difference between the currently selected parts and the parts to select are. If I have 40 nodes selected and I cast the rubber band just so that 38 of those nodes currently selected are selected again it doesn’t take so much time. If I only select 2 of the nodes it takes much longer.

So I take it that you do not have any custom selection Adornments, but you do have go:Part.SelectionAdorned = "True". If you set that to False, what happens with the timings?

That might help narrow down the problem to time to replace the images. How is that replacement made? Is it a Binding, and if so, what is the Binding?

Alright, so after replacing all the icons with a DrawingImage in which I can simply set the Brush to change the color of the Geometry rather than changing the Image itself, I’ve realized that it still takes seconds to load. At least, unselecting the elements is quick now.

So the structure of the whole thing is that we have a SelectionChanged event handler attached, who sets a property Status for all selected nodes and the setter of this Status now sets the new color for the DrawingImage. Now the problem I found out is that the SelectionChanged event handler gets called for every single selected element so when I drag-select 80 elements I get my SelectionChanged handler called 80 times, which then runs 80 times through all items in the NodesSource and all selected items to set the status and change the color. With about 500 nodes that’s 40000 times setting a status and updating the view and with all the event handling overhead I’m not surprised it takes that long. Is there a way to call the SelectionChanged handler only once with all selected elements?

Could you have a Binding on Path=Node.IsSelected ?

No, as I have four different states with each an own image.

Why can’t you change your event handler not to iterate over everything, but just modify the node whose IsSelected state changed?

I don’t quite get what you’re trying to suggest. What am I supposed to bind to Node.IsSelected?

Currently I have a single SelectionChanged event handler added to the Diagram, and this is called for every single node upon using the DragSelectionTool. In this case I have no idea which node’s IsSelected state just changed.

If you don’t want to use a binding on Node.IsSelected, that’s OK. (However, I might as well point out that maybe you could have used a MultiBinding. No matter.)

In your Diagram.SelectionChanged event handler you can look at the SelectionChangedEventArgs to decide what became selected or unselected.

This solved the problem, thanks. Still I wonder why the event handler gets called for every element instead of once for all elements.

For the DragSelectingTool, or for the SelectAll command, such an event is quite possible. However such an event is not possible in general, be ause there is no way to tell when some code might stop toggling IsSelected on some number of Parts.