Class Layout

GoDiagram®
v10.0.11
by Northwoods Software®

This is the base class for all of the predefined diagram layout implementations. They only arrange Parts (primarily Nodes and Links) in a Diagram, not to GraphObjects in Panels (i.e. panel layout).

Namespace: Northwoods.Go.Layouts
Assembly: Northwoods.GoDiagram.Avalonia.dll
Syntax
public class Layout
Remarks

The layout classes include , , , , and GridLayout. This base class is not abstract -- in fact an instance of this base class is the default value for Layout and for Layout.

An instance of a Layout class will be the value of Layout. That layout positions the graph of top-level nodes and links. Nodes and links that belong to a Group are laid out by that group's Layout. The Diagram will automatically perform all nested group layouts before laying out the whole diagram.

If you have position information for all of the nodes when you load a model, you will typically have data bound the Location to some property on your node data. In order to avoid an initial layout causing those saved node positions to be discarded, you can either not set the Layout to a predefined layout or you can set IsInitial to false.

Because performing layouts can be expensive in space and time, automatic layouts are performed only on "invalid" layouts, and only well after a layout has been invalidated. This state is held by the IsValidLayout property. Many standard operations, such as adding or removing nodes or links, will cause the layout that is responsible for positioning those nodes or routing those links to be invalidated. Such invalidation is performed by calling InvalidateLayout(), which not only clears the IsValidLayout state but also requests that the diagram do an automatic layout soon. You can avoid such invalidations by setting IsOngoing to false.

Layouts will ignore parts that have IsLayoutPositioned set to false or parts that are not Visible. Layouts will also ignore parts that are in layers that are IsTemporary.

Various operations on Parts will cause the responsible Layout to be invalidated. This includes adding or removing parts, changing their visibility, and changing their size. You can disable such automatic layout invalidations by setting LayoutConditions to the combination of Part flags named "Layout..." that you want.

But operations on parts are not the only way in which layouts become invalidated. Setting most properties on the layouts, thereby changing their behavior, will invalidate that layout. Replacing the Layout or Layout will automatically invalidate the new layout. If IsViewportSized is true, when a diagram's ViewportBounds changes size, the Layout is invalidated. (This is normally only true for GridLayouts when its WrappingWidth is NaN. Most layouts do not care about the size of the viewport.)

You can also explicitly call LayoutDiagram(bool), which can invalidate all layouts and then perform them all. But we recommend that you avoid doing so, to allow the normal updating process perform layouts as needed.

If an automatic layout is the first time that a layout has been performed for the model, the diagram first raises the DiagramEvent named "InitialLayoutCompleted". Whenever a Diagram finishes an automatic layout, it raises the DiagramEvent named "LayoutCompleted".

It is also possible to call DoLayout(IEnumerable<Part>) explicitly, but this is uncommon and only used with instances of Layout that are not the Layout or Layout. It should only be needed when you want to layout a collection of nodes and links that is not the normal graph of top-level parts of a Diagram or a subgraph of a Group.

An instance of this base class provides a rudimentary default layout that will position all of the parts that have no position (i.e. the Location is (NaN,NaN)). Parts that already have a position are ignored. This primitive layout class does not make use of a Network<V, E, Y> because it ignores all links.

To implement your own custom layouts, you can inherit from either this class, NetworkLayout<N, V, E, Y>, or from one of the other predefined layout classes. If you inherit from this base class, you will want to override the DoLayout(IEnumerable<Part>) methods. You can call the Move(Point, bool) method to re-position a part, including whole groups. Please read the Introduction page on Extensions for how to override methods and how to call a base method.

Constructors

Layout()

Create a minimal layout that only positions Nodes that do not have a location.

Declaration
public Layout()

Properties

ArrangementOrigin

Gets or sets the top-left point for where the graph should be positioned when laid out.

Declaration
public Point ArrangementOrigin { get; set; }
Property Value
Type Description
Point
Remarks

