How to drag just one node from group inside pale

Hello,
I’m working on a function block editor for some firmware. There are software blocks that can be dragged onto a main diagram from a palette and connected to each other. There are several different types of software blocks - logic, math, compare, etc. The firmware has a fixed # of each type of block, so if there are 4 math blocks and the user drags all 4 onto the canvas from the palette, then there should be no more math blocks available inside the palette for dragging onto the diagram. Also, each block is represented in the palette with a single node at the moment. That node has a # next to it representing how many “instances” are available - sort of like a group, but not technically a “group” in GoXam terms. The alternative would be to show “Math 1, Math 2, Math 3, Math 4, etc.” in the palette, but it seemed more user-friendly to just show “Math (4)” and decrement the # in parenthesis indicating how many “instances” are available as they drag each one onto the main canvas. At the moment, each Math software block gets four instances in memory, but only one of them is added to the palette’s nodesource. Whenever they drag one on, I handle the model change event for addednode and then I remove the node from the palette that was just dragged on, then add the next available instance to the palette. This causes a node to disappear and then reappear with a decremented # underneath (the # is just a property on the GraphLinksModelNodeData-derived class that it’s bound to).
I don’t want the node to disappear and then reappear. I just want it to stay there and have the # decrement until disappearing when all the instances have been dragged on. Is there a way to override the node set when dragging from the palette? Then I could just have a dummy node used in the palette.
Any insight would be appreciated. Thanks!

Remember that each Diagram, including Palettes, can have their own DataTemplates. So I would use a DataTemplate that has a TextBlock to show the number of available instances. That text is data-bound to a property on your data that you can decrement or increment. Remember also that your property setter has to call RaisePropertyChanged when the value actually changes.

You can data-bind the go:Part.Visible property to your new data property, but you’ll need to define a Converter that returns false when the value is zero. If you also specify the Palette’s Layout.Conditions=“Standard VisibleChanged”, it will re-layout automatically as nodes become Visible or not.

And if you don’t want to make unavailable nodes not Visible, you can data-bind the go:Part.Copyable property, so that the user won’t be able to copy the Node when the # instances is zero.

