Swimlanes via groups? - Drag Drop + Sizing

I would like to know how to create drag and drop behaviour between groups inside my diagram. The default behaviour is that the group gets resized when node gets moved.

Also can I fix the size of the group to stretch horizontally so it would look like a swimlane?

Any build in support for swimlanes? using silverlight 1.1 alpha version?

Thank you,

Pavel

Take a look at the Planogram sample. Basically, set the attached property: go:Part.DropOntoBehavior=“AddsToGroup”.

And to avoid having groups always precisely surround its member nodes, don’t use a GroupPanel in your group’s DataTemplate.

Although the Planogram sample allows users to resize groups interactively, you could set the height and width of your group’s main element as you wish.

We still have to clean up the Planogram sample before we go to beta, so pardon any of the extraneous stuff that might be confusing or distracting.

Thank you, that was very helpful.

Pavel

Where do you get the “Planogram sample”

Oh, sorry about that. It’s in the alpha kit for version 1.1.

If you don’t mind waiting a day (approximately), we’ll make beta kits available to everyone.

But note that go:Part.DropOntoBehavior is new for version 1.1, so just looking at the XAML won’t help you if you are using version 1.0.

Looking forward to the beta :)

How would you go about implementing some behavior a GroupPanel provides?

Want :
resize behavior the GroupPanel provides when a drag and drop occurs.

Dont Want:
resize behavior when trying to take a node in a group and dropping it into another group.

How is the system supposed to recognize the differences in those two drag-and-drop scenarios that you describe?

Perhaps my question was nebulous.

Using a GroupPanel to organize data in Subgroup 1 Data template offers the advantage of automatic resizing. Suppose I drag and dropped Node 6 into Subgroup 1, upon doing so, the GroupPanel would resize.

Now suppose I try to drag and drop any node from Subgroup 1 to Subgroup 2. As I move the Node from subgroup 1, to subgroup 2, the GroupPanel will resize itself to fit around the set of nodes it contains.

I know this is the expected behavior of GroupPanel per the documentation : “An auto-resizing SpotPanel that
always surrounds its Group’s Group.MemberNodes
plus some Padding.”

My question is, how would I go about mimicking the resize behavior a GroupPanel offers, but only allow subgroup resizing after a drag and drop event? Would I add code that uses the SelectionMovedEvent? SelectionGroupedEvent? Somehow handle it in my DataTemplate? A custom Diagram Layout??

±---------+
| Node 6 |
±---------+

  • Subgroup 1 Data Template
    ±------------------+
    | Node 1 |
    ±------------------+
    | Node 2 |
    ±------------------+
    | Subgroup 2 |
    | ±-----------+ |
    | | Node 3 | |
    | ±-----------+ |
    | | Node 4 | |
    | ±-----------+ |
    | | … | |
    | ±-----------+ |
    ±------------------+
    | Node 5 |
    ±------------------+

  • Subgroup 2 Data Template
    ±------------------+
    | |
    ±------------------+

Now I see that you’re expecting a subtly different behavior than what I thought you were asking for. (Maybe; it’s still very hard to describe behaviors in text!)

Say the DataTemplate for your GroupTemplate used a Rectangle instead of a GroupPanel inside a Border. You could implement a Diagram.SelectionMoved event handler that would make sure that for each group that contained a moved node would have the Width and Height and Location of that Rectangle be adjusted to surround all of its members, just as a GroupPanel would do.

[code] <go:BooleanBrushConverter x:Key=“theBrushChooser” TrueBrush=“Red” FalseBrush=“White” />

<DataTemplate x:Key="GroupTemplate2">
  <StackPanel go:Node.LocationElementName="grid"
              go:Part.SelectionElementName="grid"
              go:Part.DropOntoBehavior="AddsToGroup">
    <TextBlock Text="{Binding Path=Data.Key}" FontWeight="Bold" HorizontalAlignment="Left" />
    <Rectangle x:Name="grid" Stroke="Gray" StrokeThickness="3"
               Fill="{Binding Path=Node.IsDropOntoAccepted, Converter={StaticResource theBrushChooser}}"
               MinWidth="100" MinHeight="100" />
  </StackPanel>
</DataTemplate>[/code]

<go:Diagram x:Name="myDiagram" . . . GroupTemplate="{StaticResource GroupTemplate2}" SelectionMoved="myDiagram_SelectionMoved" />

private void myDiagram_SelectionMoved(object sender, DiagramEventArgs e) { foreach (Part part in myDiagram.SelectedParts) { Group sg = part.ContainingSubGraph; if (sg != null) { FrameworkElement shape = sg.FindNamedDescendant("grid"); if (shape != null) { Rect b = myDiagram.Panel.ComputeBounds(sg.MemberNodes.Cast<Part>()); shape.Width = b.Width+20; shape.Height = b.Height+20; sg.Location = new Point(b.X-10, b.Y-10); } } } }
The disadvantage of this code is that if you programmatically move a Node, there’s no GroupPanel to automatically change the Size and Location of the Group. You would need to explicitly call something like that event handler code.

Hi Walter, thanks for that! Your example is pretty close to what I was after, and defiantly gives me a starting point :)

