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
|
I have pre-existing data trees that need to be displayed in a WPF application. They are very basic, and only get to a max height of maybe 10. I have attached a screenshot as an example of what they might look like. I need the user to be able to open each diagram and then perform the following: 1) Collapse/Expand each node. 2) Add/Delete nodes by right clicking or some other method. 3) Each node needs maybe 3-4 display labels to be visible. So the user will not "build" a diagram. It will be displayed for them based on what is in the database. They only need to expand/collapse for viewing purposes and then do very minor manipulation of the tree, such as add/delete. I believe I read that you do not support direct ViewModel binding. Can you suggest how I might accomplish my goals with your WPF Diagram control, and what data model shape might support this best? The data will begin in a database with relationships, but I can easily load it into some sort of tree abstract data type if that is the easiest way to make your control work with it. Very anxious to hear any replies. |
|
|
Hello and thanks for evaluating our WPF diagramming product! All the things you have mentioned here are possible, but it will take some time to explain all of it to you. So for now, lets start with the basics. There are many ways to achieve what you need, all equally as good as each other. I'll show you one way that I recommend you approach this. Feel free to modify it if you have other patterns you like to follow. Also, I have attached a sample demonstrating what I explain here so you can follow along and get started. To run the sample, make sure to add a reference to your copy of the Mindscape.WpfDiagramming.Foundation.dll. First off, you'll want to implement a custom node model type, and a connection model type. I have placed these in the Model folder. The node extends the DiagramNode class, and the connection extends the DiagramConnection class. These classes that we provide implement the default base functionality for diagrams so you don't need to do it yourself. The main thing you need to do when implementing the node, is to setup the connection points in the constructor. (Connection points are used to connect connections to nodes). Based on the image you provided, I added one connection point to the top and bottom edge of the node. We do not provide a built-in branch expanding feature yet, but you can implement this in the model classes. In the model classes in the demo I attached, you will see the IsVisible and IsExpanded properties. These are place-holders for adding the expand/collapse functionality. The IsExpanded property of the node would be binding to a button on the node visual. When this property changes on a node, you can walk through the node structure to set the IsVisible property of the appropriate nodes and connections. The IsVisible properties can the be bound in the node/connection visuals to hide/show them. You can continue working on this implementation in the sample I attached, or let me know if you need help with implementing this feature and I'll update the demo for you. After implementing the model classes, you'll then want to provide styles/templates to visualize the node and connections on the DiagramSurface. All the code for this can be found in the DiagramStyle.xaml file in the Styles folder. At the top of this file is the node style. A setter is used to set the NodeTemplate property where you can provide a DataTemplate to visualize the node. Here I have put a border and a ContentPresenter to display the node content/data. Below the node style is the node content template which simply displays the Data property in a TextBlock. After this is the connection style which simply sets the color and thickness of the path. You can also use this style to give the path rounded corners or add arrow heads etc at each end. Next is a few template and style selectors. As we only have one template/style for each part of the diagram, these can just be fixed selectors. Then finally is the DiagramFormatter which brings everything together via the selector properties. In MainWindow.xaml, add the DiagramStyle as a merged dictionary, and set the Formatter property of the DiagramSurface. The last thing I'll point out in the attached sample is MainWindow.xaml.cs where I hard code a basic diagram. Here, create a new instance of Diagram and a few custom node and connections implemented in the first step. Nodes and connections can be added to the Nodes and Connections collections of the Diagram. To build a connection, you need to pass in the source and destination connection points to the constructor. These connection points can be obtained from the ConnectionPoints collection of the node instances. This gets you the connection-points you set up in the custom node constructor. All the code here can be replaced with code that reads the diagram from a file or from the database. You'll also notice I'm setting the Data property of the nodes to be a string. The Data property can be any object, so you could implement a class that includes the 3-4 label values that you need. Or alternatively, add more properties to the custom node implementation to hold each of the label values which can then be bound to in the node template. At the end of MainWindow.xaml.cs, I create a new instance of our TreeLayoutAlgorithm and run that over the diagram model. This will nicely arrange the nodes in an appropriate tree like structure for you. This project will give you a great place to get started. Look through it to see how things work with our diagrams library, then let me know if you need help with the more advanced features. Also, let me know if you have any questions about what I have explained here. Jason Fauchelle |
|
|
Incredibly detailed response, Jason. I'm very impressed. I will begin working through your demo, I can't thank you enough. I thought of one thing I should have originally asked... I have a strict requirement being that my user base only has .NET 3.5 with SP1 installed. How does this affect the ability to implement what I am trying to do and what you have demoed for me? Does the diagramming control support this? Do you have a list of supported features for .net 3.5 vs 4.x? Edit: I just noticed your Project file targets the 3.5 framework... I am hoping this is a good sign? Edit 2 : TreeLayoutAlgorithm was not found in the trial version I downloaded, but it was found in the 11/5 nightly. Should I assume TreeLayoutAlgorithm still exists in a branch and is not merged into the stable/release version? |
|
|
Hello The diagramming library itself targets .NET 3.5 so you won't have any issues there. All features of the diagramming component are available in both 3.5 and 4.x versions. Yes, I forgot to mention, TreeLayoutAlgorithm was not added in version 2.0, but instead was added in a nightly build based on a customer request. As you may expect, nightly builds get merged in to the release version whenever a new point release or major version is released. We happen to be working on the next major version at the moment which should be released near the end of the year. The main addition to this version (apart from merging all the nightly builds) will be improved performance. Jason Fauchelle |
|
|
Your demo project is very clear to me, thanks for doing that. I have been searching your forums for onclick handling on Nodes. http://www.mindscapehq.com/forums/thread/2367 1) Can you go over how to handle click events in the new 2.0 version, best way to handle a left-click for my purpose of expand/hide and also right-click that might open a context menu? Maybe a small piece of code as well. At this point, I'm just curious what needs to go in the XAML and how to wire it up to the code behind. 2) Just to make sure I am clear... WPF Diagrams is NOT included in the Elements package, correct? It is a single, separate purchase? |
|
|
I've made a little progress by following your advice in this thread on how to capture the doubleclick event: http://cdn.mindscapehq.com/forums/thread/2367 ...specifically, your suggestion as to capturing doubleclick events using:
So I am now able to double click a node, and determine which node was double clicked. What I need to be able to do, is capture the single click or MouseDown event. I notice when I single click a node, the event is handled somewhere else and the node is surrounded by black brackets (not sure what your terminology is for these brackets). 1) Can I capture the MouseDown (or click or whatever) event and respond to it? 2) Can I avoid showing those black brackets? Edit: Just realized they are controlled by "IsResizable". Disregard this one. 3) My goal is for the click event on the entire node object to expand/hide the subtree. Will this be doable, or do I need to put a control inside the node (a plus/minus control) to allow for handling of the click event and then expanding/hiding the subtree? 4) Can the CollapsableConnection/DiagramConnection be set to a state so the user cannot modify it or select it? |
|
|
Hello You are correct that Diagrams is NOT part of the Elements package. If you are interested in both products, then you should take a look at the Mindscape Megapack: http://www.mindscapehq.com/products/megapack. This gets you ALL Mindscape products for a very low price. Although you probably don't need them all, this is the cheapest option if you need both the Diagrams and Elements products. To answer your questions 2 and 4, the best option is to set the IsReadOnly property to True on the DiagramSurface. This will disable all the built-in editing controls in the diagram including the resizer thumbs (black brackets). By setting this property, you won't need to do anything about the IsResizable property. Also, you can be rest assured that this property won't effect any of the custom functionality you provide such left/right clicking. I have attached an updated version of the previous demo I sent you. Again, to run this demo, make sure to include a reference to the Mindscape.WpfDiagramming.Foundation.dll. First off: left click to expand/collapse branches. The best way to do this would be to add a button to the node template as you mentioned to toggle the expanded state. It is still possible to catch mouse events from the node directly, but this can lead to messy code that is difficult to maintain - so we will take the button approach. To begin, you should implement the expand/collapse logic. This is where we will use the IsExpanded and IsVisible properties I mentioned last time. All the code for this logic can be found in the CollapsableNode class in the new sample I attached. This code is fairly straight forward and easy to follow. It is recursive logic that is slightly different in the IsExpanded and IsVisible property setters. Also, keep in mind that this logic makes acceptable assumptions about the structure of the diagram. i.e. the polarity of the connections, and the type of nodes that it will find as it walks through the connections. Feel free to put in any type checks and so on if you want. Next lets move to the visual side of things in the DiagramStyle.xaml file. We need the IsVisible property from the model to control the actual visibility of the displayed elements. This is done by adding Style.Triggers to the node style and connection style. Here is a single DataTrigger that listens to the IsVisible property, and if false, sets the Visibility property of the element to Collapsed which will stop it from being rendered. Now we need a button to control the IsExpanded property on the node. Rather than a plus/minus control, I've made a button style that looks like the node itself. So when you run the attached sample, it will look exactly the same as the sample I sent you last time, but now if you left click the nodes, they will collapse and expand. (You can change this template if you prefer a plus/minus button - that can be useful if there are other controls on the node). The NodeButtonStyle can be found near the top of the DiagramStyle.xaml file. It has a setter to set the Template property which basically includes the same elements within the node-template of the previous sample (a Border and a ContentPresenter). Now in the node template, rather than having the Border and ContentPresenter, we have a ToggleButton that uses the NodeButtonStyle, and sets the Content and ContentTemplateSelector properties. The IsChecked property of the ToggleButton has a TwoWay binding to the IsExpanded property on the node model to complete this feature. Last off is the right click context menu. Rather than listening to the right click event, you can use the WPF ContentMenu feature. In DiagramStyle.xaml, you will see I set the ContextMenu property of the ToggleButton in the node template. This contains a "Delete" item but doesn't do anything yet. Let me know if you need help with wiring up a MenuItem. I look forward to hearing how you go with this next iteration of the demo. Jason Fauchelle |
|
|
Great demo. This really help me understand your product. Can I ask what XAML designer is recommended and fully supported with your WPF controls? Expression Blend? |
|
|
Expression Blend would be the best designer to use. WPF Diagrams doesn't have as much designer support as Elements, but you can always ask us if you need help with templating something. Jason Fauchelle |
|
|
Jason, I've been making great progress with the example you provided. I've come across two questions: 1) How can I make the diagram collapsed by default when it loads. 2) As the size of my tree diagram model grows, since it expands by default, the initial view is enormous and makes it tricky to scroll and get the thing back in a manageable size. Even when I do collapse enough nodes back to the 1st level or two of the tree, it is still too wide to fit and the nodes are in their original spacing. Can I re-size the diagram based on what nodes are expanded? My goal here is to keep things tight and only expand them out as needed. Hope I'm making sense. |
|
|
Hello Good to hear you are making great progress. I'll get back to you about your questions after the Christmas break. Jason Fauchelle |
|
|
Thank you. Let me know when you are back. I am just about ready to purchase if this spacing issue can be controlled, which I'm sure it can. |
|
|
Hello I got back today. The spacing issue can certainly be controlled. Either the diagram could be resized (zoomed in/out) or the nodes could be easily rearranged to reduce the spacing and make the diagram easier to read. The nodes can also be collapsed by default as you asked in your first question. I'll get back to you with actual details of how to do these in the next couple of days. Jason Fauchelle |
|
|
Hello 1) To have the diagram collapsed by default, first go to the CollapsableNode class and set the _isExpanded field to be initialized to false. Next, go to the CollapsableConnection class and add this to the constructor:
This is a basic check to set the visibility of the connection and the destination node to false if the source node is either collapsed or invisible. This will correctly set the visibility of nodes and connections as the diagram is being constructed. Note that this code only works if the connections are created in order from the top of the diagram to the bottom. This should be fine in your case, but you could make the code recursive to handle any ordering if you need to. 2) To resolve the spacing issue, I recommend running the layout algorithm every time a node is expanded or collapsed. This will rearrange all the nodes to only take up as much space as needed while ignoring invisible nodes. To do this:
And the event handler would look like this:
Run this up to see the algorithm adjust the layout every time a node is expanded or collapsed. See that when lower levels are collapsed, the higher levels have less space between them because the invisible nodes are ignored. You'll notice the diagram may move around a lot now. This is because the tree layout algorithm aligns the final diagram layout relative to the (0,0) coordinate of the diagram space. To resolve this, download the next nightly build of Diagrams 3.0 to get a StationaryNode property on the TreeLayoutAlgorithm. After instantiating the layout algorithm, or after constructing the diagram model, set the StationaryNode property to be the root node of the diagram. This will cause the root node to never be moved by the algorithm, and all the other nodes will be positioned based on the root node position. You can manually set the position of the root node to center the initial diagram if you want. Let me know if you have questions about any of this. Jason Fauchelle |
|
|
Thanks Jason, look forward to giving this a go. Can you confirm that WPF Diagrams 3 is still 100% compatible with .net 3.5 SP1? |
|
|
Diagrams 3.0 is still 100% compatible with .NET 3.5 SP1 - no changes were made to shift the target framework. Jason Fauchelle |
|
|
Jason, everything you provided works exactly as you state, thank you. I was able to center the parent node horizontally on the screen using the following:
1) Is this the correct way to center a node? Or do you recommend another method? 2) Is there a more efficient method for obtaining the rootNode of the diagram? I am using IDiagramNode rootNode = Diagram.Nodes[0] as IDiagramNode; because I know I load them in order, but will this always give me the root node for my tree? 3) How can I reset the scroll position back to its original place, like when the Diagram Surface was first initialized? All is working now except for when I scroll around and then reload another Diagram. The Diagram Surface remains scrolled to the last position when the new diagram is loaded so if I am scrolled way to the right and load a new Diagram, it is also scrolled way to the right. *Please note that I really am not "scrolling" my diagram using the scroll bars. I drag everything as this feels much more intuitive and user friendly. It seems scrolling and drag/panning are treated separately by the Diagram Surface. 4) Any option to limit drag/panning? I noticed in this post that you provided a method to limit infinite scrolling: http://www.mindscapehq.com/forums/thread/262064 However, I have my Diagram Surface set to "Pan" for it's DragAction, so it allows me to drag/pan infinitely. |
|
|
Hello 1) The code you have posted is the correct way to center a node. 2) The diagramming framework does not reorder your model nodes, so your way of getting the root node is fine. One other way to do this would be to have a property somewhere in your application such as in the view-model which stores the current root node. This may just be more convenient than getting it from the collection every time. 3) You can adjust the scroll positions by using the DiagramSurface.SetViewport method. 4) Thanks for this suggestion, I have changed this so that the drag-panning respects the AllowInfiniteScrolling property. This behavior makes more sense. This will be available through the next nightly build. Jason Fauchelle |
|
|
Thanks, I got your #3 suggestion working by following your attached example code in this posting: http://www.mindscapehq.com/forums/thread/265575 However, I noticed in this example you make reference to a DiagramSurface property "ShowConnectionPoints", but this property is not found in the nightly build I am using, 20130115. Commenting this out of course worked, but thought I would mention this. |
|
|
This property was deemed redundant for version 3.0 so it was removed. It is totally safe to remove setting this property when following the old samples. Jason Fauchelle |
|
|
Ok, I've run into a situation where not all collapsible nodes where IsVisible == true are being added to the visual tree. It's not going to be easy for me to put together an example project to demo it to you so I thought I'd ask a question first. If you have absolutely no idea, I will try to put together a reproducible example. What is happening is I am loading a Diagram of about 100 Collapsible nodes, and I'm having only Level 0 and Level 1 of the tree be visible by default. If I then expand Level 1, showing Level 2 nodes, sometimes the level 2 nodes are not displayed. The connections are always displayed, but not the nodes. So it looks like I have dangling connections. If I snoop the app, the nodes not showing are not in the visual tree. But if I then click anywhere on the Diagram Surface they appear and re-snooping shows them in the visual tree. They appear on the MouseUp of the click on the Diagram Surface, not the MouseDown. They will also appear if I scroll or drag the surface in any direction or zoom in/out. Clicking anywhere in my app outside of the Diagram Surface has no affect and they remain out of the visual tree. So this is my question: What happens on the MouseUp event of the Diagram Surface that would cause the Visual Tree to refresh itself? |
|
|
Hello I had been able to reproduce this a while ago using the demo I sent you. I don't remember fixing this issue, but I can't seem to be able to reproduce this any more. Try updating to the latest nightly build and see if that resolves the issue. If not, I'm afraid I'll need a repro project to look into this further. The issue may be caused by the virtualization - this works out what is currently on the screen. When you release the mouse, the virtualization can be applied which in-turn updates the visual tree. Jason Fauchelle |
|
|
I tried the 20130215 nightly build the other day and the problem continued. Not sure what you mean by virtualization... |
|
|
Virtualization is a performance enhancement that was added in 3.0. Rather than rendering every single node at all times, the DiagramSurface will only render nodes that are within the viewport. That is what is meant by virtualization. Since some nodes are not being rendered when they should be, it is most likely a bug in the virtualization. i.e. it may be incorrectly thinking that a node is off the viewport where actually it should be rendered. I guess I was a bit too detailed when explaining what happens when the mouse is released - that is, virtualization is re-run which results in the visual tree being updated. Somehow this re-run causes the virtualization to correctly identify what is in the viewport. |
|
|
Ok, interesting that you mention the viewport. The pattern seems to be this: When I initially load my diagram, any nodes that extend off to the right of the screen (outside the viewport) have the visual problem. So when I load up my diagram, I have enough level 1 nodes that they extend out to the right of the viewport. When I scroll to the right and expand those nodes that were out of the viewport, they don't show up until I click the surface or scroll again. |
|
|
Thanks for this information. I have now been able to consistently reproduce this issue and will work on a solution soon. Jason Fauchelle |
|
|
This bug has been resolved and the fix will be available in the next nightly build. Jason Fauchelle |
|
|
Awesome! Thanks so much. |
|
|
I am now working with the latest 20130220 nightly... VisualTree issue is fixed, thank you. I have started working with and have noticed an issue with the ContextMenu of our CollapsableNodes. Working with the 2nd demo project you posted in this thread, right clicking a node always brings up the context menu, but sometimes the "Delete" label shows, sometimes it does not. You should be able to reproduce this with your demo project in this thread. In my current application I am working on, I attached Command handlers for the context menu and I noticed that the node that is right clicked on sometimes passes in null to the handler and sometimes it passes in the node correctly. The only pattern I can find to the behavior is if I left-click the node first, the right click almost always works.. Here is my XAML for my context menu and the code in my view model:
|
|
|
Thanks for pointing out this unusual issue. This appears to be an issue in WPF that has been induced by one of our performance enhancements introduced in Diagrams 3.0. The default node style has been made as simple as possible, but when the mouse moves over a node, or the node is selected, the template is switched for a more complex template that includes the user controls such as the resizer, move thumb and connection point thumbs. This template switching seems to be messing with WPF ContextMenus. I have looked into this for a while but havn't been able to find a way to work around this at our end. The only way to resolve this seems to be to bypass the template switching mechanism. To do this, copy the code below into your main resource dictionary. This will replace the default node style with a style that does not perform template switching. To apply this style make sure to set BasedOn="{StaticResource BaseNodeStyle}" on all your node styles that include context menus.
If this causes a performance issue, you may be able to strip down the above code to only include what you need. For example, if you don't allow for node rotation, then you can remove the RotateThumb from the BaseNodeStyle. Jason Fauchelle |
|
|
Thanks Jason, Can you provide an example of how to get a reference to the node's DataContext that was right-clicked on, in the ContextMenu handler? Edit: refer to the method I am currently using in my previous post. The bound node that is passed in to the handler is still null sometimes. You're last post did fix the visual problem, so thank you. |
|
|
Hello I was unable to reproduce this issue - the command parameter always has the correct node. I was using standard WPF commands though. You could either switch to using standard WPF commands, or send me a repro project so I can see why it doesn't work with the DelegateCommand. Jason Fauchelle |
|
|
Interestingly, I've discovered that the CanExecute handler is the one that "sometimes" gets the node passed into it. The Execute handler seems to always work. This could be a PRISM issue, but I'm not going to worry about it at this point. I have bigger tasks to deal with. Thanks for your response. |
|
|
Ok, take a look at my attached project. It is our Hierarchy Demo slightly modified to use ICommands. When I delete a node from the diagram model, then add one back in, visually, everything looks right. I can expand the newly added node and collapse it. But when I right click on the newly added node and hit "delete", the parameter passed in is null. I came across this in my main application and noticed after deleting a node from my complex model and adding one back in, visually, everything worked. But when I would right click and try to delete that newly added node, that node was not found in the "Diagram.Nodes" collection. Yet is worked properly in the visual model. Steps to reproduce: Launch app right-click the A1.2 node, Delete it. right-click the A1 node, Insert New. A "B" node is added notice the diagram works fine, collapsing/expanding now right-click the new "B" node. The parameter passed into the Execute method of the ICommand is null. I am not sure if this is a commanding issue or a DiagramModel issue. |
|
|
Hello I have no idea why this works when the program starts up, but then fails when adding a new node. It may have something to do with using a plain binding within a ContextMenu. I've found that you can work around this by using this binding for the CommandParameter:
Jason Fauchelle |
|
|
That seems to partially fix the problem. The node is now passed to the command parameter, but the diagram won't delete the node visually. It seems to leave it attached to the diagram but in a disconnected way. Something is wacky. Is it just my system doing this? It's almost like Diagram.Nodes.Add and/or Diagram.Nodes.Remove is doing something strange. Same steps to reproduce as before: Delete A1.2 Right click A1, Insert New Node Right click B, delete. It removes the connection to A1, but leaves the B node visible. Add a couple of nodes to A1 and delete them. These seem to work fine, but that first one stays put. I added a timestamp to my Node Data (concatenated "B" with DateTime.Now.Milliseconds.ToString() so I could tell them apart.) Let me know if you are able to reproduce this or not. |
|
|
Thanks for pointing this out. This bug has been resolved and will be available in the next nightly build. |
|