This thread looks to be a little on the old side and therefore may no longer be relevant. Please see if there is a newer thread on the subject and ensure you're using the most recent build of any software if your question regards a particular product.
This thread has been locked and is no longer accepting new posts, if you have a question regarding this topic please email us at support@mindscape.co.nz
|
Jason, So this issue had been driving me nuts, It took me a lot of time to figure out what's happening. Again, I prefer we manage to resolve this issue without having to create a reproduction project. I think we can. So here is how my problem begins. We need to provide a copy-paste capability. We have our own logic that copies and pastes so we do what we do to our data model, and then we add nodes and connections to the diagram when the user pastes. Another requirement that we have is that when the user pastes - all currently selected items get deselected and the newly pasted ones get selected. This is were things go completely out of sync. We use the following to set a binding between The DiagramNodeElement.IsSelected to our view model IsSelected property (the value of the Data property is our custom view model):
We then expect that if changes are made to the view model IsSelected property it will propagate to the element in the diagram. Mostly it works ok but there are problems that are caused due to Mindscape's virtualization mechanism that we cannot resolve ourselves unless we change your code - which we prefer to avoid. So take this case for example - the user pastes a bunch of new nodes - behind the scenes - we add the nodes to the FlowDiagramModel.Nodes collection. We then set, through the view model their IsSelected property to be true (and we clear the existing ones to false). Since we have the above style we expect the binding to do its thing and ensure the node elements follow the view model. What's happening in reality is that nodes within the viewport behave perfectly while nodes outside of the viewport behave really inconsistently. So I spent some time taking a look at your code and I think what i'm seeing explains why the problem is happening. Check this method (in your code under the DiagramSurface class):
And look at the marked line: nodeElement.IsSelected = _selectedItems.Contains(node); When the viewport changes - you guys are creating new diagram node elements, but when the node element is created you determine whether it is selected or not depending on if it exists in the _selectedItems collection. Now the question is how that collection is managed. I can see that items are added or removed from this collection when there is an explicit call to SelectItem()/DeselectItem() methods and also when the underlying ListBoxItem.IsSelected is changing. But - if the node is outside of the viewport there's no ListBoxItem to represent it, so the binding doesn't do anything - you don't listen to my binding cause there's no UI element to represent it. So when the node gets into view it gets deselected. The wired thing is that some nodes get deselected while others aren't, this i couldn't explain yet, it was just too much for me. There are few options as far as I can see - Option 1: When the node element is created - check if there's binding set on the nodeElement.IsSelected property - if so - take the value from the binding source, this has a downside, cause despite the fact the my data object would have the node marked as selected the SelectedItems in the DiagramSurface would not indicate that, hard to say if it will cause additional issues. But as long as it doesn't - it is good enough for me. Option 2: Make the SelectedItems bindable, currently it is a read only collection, i can't define binding on this. But if you enable this - it will give me a way to tell you what are the nodes that are selected regardless to whether there's a UI element to represent them or not. In this case i will still need to maintain the above style that binds the nodeElement.IsSelected to my view model IsSelected property and maintain an observable collection of selected nodes. Option 3: Create a point in which i can interfere in the process of creating a new node element - so I can write my own logic that checks whether my data object is selected or not and set the value. That would solve my personal issue - but other customers might bump in this issue as well and they will have to follow the same workaround. option 4: Knowing much better than me - maybe you have a better option that would fix my issue, a workaround is also acceptable as long as it won't cascade and cause other issues. This is causing us serious issues, not only in the copy/paste feature, also while trying to implement custom selection mechanism (which i wrote you about before) - we constantly end up with situations where our data objects are out of sync or cases where the diagram is out of sync with our data layer. In fact - on our custom selection implementation we ended up setting both the view model IsSelected to true (and false to deselect) and calling the _daigarmSurface.SelectItem(item) method (and respectively for deselection), eliminating one of them causes wired things to happen, either it doesn't get selected in the UI or the view model isn't getting updated. In addition - we have other side affects because of that, for example - when the user clicks on an empty point on the surface the diagram immediately deselects all items - this is a desired behavior but the problem is that since all nodes outside of the viewport don't have a UI element to represent them - there's no binding that pushes the deselection to the view model IsSelected property. This one we managed to bypass by detecting the point where you guys auto deselect everything and we make sure to go and update all our node data object to be unselected. But that's truly a mass. What's even more wired is that again while we were implementing the custom selection - at the beginning we only used the SelectItem(item) and DeselectItem(item) methods of the DiagramSurface (as opposed to calling them in addition to explicitly setting our data object IsSelected to be true or false), but then when we selected multiple nodes and moved them on the surface as a group - the node being held (in the group move) would move in an out of sync way, its just wired, its movement isn't in sync with the rest of the elements in the group. Somehow doing both sort of fixed the issue, I hope it did, we still need to take it under intense testing to ensure it doesn't happen in other corner cases. See what we had to do to bypass it... it's ain't pretty:
Set both.... eliminating one would cause un-explainable behavior. So when i'm saying that i'm concerned from side affects - this kind of side affects is what i'm talking about. If you need to see it in action i think the following steps will help you to easily reproduce it, i bet you already have a baseline project that you're using so it would be relatively easy for you to reproduce it:
HELP!! :-) Gili |
|
|
Hi Gili, Apologies for the issues, the selection is not very easy to control. First I'd like to say that due to the virtualization, I wouldn't recommend binding from your viewport directly to the IsSelected property of the node elements. As you've found, this is not reliable. The IsSelected property should be reserved for signaling the visual elements to display the selected visuals, and to transfer selection user interface actions back to the DiagramSurface (Such as clicking or marquee dragging). The DiagramSurface maintains a selection 'model' within itself which is mainly the collection you pointed out. If you really need your node view-models to maintain its only selection state, then it is the internal DiagramSurface selection model that you'll want to be syncing with - rather than the visual elements - which the DiagramSurface should take care of. To keep these two models in sync, you could attach an event handler to the SelectedItemsChanged event. In the handler, iterate the SelectedItems property of the DiagramSurface to update the selection state of your view-models. Unfortunately as this does not tell you which items have been deselected, you'll need a way to find all the currently selected nodes in your model and deselect the ones that are no longer selected. To go back the other way, you could listen to when the IsSelected property changes in your view-models, and as a result, use the SelectItem and DeselectItem methods to update the internal DiagramSurface selection model. The DiagramSurface has checks to prevent infinite loops occurring between syncing, but you may want to include checks in your syncing logic too to prevent redundant operations. (Such as if you're in the process of listening to the SelectedItemsChanged event which sets the IsSelected properties in your view-models, then there's no point calling the SelectItem method as a result of that). Hope that helps. I really recommend moving to this approach and removing the bindings. Let me know if you come across any side-effects from this and I'll see if I can help out. -Jason Fauchelle |
|
|
OK. I think I have an idea on how to do it. I'll try it and see f it works. |
|
|
I think though if you guys exposed a SelectedNodes property in the DiagramModel this issue could have been more obvious. Anyway, don't do it. It is more than just a tiny change. But I think that as an infrastructure Mindscape should have a better solution for something so fundamental. |
|
|
I agree, selection customizability and binding for WPF Diagrams could be greatly improved. If I had time, I'd certainly fix it up. Hopefully we can work around it though. |
|
|
Yes! wish me luck :-) |
|
|
So Jason, I gave it a try and mostly it is working well. There was only one issue I had to workaround, I can't really make sense out of it but at least I managed to find a way to fix it. It appears that if I use the native DiagramSurface.SelectItem and DiagramSurface.DeselectItem to select or deselect a bulk of instances it interferes with with their movement. So after I make a selection and then i drag and move the nodes as a group the node under the mouse move is out of sync with the rest of the nodes. I managed to fix the problem by hiding these methods (I'm inheriting DiagramSurface) like this:
And a similar method to SelectItem of course. I have copied the same FindElement method from your code but the order in which the code is executed seems to make a difference. No idea why. Even if i first call base.DeselectItem and then call my code the problem still manifest. Other customers may have a similar issue so I think you guys should check it out despite the fact that I had found a workaround. Thanx, Gili |
|
|
Hi Gili, Good to hear there was only one issue. This was caused by the same node being added to the internal collection of selected items multiple times. This was easy to fix - we were missing a check in one of the cases where the collection is updated. A fix for this will be available in the next nightly build. -Jason Fauchelle |
|
|
Awesome! :-) I'll try it tomorrow! Thank you! |
|
|
Thank you Jason, your fix worked like a charm. :-) |
|
|
Jason, at first it looked like everything is perfect but we started having tests failing. When I debugged it I noticed that when I call SelectItem/DeselctItem the selection changed event isn't fired. Is that expected? |
|
|
Hi Gili, The selected items changed event will be raised when those methods are called in the next nightly build. -Jason Fauchelle |
|
|
Thanx! :-) |
|