The default value for this property is the Point(0, 0). Setting this property to a new value invalidates this layout. This property is likely to be set by many Layouts that belong to a Group when the layout is performed.

BoundsComputation

Gets or sets a function that determines the initial size and position in document coordinates of a LayoutVertex corresponding to a Node.

Declaration
public Func<Part, Layout, Rect> BoundsComputation { get; set; }
Property Value
Type Description
Func<Part, Layout, Rect>
Remarks

This function is called by GetLayoutBounds(Part). The default value for this property is null, in which case the ActualBounds of the Node is used. Setting this property to a new value invalidates this layout.

The non-null value must be a function that takes 2 arguments. The first argument will be the Part whose bounds the Layout should use. The second argument will be this Layout. The return value must be in document coordinates. You may find it convenient to call GetDocumentBounds() to get the bounds in document coordinates of an object within the node.

Diagram

Gets the Diagram that owns this layout, if it is the value of Layout.

Declaration
public Diagram Diagram { get; set; }
Property Value
Type Description
Diagram
Remarks

If this property and Group are non-null, the Group should be in this Diagram.

See Also

Group

Gets the Group that uses this layout, if it is the value of a group's Layout.

Declaration
public Group Group { get; set; }
Property Value
Type Description
Group
Remarks

If this property is set to a Group, Diagram is automatically set to be the Group's Diagram.

See Also

IsInitial

Gets or sets whether this layout is performed on an initial layout.

Declaration
public bool IsInitial { get; set; }
Property Value
Type Description
bool
Remarks

The default value is true. Setting this property to false causes IsValidLayout to be set to true so that the diagram does not perform this layout.

If you set both IsInitial and IsOngoing to false, there will be no automatic layout invalidation, because InvalidateLayout() will not set IsValidLayout to false. To get your nodes to appear, you will need to explicitly set or data-bind their Location or Position to real Point values, because automatic layout will not assign any positions.

Another way of controlling when layouts are invalidated is by setting IsLayoutPositioned or LayoutConditions.

IsOngoing

Gets or sets whether this layout can be invalidated by InvalidateLayout().

Declaration
public bool IsOngoing { get; set; }
Property Value
Type Description
bool
Remarks

Set this to false to prevent actions such as adding or removing Parts from invalidating this layout. The default value is true. Setting this property does not invalidate this layout.

If you set both IsInitial and IsOngoing to false, there will be no automatic layout invalidation, because InvalidateLayout() will not set IsValidLayout to false. To get your nodes to appear, you will need to explicitly set or data-bind their Location or Position to real Point values, because automatic layout will not assign any positions.

Another way of controlling when layouts are invalidated is by setting IsLayoutPositioned or LayoutConditions.

IsRealtime

Gets or sets whether this layout be performed in real-time, before the end of a transaction.

Declaration
public bool? IsRealtime { get; set; }
Property Value
Type Description
bool?
Remarks

All layouts that are invalidated will be performed at the end of a transaction. The default value is null. A null value is treated as true for a Layout but false for a Layout. Setting this property does not invalidate this layout.

IsRouting

Gets or sets whether this layout routes Links.

Declaration
public bool IsRouting { get; set; }
Property Value
Type Description
bool
Remarks

The default value is true. When false, this layout will not explicitly set the Points, and the default routing of each individual Link will take place after the Nodes are moved by CommitLayout(). Setting this property does not invalidate this layout.

Some layouts ignore links, in which case this property is ignored.

IsValidLayout

Gets or sets whether this layout needs to be performed again (if false).

Declaration
public bool IsValidLayout { get; set; }
Property Value
Type Description
bool
Remarks

Instead of setting this property directly, it is normal to set it to false by calling InvalidateLayout(), since that also requests performing a layout in the near future.

IsViewportSized

Gets or sets whether this layout depends on the ViewportBounds's size.

