Using TreeModelNodeData in GraphLinksModel

Hi,



I suspect I already know the answer to this one but I’d like to here your thoughts on it. It would be really handy if we could support sub-graphs and sub-trees in the same diagram but as far as I can tell, they both require different diagram models.



We have three types of node in our diagram (you may remember from earlier posts of mine ). A Group node containing instances of my main node (the one with the list of other nodes contained within) and then our ‘satellite’ nodes of this main node. We’d like to be able to collapse and expand these satellite nodes. The most obvious way would be to have them as a sub-tree but we’re already using sub-groups.



I guess it’s not too much work to write this functionality myself but if it’s already done for me, of course that would be even easier.



Neil.

The tree-expand-collapse functionality is defined on the Node class, so it works with any model.

You can even set GraphLinksModel.ValidCycle to limit users to drawing links that only create a tree structure.

If you are talking about tree-specific properties that TreeModelNodeData has, they should be trivial to implement in your own node data class. I can post the code here in a little while.

There is no ParentKey property on GraphLinksModelNodeData though so how do I set the parent object?

To define any link relationship in GraphLinksModel you have to create a link data object.

If you were using GraphModel, you could set the FromKeys property. Of course if you want to maintain a tree-structure, you would have at most one key in that FromKeys collection.

Is there a reason you need to use GraphLinksModel instead of GraphModel?

Yes, we are using GraphLinksModel as we need to use specific ports on the node.



Let me just clarify this, by using the LinkSource collection, any node that has a link to another node is child of that node?

Actually, it’s the other way around – as far as “tree-children” and “tree-collapse/expand” is concerned, all of the Node.NodesOutOf are the “children”.

You can see this in many of the samples.

Oooh, that’s going to hurt then. Our architecture means that I’d like to collapse/expand all the nodes which link into a central node. Looks like I may have to implement my own solution then.

Well, you can always just put the arrowheads on the “from” end of each link.

Surely that would involve me changing the routing of my whole model (changing FromSpots to ToSpots etc)? That would be quite messy.



In your implementation of the tree collapse/expand are you just setting the visibility property of the children and their links?

I think it should only involve changes to your XAML.

This is a slightly simplified definition of Node.CollapseTree:

[code] public void CollapseTree() {
const String Action = “Collapse Tree”;
Diagram diagram = this.Diagram;
if (diagram == null) return;
// avoid multiple simultaneous recursions
if (diagram.IsCollapsingExpanding) return;
diagram.IsCollapsingExpanding = true;
diagram.StartTransaction(Action);
CollapseTree1();
diagram.CommitTransaction(Action);
diagram.IsCollapsingExpanding = false;
}

private void CollapseTree1() {
  FrameworkElement ve = this.VisualElement;
  if (ve == null) return;

  // change the visibility of the children
  bool oldexp = Node.GetIsTreeExpanded(ve);
  Node.SetIsTreeExpanded(ve, false);  // also sets IsExpandedTree carefully

  foreach (Node n in this.NodesOutOf) {
    if (n != this) {
      FrameworkElement nve = n.VisualElement;
      if (nve != null) {
        if (Node.GetIsTreeExpanded(nve)) {
          Node.SetWasTreeExpanded(nve, true);
          n.CollapseTree1();
        } else if (!oldexp && n.Visible) {
          Node.SetWasTreeExpanded(nve, true);
          n.CollapseTree1();
          n.Visible = false;
        } else {
          Node.SetWasTreeExpanded(nve, false);
        }
      }
    }
  }
}[/code]

And Node.ExpandTree:

[code] public void ExpandTree() {
const String Action = “Expand Tree”;
Diagram diagram = this.Diagram;
if (diagram == null) return;
// avoid multiple simultaneous recursions
if (diagram.IsCollapsingExpanding) return;
diagram.IsCollapsingExpanding = true;
diagram.StartTransaction(Action);
ExpandTree1();
diagram.CommitTransaction(Action);
diagram.IsCollapsingExpanding = false;
}

private void ExpandTree1() {
  FrameworkElement ve = this.VisualElement;
  if (ve == null) return;

  // change the visibility of the children
  bool oldexp = Node.GetIsTreeExpanded(ve);
  Node.SetIsTreeExpanded(ve, true);  // also sets IsExpandedTree carefully

  Point loc = this.Location;
  foreach (Node n in this.NodesOutOf) {
    if (n != this) {
      FrameworkElement nve = n.VisualElement;
      Point l = n.Location;
      if (Double.IsNaN(l.X) || Double.IsNaN(l.Y)) n.Location = loc;
      if (nve != null) {
        if (!Node.GetIsTreeExpanded(nve) && Node.GetWasTreeExpanded(nve)) {
          n.ExpandTree1();
        } else if (oldexp && !n.Visible) {
          n.ExpandTree1();
          n.Visible = true;
        }
      }
    }
  }
}[/code]

The Diagram.IsCollapsingExpanding property is internal – you’ll need to define your own similar shared property.

Sorry Walter, can you please explain further how I could just change the XAML. Does the NodesOutOf collection work on where the arrow heads are or the Link.ToData?

If you just want to have the arrowheads on the “from” end instead of the “to” end, I think you just want to change go:LinkPanel.Index=“0” and (probably) go:LinkPanel.Alignment=“Opposite”.

I don’t think you would need to change any Spots. Or anything else, really. It’s just to change the appearance to the user.

I gave you the CollpseTree/ExpandTree code in case you really do want to change how it works. But I don’t think you’ll need it.

Yep, I think I follow. However, correct me if I’m wrong. We currently have our ‘child’ nodes flowing into our ‘parent’ node. What you are suggesting is to change the flow direction but put the arrows on the opposite ends to make the user think that the flow direction is the same as it is now. By changing the flow direction I would have to change all my logic and XAML regarding to and from spots, not a task I particularly fancy.

Oh, I see what you mean. There are a lot of possibilities, aren’t there? But you’re probably right.

So I would just adapt the code I posted above, but use NodesInto instead of NodesOutOf.

Right, I’m slowly getting somewhere with this now. I’ve implemented a workable solution that does what I need. The problem I have now is that if I move my parent node, the children remain where they were (remember my parent child relationship is the opposite of yours). I’m thinking the simplest solution would be to force a redraw of the layout of the child nodes when the parent is expanded. I hope that there’s a simple way to do this.

Have you set DraggingTool.Inclusions=“SubTree”?

We’re implementing a new property: Diagram.TreePath. That will control all tree operations whether links go from the parent to the children (the default) or from each child to its parent (if == “Source”). So you won’t have to reimplement Node.CollapseTree and ExpandTree, nor DraggingTool.ComputeEffectiveCollection.

I can’t use DraggingingTool.Inclusions=“SubTree” as my parent child relationship is non-standard. Is there a way to force a refresh of some defined nodes when I expand my parent node?



It’s good news that you’re developing a tree implementation, it will probably be too late for us though as I’m developing our own strategy.

You can call Diagram.LayoutDiagram(). That will force everything to be laid out again.

That’s the ticket, thank you.

In version 1.2.1 (beta 2) we have added a Diagram.TreePath property. The default value is “Destination”, where links go from a parent node to its children.

If the value is “Source”, links go from a child node to its parent node.

So now the Node.ExpandTree method and the way the DraggingTool collects a subtree (when DraggingTool.Inclusions=“SubTree”) is controlled by this new property on Diagram.