Thank you for the quick reply, Walter.
Yes, I am using different datatemplates. The visible tip is not something I had considered though. However, the main problem at this point is how do I make this one palette node represent a “group” of nodes that can be dragged and dropped onto the main canvas? For example, let’s say I have 5 math nodes that can be on the main canvas. By default, none of them will be on the main canvas and just one of them (the first one, so “Math 1”) will be shown in the palette with its instancecount set to 5 (the specific instance # “1” will not show in the palette data template). The palette datatemplate just simply displays “Math” and the instance count “(5).” When they drag from the palette to the canvas, it will make a copy of the specific node (Math 1). The main canvas node template does show the specific instance, so you will see a “Math 1” copy on the main canvas. At this point, I have to then remove “Math 1” from the palette since it cannot be used twice, and then add “Math 2” to the palette and set its InstanceCount to 4. So now the palette will display “Math (4)” because one of the 5 available math instances has been used and there are 4 remaining. When they drag again from the palette, the cycle repeats itself - “Math 2” will show on the main canvas, and I will have to remove it from the palette, add Math 3 to the palette, set its instancecount to 3. Is there a way to not have to set the specific node in the palette? Like I could catch some sort of dragstarted event for a palette node and then choose the specific instance I want to copy to the main diagram?

It seems to me that if you only show a “generic” node of each type in the Palette, you (the application) can get to choose which particular instance of that type of node should actually be dropped in the target Diagram.

If that’s the case, you don’t need to modify the contents (i.e. the set of Nodes) in the Palette, ever. Sure, due to data-binding as I replied before, each “generic” node will appear differently depending on how many “instances” remain for that type of node. But you don’t need to remove or add anything from the Palette’s Model.

You can implement a Diagram.ExternalObjectsDropped event handler on your main diagram to note when a “Math” node is dropped. You can then modify its data (look at Diagram.SelectedParts, or at Diagram.SelectedNode for the primary selection) according to the next available specific “Math” data.

Ok, that sounds promising. Thanks for the tip! I will give it a shot.

Hmm…the problem with this approach is that we show the node as its being dragged. So you see “Math 1” as you’re moving your mouse on the main diagram. When you let off the mouse button it changes to “Math 4” or whatever the next available instance was. I was able to override the MakeNodeForData method in the partmanager and swap the nodedata to the specific instance I want, which will show the correct instance even as the mouse is moving. However, there are a few things I need to implement to get this fully working with that approach and I’m wondering if it would be easier to swap the nodedata sooner, but I’m not sure where I can do that. Is there a way to tap into the start of the drag operation where the nodedata object that gets bound to the node datatemplate is set? I assume somewhere in GoXam, System.Windows.DragDrop.DoDragDrop is being called and that’s where the initial nodedata gets set to be copied. Is that method overridable?
I’m also wondering if maybe I need to use GoXam’s concept of groups for the palette. So I’d have a “Math” group that would contain each of the individual specific Math instances. The group wouldn’t be expandable in the palette and whenever they mouse down on the Math group to begin a drag, one of the specific instances would be chosen as the drag operation’s nodedata instead of the whole group of nodes. Would this be a better approach?
Thanks!

Yes, override DraggingTool.DoDragDrop. It’s virtual for exactly this reason. To install, replace the Palette.DraggingTool with an instance of your custom tool.

Ah - perfect! Thank you sir! I don’t have the source code, but I was able to pull the original implementation code with DotPeek for that method and see the call to System.Windows.DragDrop, which is exactly what I wanted. Should be pretty straightforward to get it to work. Thanks!

It worked. Just in case anyone else has a similar issue, all I had to do was create a custom draggingtool and set it as the palette’s dragging tool. Then I had to override the DoDragDrop method. There’s an IDataCollection passed in containing links and nodes. I simply called RemoveNode since by default it will have the dummy node I don’t want from the palette. Then I have logic to find the next available instance and then call addnode on the IDataCollection before calling the base implementation. Thanks Walter for all your help!

Hey Walter, one more question. I made a new data class for the library that has a child collection of all the instances. I then added the method below to the palette dragging tool and it works like I want. However, once I drag it onto the diagram, I am unable to draw connections to the ports on the node. What are the reasons you’d be unable to draw a link to a node? I put a breakpoint in the custom linking tool we use to validate connections, but it’s never even getting hit. It’s almost like it’s on a different layer and can’t be connected. Any thoughts? Thanks!

protected override System.Windows.DragDropEffects DoDragDrop(Northwoods.GoXam.Model.IDataCollection data, System.Windows.DragDropEffects eff) { LibraryNodeData node = data.Nodes.First() as LibraryNodeData; data.RemoveNode(node); IFunctionBlock nextAvailableInstance = node.GetNextAvailableInstance(); FunctionBlockNodeData newNode = new FunctionBlockNodeData(nextAvailableInstance); data.AddNode(newNode); return base.DoDragDrop(data, eff); }

For some reason, I am able to draw connections the reverse direction - if I start the mouse at the “to” port and draw to the “from” port, it works fine. It’s almost like the linkableto attached property isn’t getting set for some reason on the ports of the node I dragged on from the palette.

And you’ve set go:Node.LinkableFrom=“True” on the port (FrameworkElement) that you want to have the user be able to start to draw a link?

Yes, it is set.

I just realized two nodes are getting added with each drag from the palette. I hadn’t noticed before because the duplicate node gets put in the top row and I was scrolled way down in a diagram full of nodes. I have not yet figured out why two nodes are getting created with each drag - the dodragdrop method is only getting hit once and the generic node from the palette is getting removed.

Ok, I figured out the issue. I read in another post that GoXam creates two nodes when you drag from the palette. One is just a temporary node on a temporary layer. It then deletes the temporary node once it performs the drop and creates the real node. Unfortunately, we had an IsDeletable property on our node data class that had incorrect logic and was returning false so the temporary node wasn’t getting deleted.