Suppose I am using a layout manager and want it to refresh because I programmatically modified the size.

        private void myDiagram_SelectionMoved(object sender, DiagramEventArgs e)
        {
            foreach (Part part in myDiagram.SelectedParts)
            {
                Group sg = part.ContainingSubGraph;
                if (sg != null)
                {
                    FrameworkElement shape = sg.FindNamedDescendant("grid");
                    if (shape != null)
                    {
                        <b>myDiagram.LayoutManager.InvalidateLayout((Part)(part.ContainingSubGraph), LayoutChange.All);</b>
                        Rect b = myDiagram.Panel.ComputeBounds(sg.MemberNodes.Cast<Part>());
                        shape.Width = b.Width + 20;
                        shape.Height = b.Height + 20;
                        sg.Location = new Point(b.X - 10, b.Y - 10);
                    }
                }
            }
        }

Is this the best way to do so?? Also, I doubt LayoutChange.All is the most efficient way, but it seemed to work :)

There’s always a Diagram.LayoutManager. By default there’s both a Diagram.Layout and a Group.Layout, which just try to assign some Node.Location to each Node that doesn’t have a Location.

Are you trying to force each Group to layout its members? It would be easier to just do:
sg.Layout.DoLayout(sg.MemberNodes, sg.MemberLinks);

(By the way, part.ContainingSubGraph is already a Part, so you don’t have to cast it, and it’s already in the “sg” variable.)

Here it is, somewhat more efficiently:

private void myDiagram_SelectionMoved(object sender, DiagramEventArgs e) { var groups = myDiagram.SelectedParts.Select(p => p.ContainingSubGraph).Where(g => g != null).Distinct().ToList(); foreach (Group sg in groups) { FrameworkElement shape = sg.FindNamedDescendant("grid"); if (shape != null) { //sg.Layout.DoLayout(sg.MemberNodes, sg.MemberLinks); Rect b = myDiagram.Panel.ComputeBounds(sg.MemberNodes.Cast<Part>()); if (b == Rect.Empty) continue; shape.Width = b.Width+20; shape.Height = b.Height+20; sg.Location = new Point(b.X-10, b.Y-10); } } }

To get the behavior I think you want, I would implement a LayoutCompleted event handler:

private void myDiagram_LayoutCompleted(object sender, DiagramEventArgs e) { foreach (Group sg in myDiagram.Nodes.OfType<Group>()) { FrameworkElement shape = sg.FindNamedDescendant("grid"); if (shape != null) { Rect b = myDiagram.Panel.ComputeBounds(sg.MemberNodes.Cast<Part>()); if (b == Rect.Empty) continue; shape.Width = b.Width+20; shape.Height = b.Height+20; sg.Location = new Point(b.X-10, b.Y-10); } } }
That way each time the user adds or removes a node from a group, a layout happens, and then afterwards this code will adjust the Bounds of the shape to surround the member nodes.

Hi,

This is close to the behavior that I am looking for. I just have a couple questions about the ordering of events.

Suppose I modified your example :

  • GroupTemplate2 contains a GridLayout for “grid”
  • GoDiagram “myDiagram” contains a TreeLayout

Ideally, I think the flow of events I am after on a drag and drop are as follows:

Case 1 - Drag node X from Group A to Group B

  • Do “Group A” GridLayout

  • Resize “Group A” “grid” to contain new Node X

  • Do “Group B” GridLayout

  • Resize “Group B” “grid” to reflect removal of Node X

  • Do myDiagram TreeLayout to update positioning based on “Group A” and “Group B” size changes

Case 2 - Drag node X from Group A to Group A (suppose a user wanted to drag and drop Node X, but then decided not to)

  • Do “Group A” GridLayout
  • Resize “Group A” (size will not change in this case)
  • Do myDiagram TreeLayout (layout will not change in this case)

Your example works well, except I am unclear the best way/place to force a myDiagram TreeLayout to occur.

Also, is it not advisable to use diagram layouts in conjunction with code that resizes/positions the location element within groups? Am I in effect, implementing my own layout control?

I apologize for the barrage of seemingly simple questions, I am still trying to come up to speed with GoXam :)

So your Diagram.Layout is a TreeLayout. You can specify the conditions for which it will perform another layout by setting the Conditions property (the ConditionFlags attribute in Silverlight XAML).

So if you want there to be another tree layout when one of the nodes (that is a Group) changes size, just add “GroupSizeChanged” to the list of Conditions:

<go:Diagram ...> <go:Diagram.Layout> <golayout:TreeLayout ConditionFlags="Standard GroupSizeChanged" ... /> </go:Diagram.Layout> </go:Diagram>
The “Standard” conditions includes adding or removing a node or a link, but not a node size change.

For your case 2, it seems there are several issues.
First, if they really changed their mind about moving a node, they could cancel the drag by hitting ESCAPE.
Second, perhaps they just want to rearrange the nodes a bit, without reparenting to a different group. If so you don’t have to perform a layout.
Third, if no manual rearrangement is permitted within a group, you could add “NodeLocationChanged” to the ConditionFlags of the GridLayout for your Group.

Hi Walter,

I am still having subtle issues with the tree layout not always redrawing on a size change. Is it possible to email you my sample project with instructions on how to reproduce?

Sure. GoXam at our domain.

Thanks, email sent!