Declaration
public bool IsViewportSized { get; set; }
Property Value
Type Description
bool
Remarks

If set to true, the layout will invalidate when the Diagram's viewport changes size. This only applies to diagram layouts, not to group layouts, and only when AutoScale is set to None.

The default value is false. Setting this property to true will invalidate this layout.

It is possible that a viewport-sized layout will trigger the Diagram to require scrollbars, which modifies the ViewportBounds, which will in turn trigger another layout. This is uncommon, but possible with GridLayout if the results require a vertical scrollbar, and that vertical scrollbar shrinks the viewport width enough that a grid column can no longer fit. When designing custom layouts, one should be careful that this behavior does not result in an infinite loop.

Methods

CloneProtected(Layout)

Copies properties from this object to the given object, which is of the same class.

Declaration
protected virtual void CloneProtected(Layout c)
Parameters
Type Name Description
Layout c
Remarks

This is called by Copy() and should be overridden for each class that adds properties. There are examples of such overrides in the samples. Please read the Introduction page on Extensions for how to override methods and how to call this base method.

CollectParts(Diagram)

A convenient way of converting the argument to DoLayout to an actual collection of eligible Parts.

Declaration
protected HashSet<Part> CollectParts(Diagram diagram)
Parameters
Type Name Description
Diagram diagram
Returns
Type Description
HashSet<Part>
Remarks

The resulting set will not include any Nodes or Links for which CanLayout() is false. If the argument includes a Group for which Layout is null, the resulting set will include the member parts of that group rather than that group itself. You will not need to call CollectParts if you call MakeNetwork(IEnumerable<Part>), because that method does effectively the same thing when building the Network<V, E, Y>.

Typical usage:

public override void DoLayout(IEnumerable<Part> coll = null) {
  HashSet<Part> allparts = null;
  if (coll != null) {
    allparts = CollectParts(coll);
  } else if (Group != null) {
    allparts = CollectParts(Group);
  } else if (Diagram != null) {
    allparts = CollectParts(Diagram);
  } else {
    return; // Nothing to layout!
  }
  foreach (var node in allparts) {
    if (node is Node) {
      // . . . position the node . . .
    }
  }
}

CollectParts(Group)

A convenient way of converting the argument to DoLayout to an actual collection of eligible Parts.

Declaration
protected HashSet<Part> CollectParts(Group group)
Parameters
Type Name Description
Group group
Returns
Type Description
HashSet<Part>
Remarks

The resulting set will not include any Nodes or Links for which CanLayout() is false. If the argument includes a Group for which Layout is null, the resulting set will include the member parts of that group rather than that group itself. You will not need to call CollectParts if you call MakeNetwork(IEnumerable<Part>), because that method does effectively the same thing when building the Network<V, E, Y>.

Typical usage:

public override void DoLayout(IEnumerable<Part> coll = null) {
  HashSet<Part> allparts = null;
  if (coll != null) {
    allparts = CollectParts(coll);
  } else if (Group != null) {
    allparts = CollectParts(Group);
  } else if (Diagram != null) {
    allparts = CollectParts(Diagram);
  } else {
    return; // Nothing to layout!
  }
  foreach (var node in allparts) {
    if (node is Node) {
      // . . . position the node . . .
    }
  }
}

CollectParts(IEnumerable<Part>)

A convenient way of converting the argument to DoLayout to an actual collection of eligible Parts.

Declaration
protected HashSet<Part> CollectParts(IEnumerable<Part> coll)
Parameters
Type Name Description
IEnumerable<Part> coll
Returns
Type Description
HashSet<Part>
Remarks

The resulting set will not include any Nodes or Links for which CanLayout() is false. If the argument includes a Group for which Layout is null, the resulting set will include the member parts of that group rather than that group itself. You will not need to call CollectParts if you call MakeNetwork(IEnumerable<Part>), because that method does effectively the same thing when building the Network<V, E, Y>.

Typical usage:

public override void DoLayout(IEnumerable<Part> coll = null) {
  HashSet<Part> allparts = null;
  if (coll != null) {
    allparts = CollectParts(coll);
  } else if (Group != null) {
    allparts = CollectParts(Group);
  } else if (Diagram != null) {
    allparts = CollectParts(Diagram);
  } else {
    return; // Nothing to layout!
  }
  foreach (var node in allparts) {
    if (node is Node) {
      // . . . position the node . . .
    }
  }
}

Copy()

Creates a copy of this Layout and returns it.

Declaration
public Layout Copy()
Returns
Type Description
Layout
Remarks

When a Group is copied that has a Layout, the Layout must also be copied. This calls CloneProtected(Layout) on a newly constructed Layout.

DoLayout(IEnumerable<Part>)

Position all of the nodes that do not have an assigned Location in the manner of a simple rectangular array.

Declaration
public virtual void DoLayout(IEnumerable<Part> coll = null)
Parameters
Type Name Description
IEnumerable<Part> coll
Remarks

The default implementation ignores all Groups and Links; many subclasses of Layout ignore all instances of Parts that are not Nodes or Links.

You can override this method to do whatever node positioning and link routing that you wish.

When the layout extends NetworkLayout<N, V, E, Y>, this method should call MakeNetwork(IEnumerable<Part>) and UpdateParts(). The UpdateParts() method will call CommitLayout() within a transaction. The outline of such an override should be like:

public override void DoLayout(IEnumerable<Part> coll = null) {
  if (Network == null) Network = MakeNetwork(coll);
  // assign Bounds to all vertexes in the network:
  foreach (var v in Network.Vertexes) {
    v.CenterX = ...
    v.CenterY = ...
  }
  UpdateParts();
  Network = null;
}

Override CommitLayout() to actually position nodes and route links.

When the layout does not extend NetworkLayout<N, V, E, Y>, this method should make direct changes to Nodes and Links within a transaction, and it should call CollectParts(IEnumerable<Part>) on the argument in order to get the proper set of Nodes and Links to work on. Examples are provided in the Extensions directory.

Please read the Introduction page on Extensions for how to override methods and how to call this base method.

GetLayoutBounds(Part)

This method is called by layouts to determine the size and initial position of the nodes that it is laying out.

Declaration
public Rect GetLayoutBounds(Part part)
Parameters
Type Name Description
Part part

the Part being laid out

Returns
Type Description
Rect

a Rect in document coordinates

Remarks

Normally this just returns the part's ActualBounds plus its Margin. However, if BoundsComputation has been set to a function, that function will be called in order to return the bounds of the given Part in document coordinates that the layout should pretend it has.

InitialOrigin(Point)

Compute the desired value of ArrangementOrigin if this Layout is being performed for a Group.

Declaration
protected virtual Point InitialOrigin(Point origin)
Parameters
Type Name Description
Point origin
Returns
Type Description
Point
Remarks

This is typically called near the beginning of the implementation of DoLayout(IEnumerable<Part>): this.ArrangementOrigin = this.InitialOrigin(this.ArrangementOrigin); if the layout wants to respect the pre-layout location of the Group when deciding where to position its member nodes.

InvalidateLayout()

If IsOngoing is true and if an initial layout has not yet been performed, set the IsValidLayout property to false, and ask to perform another layout in the near future.

Declaration
public void InvalidateLayout()
Remarks

If IsInitial is true, this layout is invalidated only when the Model is replaced, not under the normal circumstances such as when parts are added or removed or due to other calls to InvalidateLayout().

If you set both IsInitial and IsOngoing to false, there will be no automatic layout invalidation, because this method will not set IsValidLayout to false. However you can still set IsValidLayout explicitly.

This is typically called when a layout property value has changed, or when a Part is added or removed or changes visibility, if LayoutConditions includes the pertinent flags.