Visual Representation of Graph Elements

Model items used in an IGraph (i.e., items of type INode, IEdge, IPort, and ILabel) by themselves do not provide any visual representation. Instead, the functionality to render model items in the GraphControl is encapsulated by so-called "styles." Along with their complementing counterparts, the so-called "style renderers," they hold all necessary state and provide all logic needed to create the visual appearance of graph elements.

The following sections cover:

Additionally, in the Port Support and Label Support sections, the so-called port location models and label models are presented. Although they do not directly influence the styling of the graph, they are used for determining the geometry of ports and labels and thus influence the overall visual appearance.

Styles

For either type of graph element there is a specialized style type available: interfaces INodeStyle and IEdgeStyle for nodes and edges, and interfaces IPortStyle and ILabelStyle for ports and labels, respectively. Figure 4.6, “Hierarchy of style types” shows the type hierarchy of the base style interfaces.

Figure 4.6. Hierarchy of style types

Hierarchy of style types.

yFiles for Silverlight Viewer leverages the native Silverlight styling mechanism by providing specialized ContentControl-based style types for the visual representation of graph elements. INodeControlNodeStyle, ILabelControlLabelStyle, and IPortControlPortStyle are the corresponding style type interfaces for nodes, labels, edges, and ports, respectively. Figure 4.7, “Hierarchy of style types that support Silverlight styling of ContentControls” shows the type hierarchy of ContentControl-based styles.

Figure 4.7. Hierarchy of style types that support Silverlight styling of ContentControls

Hierarchy of style types that support using ContentControl objects.

Generic interface IVisualStyle is the base type for all styles. Its type parameter is constrained to IModelItem, which is the base type for all graph structure interfaces used by interface IGraph (see also the section called “Graph Structure”). The style types that directly extend IVisualStyle<TModelItem> appropriately bind the type parameter TModelItem. For example, INodeStyle uses INode, IEdgeStyle uses IEdge, etc. to specify the style's specific kind of IModelItem.

The ContentControl style types extend generic interface ITaggedStyleBase<TModelItem>, a sub-interface of IVisualStyle, and appropriately bind its type parameter TModelItem in a similar manner. ContentControl style type interfaces also inherit from the respective interface for their kind of model item, i.e., an INodeControlNodeStyle is also a true INodeStyle, for example. (See also in the sections below.)

Abstract generic class ControlStyleBase<TModelItem> is the base implementation for all actual ContentControl style type classes. Each ContentControl-based style is accompanied by a specialized ContentControl subclass which can be styled using Silverlight styling techniques. A style instance basically determines which Silverlight style to apply to the ContentControl. The Silverlight style can conveniently be created and designed in any Silverlight-enabled XAML designer application.

The following sections cover the predefined style implementations for the model item types:

Using styles along with class DefaultGraph, the default IGraph implementation, is described in the section called “Working with Styles”.

Node Styles

Node styles are responsible for the graphical rendering of nodes in the canvas. Interface INodeStyle is the common base type for actual implementations. Figure 4.8, “Hierarchy of node style types” shows part of the type hierarchy of the node style implementations.

Figure 4.8. Hierarchy of node style types

Hierarchy of node style types.

Table 4.3, “Predefined node style implementations” lists the predefined node style implementations present in the yWorks.yFiles.UI.Drawing namespace.

Table 4.3. Predefined node style implementations

Type Name Description
NodeControlNodeStyle Enables using Silverlight-styled ContentControls for the visual representation of a node. (See below for a description on using NodeControlNodeStyle.)
SimpleAbstractNodeStyle<TVisual> Greatly simplifies node style customization using subclassing. (See below for a description of SimpleAbstractNodeStyle.)
ShapeNodeStyle Provides a variety of predefined shapes for the visual appearance of a node. The enumeration type ShapeNodeShape defines enumerators for the available shapes.
ImageNodeStyle Enables using an image to create the visual representation for a node.
MemoryImageNodeStyle Enables using a bitmap image to create the visual representation of a node. The serialization mechanism of this style uses Base64-encoded bitmap data when writing the image.
GeneralPathNodeStyle Allows arbitrary GeneralPath objects to create the visual representation for a node.
PanelNodeStyle An implementation of interface IPanelNodeStyle that can be used to create the visual representation for group nodes, for example. By default, this style renders a drop shadow by itself.
BevelNodeStyle An implementation of interface IBevelNodeStyle that can be used to create rectangular nodes with rounded corners, a bevel border, and a shining background. Optionally, this style renders a drop shadow by itself.
ShinyPlateNodeStyle Can be used to create rectangular nodes with rounded corners and a 'shiny plate' interior. By default, this style renders a drop shadow by itself.
ShadowNodeStyleDecorator A node style decorator to add a drop shadow to the visual representation of the wrapped node style. See below.

Figure 4.9, “Node style representations” shows examples of nodes using predefined node style implementations.

Figure 4.9. Node style representations

ShapeNodeStyle: Rectangle
ShapeNodeStyle: RoundRectangle
ShapeNodeStyle: Ellipse
ImageNodeStyle
Some of the predefined shapes of ShapeNodeStyle ImageNodeStyle showing the yWorks logo
PanelNodeStyle
BevelNodeStyle
ShinyPlateNodeStyle
Node Representation Using ContentControls

Node style type NodeControlNodeStyle allows to use the ContentControl-based Silverlight styling mechanism to create the visual representation of nodes in an IGraph. This enables using the XAML control templating scheme to define the appearance of nodes, including the support of triggers, visual states, and transitions.

The control style and its control template is applied to class NodeControl, a specialized ContentControl subclass. Example 4.12, “XAML ControlTemplate definition excerpt for the appearance of nodes” shows part of a control template definition for nodes taken from the ControlStylesDemo demo application. Note how the target type of the template is specified to be of type NodeControl.

Example 4.12. XAML ControlTemplate definition excerpt for the appearance of nodes

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:y="http://www.yworks.com/xml/yfiles-for-silverlight/1.0/xaml" 
    xmlns:ControlStyles="clr-namespace:Demo.yFiles.Graph.ControlStyles" 
>

<Style x:Key="CustomerNodeTemplate" TargetType="y:NodeControl">
  <Setter Property="MinHeight" Value="80"/>
  <Setter Property="MinWidth" Value="150"/>
  <Setter Property="ContentTemplate">
    <Setter.Value>
      <DataTemplate>
        <StackPanel>
          <StackPanel Orientation="Horizontal">
            <TextBlock Text="Id: "/>
            <TextBlock Text="{Binding Id}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal">
            <TextBlock Text="Location: "/>
            <TextBlock Text="{Binding Location}"/>
          </StackPanel>
        </StackPanel>
      </DataTemplate>
    </Setter.Value>
  </Setter>
  <Setter Property="Background">
    <Setter.Value>
      <!-- Background Gradient -->
      <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
        <LinearGradientBrush.GradientStops>
          <GradientStop Offset="0" Color="#FFCCFFFF"/>
          <GradientStop Offset="1" Color="#FF249ae7"/>
        </LinearGradientBrush.GradientStops>
      </LinearGradientBrush>
    </Setter.Value>
  </Setter>
  <Setter Property="BorderBrush" Value="Transparent"/>
  <Setter Property="BorderThickness" Value="1"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="y:NodeControl">
        <Border x:Name="border" CornerRadius="5" 
                Background="{TemplateBinding Background}" 
                BorderThickness="{TemplateBinding BorderThickness}" 
                BorderBrush="{TemplateBinding BorderBrush}">
          <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="ItemFocusStates">
              <VisualStateGroup.Transitions>
                <VisualTransition GeneratedDuration="0:0:0.25"/>
              </VisualStateGroup.Transitions>
              <VisualState x:Name="ItemUnfocused"/>
              <VisualState x:Name="ItemFocused">
                <Storyboard>
                  <ColorAnimation Storyboard.TargetName="border" To="Black" 
                                  Storyboard.TargetProperty="
                                    (Border.BorderBrush).
                                    (SolidColorBrush.Color)" Duration="0"/>
                </Storyboard>
              </VisualState>
            </VisualStateGroup>
          </VisualStateManager.VisualStateGroups>
          <StackPanel>
            <!-- Name -->
            <Border>
              <Border.Background>
                <SolidColorBrush Opacity="0.25" Color="White"/>
              </Border.Background>
              <TextBlock Margin="1,10,1,5" x:Name="NameTextBlock" FontSize="13"
                         Text="{Binding 
                           RelativeSource={RelativeSource TemplatedParent},
                           Path=Content.Name}" 
                         TextAlignment="Center" HorizontalAlignment="Center"/>
            </Border>
            <!-- Other Properties -->
            <ContentPresenter Margin="10,10,0,0"/>
          </StackPanel>
        <Border.Effect>
          <DropShadowEffect Color="Orange" Opacity="0" BlurRadius="20" 
                            ShadowDepth="0"/>
        </Border.Effect>
      </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
</ResourceDictionary>
Node Representation Using SimpleAbstractNodeStyle

Abstract generic class SimpleAbstractNodeStyle<TVisual> facilitates convenient creation of custom node representations. SimpleAbstractNodeStyle is an INodeStyle implementation that uses a fixed, predefined renderer implementation, eliminating the need to create an explicit renderer for node representations.

The following methods from class SimpleAbstractNodeStyle are called to create, respectively update, the visual representation for a given node. Being abstract, the former method needs to be overridden by subclasses. The latter method is recommended to override as well in order to improve the rendering performance in an application.

protected abstract TVisual CreateVisual(INode node,
                                        IRenderContext renderContext)
Description Abstract method to create the visual representation for the given INode.
protected virtual TVisual UpdateVisual(INode node,
                                       IRenderContext renderContext,
                                       TVisual oldVisual)
Description Optional, but recommended override to update the given INode's visual representation.

The MySimpleNodeStyle class in the SimpleCustomStyleWindow demo application presents a node style implementation that subclasses SimpleAbstractNodeStyle. How to subclass SimpleAbstractNodeStyle is also shown in the Custom Styles tutorial; see especially the steps 01 and 03 on how to override the CreateVisual and UpdateVisual methods.

The StyleDecoratorsDemo demo application shows how to decorate node styles using a node style wrapper class that bases on SimpleAbstractNodeStyle. The decorator pattern for node styles is also shown in the Custom Styles tutorial; see especially step 23.

Node Style Customization

Custom node styles that do not base on SimpleAbstractNodeStyle need to implement the Renderer property defined in interface INodeStyle and the Install method defined in the superinterface of generic base type IVisualStyle. See also the section called “Creating Custom Styles”.

Class ShadowNodeStyleDecorator is a special INodeStyle implementation that allows to decorate node styles. It can be used to add a drop shadow to the visual representation of the wrapped node style.

Example 4.13. Decorating a drop shadow onto a node style

// 'graph' is of type yWorks.yFiles.UI.Model.IGraph.
ShapeNodeStyle sns = new ShapeNodeStyle();
sns.Brush = new SolidColorBrush(Colors.LightGray);
sns.Shape = ShapeNodeShape.RoundRectangle;
ShadowNodeStyleDecorator snsd = new ShadowNodeStyleDecorator(sns);

// 'someNode' is of type yWorks.yFiles.UI.Model.INode.
graph.SetStyle(someNode, snsd);

Figure 4.10, “ShapeNodeStyle decorated using ShadowNodeStyleDecorator” shows a node using ShapeNodeStyle that is decorated by means of ShadowNodeStyleDecorator to also have a drop shadow.

Figure 4.10. ShapeNodeStyle decorated using ShadowNodeStyleDecorator

ShapeNodeStyle decorated using ShadowNodeStyleDecorator

The decorator pattern for node styles is also shown in the StyleDecoratorsDemo demo application and the Custom Styles tutorial; see especially step 23.

Edge Styles

Edge styles are responsible for the graphical rendering of edges in the canvas. Interface IEdgeStyle is the common base type for actual implementations, and abstract class AbstractEdgeStyle provides default implementations for the methods of interface IEdgeStyle. Figure 4.11, “Hierarchy of edge style types” shows a part of the type hierarchy of edge style implementations.

Figure 4.11. Hierarchy of edge style types

Hierarchy of edge style types.

Note

Rendering of graphical decorations at the ends of an edge, like, e.g., arrowheads, is provided by the classes described in the section called “Arrows”.

Table 4.4, “Predefined edge style implementations” lists the predefined edge style implementations present in the yWorks.yFiles.UI.Drawing namespace.

Table 4.4. Predefined edge style implementations

Type Name Description
EdgeSegmentControlEdgeStyle Enables using Silverlight-styled ContentControls for the visual representation of the segments of an edge. (See below for a description on using EdgeSegmentControlEdgeStyle.)
SimpleAbstractEdgeStyle<TVisual> Greatly simplifies edge style customization using subclassing. (See below for a description of SimpleAbstractEdgeStyle.)
ArcEdgeStyle Provides an arc representation for an edge. The rendering does not take into account bends of an edge, instead the arc's height is determined as a fixed ratio or fixed height.
PolylineEdgeStyle An edge representation where straight line segments are used to connect the bends.

Figure 4.12, “Edge style representations” shows examples of edges using predefined edge style implementations.

Figure 4.12. Edge style representations

ArcEdgeStyle
PolylineEdgeStyle
ArcEdgeStyle PolylineEdgeStyle
Edge Representation Using ContentControls

Edge style type EdgeSegmentControlEdgeStyle allows to use the ContentControl-based Silverlight styling mechanism to create the visual representation of edges in an IGraph. This enables using the XAML control templating scheme to define the appearance of edges, including the support of triggers, visual states, and transitions.

The control style and its control template is applied to class EdgeSegmentControl, a specialized ContentControl subclass. Example 4.14, “XAML ControlTemplate definition excerpt for the appearance of edge segments” shows part of a control template definition for edge segments taken from the ControlStylesDemo demo application. Note how the target type of the template is specified to be of type EdgeSegmentControl.

Example 4.14. XAML ControlTemplate definition excerpt for the appearance of edge segments

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:y="http://www.yworks.com/xml/yfiles-for-silverlight/1.0/xaml" 
    xmlns:ControlStyles="clr-namespace:Demo.yFiles.Graph.ControlStyles"
>

<Style x:Key="EdgeSegmentTemplate" TargetType="y:EdgeSegmentControl">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="y:EdgeSegmentControl">
        <Grid>
          <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="SegmentPositionStates">
              <VisualStateGroup.Transitions>
                <VisualTransition GeneratedDuration="0:0:0"/>
              </VisualStateGroup.Transitions>
              <VisualState x:Name="FirstSegment"></VisualState>
              <VisualState x:Name="InnerSegment"></VisualState>
              <VisualState x:Name="LastSegment">
                <Storyboard>
                  <ObjectAnimationUsingKeyFrames Duration="0" 
                      Storyboard.TargetName="rectangle" 
                      Storyboard.TargetProperty="(Rectangle.Margin)">
                    <DiscreteObjectKeyFrame Value="-2.5,0,7.5,0" KeyTime="0">
                    </DiscreteObjectKeyFrame>
                  </ObjectAnimationUsingKeyFrames>
                  <ObjectAnimationUsingKeyFrames Duration="0" 
                      Storyboard.TargetName="targetArrow" 
                      Storyboard.TargetProperty="Visibility">
                    <DiscreteObjectKeyFrame Value="Visible" KeyTime="0"/>
                  </ObjectAnimationUsingKeyFrames>
                </Storyboard>
              </VisualState>
              <VisualState x:Name="SingleSegment">
                <Storyboard>
                  <ObjectAnimationUsingKeyFrames Duration="0" 
                      Storyboard.TargetName="rectangle" 
                      Storyboard.TargetProperty="(Rectangle.Margin)">
                    <DiscreteObjectKeyFrame Value="-2.5,0,7.5,0" KeyTime="0">
                    </DiscreteObjectKeyFrame>
                  </ObjectAnimationUsingKeyFrames>
                  <ObjectAnimationUsingKeyFrames Duration="0" 
                      Storyboard.TargetName="targetArrow" 
                      Storyboard.TargetProperty="Visibility">
                    <DiscreteObjectKeyFrame Value="Visible" KeyTime="0"/>
                  </ObjectAnimationUsingKeyFrames>
                </Storyboard>
              </VisualState>
            </VisualStateGroup>
          </VisualStateManager.VisualStateGroups>
          <Rectangle x:Name="rectangle" Fill="OrangeRed" 
                     RadiusX="2.5" RadiusY="2.5" Margin="-2.5,0,-2.5,0">
          </Rectangle>
          <Polygon x:Name="targetArrow" Points="10, 2.5, 0, 0, 0, 5" 
                   Fill="{Binding ElementName=rectangle, Path=Fill}" 
                   VerticalAlignment="Stretch" HorizontalAlignment="Right" 
                   Visibility="Collapsed"/>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
</ResourceDictionary>
Edge Representation Using SimpleAbstractEdgeStyle

Abstract generic class SimpleAbstractEdgeStyle<TVisual> facilitates convenient creation of custom edge representations. SimpleAbstractEdgeStyle is an IEdgeStyle implementation that uses a fixed, predefined renderer implementation, eliminating the need to create an explicit renderer for edge representations.

The following methods from class SimpleAbstractEdgeStyle are called to create, respectively update, the visual representation for a given edge. Being abstract, the former method needs to be overridden by subclasses. The latter method is recommended to override as well in order to improve the rendering performance in an application.

protected abstract TVisual CreateVisual(IEdge edge,
                                        IRenderContext renderContext)
Description Abstract method to create the visual representation for the given IEdge.
protected virtual TVisual UpdateVisual(IEdge edge,
                                       IRenderContext renderContext,
                                       TVisual oldVisual)
Description Optional, but recommended override to update the given IEdge's visual representation.

The MySimpleEdgeStyle class in the SimpleCustomStyleWindow demo application presents an edge style implementation that subclasses SimpleAbstractEdgeStyle. How to subclass SimpleAbstractEdgeStyle is also shown in the Custom Styles tutorial; see especially step 16.

The StyleDecoratorsDemo demo application shows how to decorate edge styles using an edge style wrapper class that bases on SimpleAbstractEdgeStyle. The decorator pattern for edge styles is also shown in the Custom Styles tutorial; see especially step 23.

Edge Style Customization

Abstract class AbstractEdgeStyle serves as a convenient basis for actual edge styles that need more custom rendering support than SimpleAbstractEdgeStyle provides, for example. Custom edge styles that base on AbstractEdgeStyle still need to implement the Install method, which is repeated from the superinterface of generic base type IVisualStyle, however adapted to use IEdge as the type for the original type parameter TModelItem. See also the section called “Creating Custom Styles”.

Note

Because bends are an integral part of an edge, they are rendered by the edge style, too.

Port Styles

Port styles are responsible for the graphical rendering of ports in the canvas. Interface IPortStyle is the common base type for actual implementations. Figure 4.13, “Hierarchy of port style types” shows a part of the type hierarchy of port style implementations.

Figure 4.13. Hierarchy of port style types

Hierarchy of port style types.

Table 4.5, “Predefined port style implementations” lists the predefined port style implementations present in the yWorks.yFiles.UI.Drawing namespace.

Table 4.5. Predefined port style implementations

Type Name Description
PortControlPortStyle Enables using Silverlight-styled ContentControls for the visual representation of a port. (See below for a description on using PortControlPortStyle.)
SimpleAbstractPortStyle<TVisual> Greatly simplifies port style customization using subclassing. (See below for a description of SimpleAbstractPortStyle.)
SimplePortStyle Provides a very simple representation for ports.
Port Representation Using ContentControls

Port style type PortControlPortStyle allows to use the ContentControl-based Silverlight styling mechanism to create the visual representation of ports in an IGraph. This enables using the XAML control templating scheme to define the appearance of ports, including the support of triggers, visual states, and transitions.

The control style and its control template is applied to class PortControl, a specialized ContentControl subclass. Example 4.15, “XAML ControlTemplate definition for the appearance of ports” shows a control template definition for ports taken from the ControlStylesDemo demo application. Note how the target type of the template is specified to be of type PortControl.

Example 4.15. XAML ControlTemplate definition for the appearance of ports

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:y="http://www.yworks.com/xml/yfiles-for-silverlight/1.0/xaml" 
    xmlns:ControlStyles="clr-namespace:Demo.yFiles.Graph.ControlStyles"
>

<Style x:Key="PortTemplate" TargetType="y:PortControl">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="y:PortControl">
        <Border x:Name="border" CornerRadius="5">
          <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="MouseStates">
              <VisualStateGroup.Transitions>
                <VisualTransition GeneratedDuration="0:0:1"/>
              </VisualStateGroup.Transitions>
              <VisualState x:Name="MouseOutside"/>
              <VisualState x:Name="MouseOver">
                <Storyboard>
                  <ColorAnimation Storyboard.TargetName="border" Duration="0" 
                                  Storyboard.TargetProperty="
                                    (Border.Background).
                                    (LinearGradientBrush.GradientStops)[1].
                                    (GradientStop.Color)" To="Black"/>
                </Storyboard>
              </VisualState>
            </VisualStateGroup>
          </VisualStateManager.VisualStateGroups>
          <Border.Background>
            <!-- Background Gradient -->
            <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
              <LinearGradientBrush.GradientStops>
                <GradientStop Offset="0" Color="#FFFFFFBB"/>
                <GradientStop x:Name="Color2" Offset="1" Color="#FFFFEE77"/>
              </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
          </Border.Background>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
</ResourceDictionary>
Port Representation Using SimpleAbstractPortStyle

Abstract generic class SimpleAbstractPortStyle<TVisual> facilitates convenient creation of custom port representations. SimpleAbstractPortStyle is an IPortStyle implementation that uses a fixed, predefined renderer implementation, eliminating the need to create an explicit renderer for port representations.

The following methods from class SimpleAbstractPortStyle are called to create the visual representation for a given port and that return the port's bounds. The method to update the port's visual representation is also included. Being abstract, the former methods need to be overridden by subclasses. The latter method is recommended to override as well in order to improve the rendering performance in an application.

protected abstract TVisual CreateVisual(IPort port,
                                        IRenderContext renderContext)
Description Abstract method to create the visual representation for the given IPort.
protected abstract RectD GetBounds(IPort port, ICanvasContext canvasContext)
Description Abstract method to get the bounds of the given IPort's visual.
protected virtual TVisual UpdateVisual(IPort port,
                                       IRenderContext renderContext,
                                       TVisual oldVisual)
Description Optional, but recommended override to update the given IPort's visual representation.

The MySimplePortStyle class in the SimpleCustomStyleWindow demo application presents a port style implementation that subclasses SimpleAbstractPortStyle. How to subclass SimpleAbstractPortStyle is also shown in the Custom Styles tutorial; see especially step 22.

Port Style Customization

Custom port styles that do not base on SimpleAbstractPortStyle need to implement the Renderer property defined in interface IPortStyle and the Install method defined in the superinterface of generic base type IVisualStyle. See also the section called “Creating Custom Styles”.

Label Styles

Label styles are responsible for the graphical rendering of labels in the canvas. Interface ILabelStyle is the common base type for actual implementations. Figure 4.14, “Predefined label style types” shows a part of the type hierarchy of label style implementations.

Figure 4.14. Predefined label style types

Predefined label style types.

Table 4.6, “Predefined label style implementations” lists the predefined label style implementations present in the yWorks.yFiles.UI.Drawing namespace.

Table 4.6. Predefined label style implementations

Type Name Description
LabelControlLabelStyle Enables using Silverlight-styled ContentControls for the visual representation of a label. (See below for a description on using LabelControlLabelStyle.)
SimpleAbstractLabelStyle<TVisual> Greatly simplifies label style customization using subclassing. (See below for a description of SimpleAbstractLabelStyle.)
SimpleLabelStyle Enables rendering of a label's text within an oriented rectangle.
IconLabelStyle Builds on the functionality that SimpleLabelStyle provides and additionally allows to place an icon at several positions relative to the label's text.
NodeStyleLabelStyleAdapter Allows to add the rendering of a given node style as the background for the visual representation of the wrapped label style. See below.
Label Representation Using ContentControls

Label style type LabelControlLabelStyle allows to use the ContentControl-based Silverlight styling mechanism to create the visual representation of labels in an IGraph. This enables using the XAML control templating scheme to define the appearance of labels, including the support of triggers, visual states, and transitions.

The control style and its control template is applied to class LabelControl, a specialized ContentControl subclass. Example 4.16, “XAML ControlTemplate definition excerpt for the appearance of labels” shows part of a control template definition for labels taken from the ControlStylesDemo demo application. Note how the target type of the template is specified to be of type LabelControl.

Example 4.16. XAML ControlTemplate definition excerpt for the appearance of labels

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:y="http://www.yworks.com/xml/yfiles-for-silverlight/1.0/xaml" 
    xmlns:ControlStyles="clr-namespace:Demo.yFiles.Graph.ControlStyles"
>

<Style x:Key="LabelTemplate" TargetType="y:LabelControl">
  <Setter Property="Background">
    <Setter.Value>
      <!-- Background Gradient -->
      <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
        <LinearGradientBrush.GradientStops>
          <GradientStop Offset="0" Color="#FFFFFFBB"/>
          <GradientStop Offset="1" Color="#FFFFEE77"/>
        </LinearGradientBrush.GradientStops>
      </LinearGradientBrush>
    </Setter.Value>
  </Setter>
  <Setter Property="ContentTemplate">
    <Setter.Value>
      <DataTemplate>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="{Binding Path=Product.Name}" FontSize="6"/>
          <TextBlock Text=" &lt;- " FontSize="6"/>
          <TextBlock Text="{Binding Path=Customer.Name}" FontSize="6"/>
        </StackPanel>
      </DataTemplate>
    </Setter.Value>
  </Setter>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="y:LabelControl">
        <Border CornerRadius="5" Background="{TemplateBinding Background}" 
                MinWidth="100">
          <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="ItemSelectionStates">
              <VisualState x:Name="ItemSelected">
                <Storyboard Duration="0">
                  <ObjectAnimationUsingKeyFrames 
                      Storyboard.TargetName="LabelTextBlock" 
                      Storyboard.TargetProperty="FontWeight">
                    <DiscreteObjectKeyFrame Value="Bold" KeyTime="0"/>
                  </ObjectAnimationUsingKeyFrames>
                </Storyboard>
              </VisualState>
              <VisualState x:Name="ItemUnselected"/>
            </VisualStateGroup>
          </VisualStateManager.VisualStateGroups>
          <StackPanel Orientation="Vertical" Margin="5">
            <TextBlock x:Name="LabelTextBlock" TextAlignment="Center" Margin="3" 
                       Text="{TemplateBinding LabelText}" FontSize="12"/>
            <ContentPresenter x:Name="contentPresenter"/>
          </StackPanel>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
</ResourceDictionary>
Label Representation Using SimpleAbstractLabelStyle

Abstract generic class SimpleAbstractLabelStyle<TVisual> facilitates convenient creation of custom label representations. SimpleAbstractLabelStyle is an ILabelStyle implementation that uses a fixed, predefined renderer implementation, eliminating the need to create an explicit renderer for label representations.

The following methods from class SimpleAbstractLabelStyle are called to create the visual representation for a given label and that return the label's preferred size. The method to update the label's visual representation is also included. Being abstract, the former methods need to be overridden by subclasses. The latter method is recommended to override as well in order to improve the rendering performance in an application.

protected abstract TVisual CreateVisual(ILabel label,
                                        IRenderContext renderContext)
Description Abstract method to create the visual representation for the given ILabel.
protected abstract SizeD GetPreferredSize(ILabel label)
Description Abstract method to get the given ILabel's preferred size.
protected virtual TVisual UpdateVisual(ILabel label,
                                       IRenderContext renderContext,
                                       TVisual oldVisual)
Description Optional, but recommended override to update the given ILabel's visual representation.

The MySimpleLabelStyle class in the SimpleCustomStyleWindow demo application presents a label style implementation that subclasses SimpleAbstractLabelStyle. How to subclass SimpleAbstractLabelStyle is also shown in the Custom Styles tutorial; see especially the steps 10 and 12 on how to override the CreateVisual and UpdateVisual methods.

The StyleDecoratorsDemo demo application shows how to decorate label styles using a label style wrapper class that bases on SimpleAbstractLabelStyle. The decorator pattern for label styles is also shown in the Custom Styles tutorial; see especially step 23.

Label Style Customization

Custom label styles that do not base on SimpleAbstractLabelStyle need to implement the Renderer property defined in interface ILabelStyle and the Install method defined in the superinterface of generic base type IVisualStyle. See also the section called “Creating Custom Styles”.

Class NodeStyleLabelStyleAdapter is an ILabelStyle implementation that allows to decorate label styles. It can be used to add the rendering of a given node style as the background for the visual representation of the wrapped label style. Figure 4.15, “Edge label that uses NodeStyleLabelStyleAdapter” shows an edge label where a BevelNodeStyle is used to render the background.

Figure 4.15. Edge label that uses NodeStyleLabelStyleAdapter

NodeStyleLabelStyleAdapter

Working with Styles

Setting up different node styles and associating them with nodes of a graph is presented in Example 4.17, “Creating a graph”. The resulting graph is depicted in Figure 4.16, “Shape node style and image node style”.

Figure 4.16. Shape node style and image node style

Shape node style and image node style.

Example 4.17. Creating a graph

IGraph graph = new DefaultGraph();

// Set up a node style that uses a circle.
ShapeNodeStyle sns = graph.NodeDefaults.Style as ShapeNodeStyle;
if (sns != null) {
  sns.Shape = ShapeNodeShape.Ellipse;
}

// Set up a node style that uses an image to represent nodes.
ImageNodeStyle ins = new ImageNodeStyle();
ins.Image =
  new BitmapImage(new Uri("PathToSomeImage.png", UriKind.RelativeOrAbsolute));
graph.NodeDefaults.Style = ins;
graph.NodeDefaults.Size = new SizeD(ins.Image.Width*2.0, ins.Image.Height);

// Create some nodes. 
INode[] nodes = new INode[6];
for (int i = 0; i < nodes.Length - 1; i += 2) {
  nodes[i] = graph.CreateNode(new PointD(i*50, 100));
  graph.SetStyle(nodes[i], sns);
  graph.SetBounds(nodes[i],
                  new RectD(nodes[i].Layout.X, nodes[i].Layout.Y, 40, 40));
  nodes[i + 1] = graph.CreateNode(new PointD(i*50, 200));
}

Styles can be associated with model items using different schemes: an IVisualStyle instance for a specific kind of model item can be both

  • shared among a set of like items or be
  • used exclusively with a single item.

Style sharing can be easily achieved whenever a single style instance is associated with a model item by reference. Example 4.18, “Style sharing” presents this scheme using the SetStyle method to associate a single custom ShapeNodeStyle instance by reference with a set of nodes. Similarly, when associating a single ShapeNodeStyle instance by reference at node creation time, the style can also be shared among multiple nodes.

Example 4.18. Style sharing

// 'graph' is of type yWorks.yFiles.UI.Model.IGraph.

MyShapeNodeStyle shapeNS = new MyShapeNodeStyle();
foreach (INode node in myNodeList) {
  graph.SetStyle(node, shapeNS);
}

When a node is associated with the default style via the support for default settings provided by IGraph, the style instance that is returned by the Style property of the INodeDefaults implementation is also shared.

Example 4.19, “Changing the color of a node” shows how to obtain the style that is used for a specific node from its graph and changing the node's color. Styles for the other kinds of items can be obtained and modified in a similar manner.

Example 4.19. Changing the color of a node

// 'graph' is of type yWorks.yFiles.UI.Model.IGraph.
// 'node' is of type yWorks.yFiles.UI.Model.INode.

ShapeNodeStyle shapeNS = node.Style as ShapeNodeStyle;
if (shapeNS != null) {
  shapeNS.Brush = Brushes.AliceBlue;
}

// Tell the engine to update the displays (the style instance cannot know itself
// which display it appears in).
graph.InvalidateDisplays();

Creating Custom Styles

Generally, style types team up with a corresponding style renderer type, separating duties as follows: the actual style implementation ideally only holds the state that is needed for rendering an item, while the style renderer provides the logic that performs the real work using the style's state. This separation enables sharing of style renderer instances among an arbitrary number of style objects and is also known as the Flyweight pattern.

As a consequence, creating a custom style is mainly the task of creating a proper style renderer. Most conveniently, a custom style type can be created using the appropriate generic abstract class listed in the following table:

Table 4.7. Base types for custom style type implementations

Base Type Description
SimpleAbstractNodeStyle<TVisual> Simplifies node style customization using subclassing.
SimpleAbstractEdgeStyle<TVisual> Simplifies edge style customization using subclassing.
SimpleAbstractLabelStyle<TVisual> Simplifies label style customization using subclassing.
SimpleAbstractPortStyle<TVisual> Simplifies port style customization using subclassing.

Each of these base types uses a fixed, predefined renderer implementation for the representation of the respective graph element, eliminating the need to create an explicit renderer and thus simplifying the creation of a custom style type to appropriate implementations of a few callback methods. Instead the work of the renderer is delegated to methods in the subclass, of which the important ones are abstract.

Example 4.20, “Custom node style implementation” presents a sample node style type based on generic abstract class SimpleAbstractNodeStyle that provides custom implementations for the callback methods.

Example 4.20. Custom node style implementation

public class MyCirclesNodeStyle : SimpleAbstractNodeStyle<Canvas> {
  private double percentage = 0.25d;
  private Color backColor = Colors.Blue;
  private Color foreColor = Colors.Orange;

  public double Percentage {
    get { return percentage; }
    set { percentage = Math.Min(Math.Max(value, 0.0), 1.0); }
  }
  public Color BackColor {
    get { return backColor; }
    set { backColor = value; }
  }
  public Color ForeColor {
    get { return foreColor; }
    set { foreColor = value; }
  }

  // Paints a filled circle and on top of that another filled circle. The radius
  // of the second circle is given as a percentage value relative to that of the
  // first circle.
  protected override Canvas CreateVisual(INode node, IRenderContext rc) {
    var canvas = new Canvas();
    Brush brush = new SolidColorBrush(style.BackColor);
    canvas.Children.Add(new Ellipse() { Fill = brush, Width = node.Layout.Width,
        Height = node.Layout.Height, Stroke = brush, StrokeThickness = 1.0
      });

    brush = new SolidColorBrush(style.ForeColor);
    var innerEllipse = new Ellipse() { Fill = brush, Stroke = brush,
        StrokeThickness = 1.0
      };
    innerEllipse.Width = node.Layout.Width * style.Percentage;
    innerEllipse.Height = node.Layout.Height * style.Percentage;

    Canvas.SetTop(innerEllipse, 
                  (node.Layout.Height * (1.0 - style.Percentage)) / 2.0);
    Canvas.SetLeft(innerEllipse, 
                   (node.Layout.Width * (1.0 - style.Percentage)) / 2.0);
    canvas.Children.Add(innerEllipse);

    // Tell the CanvasControl where to place the FrameworkElement.
    canvas.SetCanvasArrangeRect(
        new Rect(node.Layout.X, node.Layout.Y,
                 node.Layout.Width, node.Layout.Height));
    return canvas;
  }

  // For performance reasons, we implement the update method.
  protected override Canvas UpdateVisual(INode node, IRenderContext rc,
                                         Canvas oldVisual) {
    var canvas = oldVisual as Canvas;
    if (canvas != null && canvas.Children.Count == 2) {
      // Adjust the data for the outer ellipse.
      Ellipse ellipse1 = (Ellipse) canvas.Children[0];
      ellipse1.Width = node.Layout.Width;
      ellipse1.Height = node.Layout.Height;

      // Adjust the data for inner ellipse.
      Ellipse ellipse2 = (Ellipse) canvas.Children[1];
      ellipse2.Width = node.Layout.Width * style.Percentage;
      ellipse2.Height = node.Layout.Height * style.Percentage;;

      // Tell the CanvasControl where to move the FrameworkElement.
      canvas.SetCanvasArrangeRect(
          new Rect(node.Layout.X, node.Layout.Y,
                   node.Layout.Width, node.Layout.Height));
      return canvas;
    }
    return CreateVisual(rc);
  }

  // The abstract base class calls this method when hit tests should be
  // performed.
  protected override bool IsHit(INode node, PointD point, ICanvasContext ctx) {
    return GeomSupport.EllipseContains(node.Layout.ToRectD(), point,
                                       ctx.HitTestRadius);
  }

  // Finds an intersection for the current node.
  protected override PointD? GetIntersection(INode node,
                                             PointD inner, PointD outer) {
    return GeomSupport.FindEllipseLineIntersection(node.Layout.ToRectD(),
                                                   inner, outer);
  }

  // Checks whether the given coordinates lie within the circle.
  protected override bool IsInside(INode node, PointD point) {
    return GeomSupport.EllipseContains(node.Layout.ToRectD(), point, 0);
  }
}

The Custom Styles tutorial presents step by step how to create custom node, edge, port, and label styles on the basis of the respective SimpleAbstractStyle class.

Style Renderers

The available style renderer implementations either directly or indirectly inherit from abstract generic class AbstractStyleRenderer<TModelItem, TModelStyle, TStyle>, which combines all necessary functionality that relates to the visual appearance of graph elements. In particular, it implements interface IVisualCreator, which defines the methods for creating and updating the Visual for a model item. Figure 4.17, “Common style renderer base type AbstractStyleRenderer” depicts the inheritance structure of AbstractStyleRenderer.

Figure 4.17. Common style renderer base type AbstractStyleRenderer

Common style renderer base type AbstractStyleRenderer.

Node Style Renderers

Node style renderers are the complementary counterparts to the node styles. They provide the actual logic that is invoked by the canvas for rendering an item. Interface INodeStyleRenderer is the common base type for actual implementations.

The predefined node style renderer implementations together with the respective styles they correspond to are listed in Table 4.8, “Predefined node style renderer implementations”.

Table 4.8. Predefined node style renderer implementations

Style Type Complementing Renderer Type Description
INodeControlNodeStyle NodeControlNodeStyleRenderer The renderer uses the styled NodeControl instance of the node's associated INodeControlNodeStyle object to render the node and its tag.
IShapeNodeStyle ShapeNodeStyleRenderer Renders a node using the shape that is defined by the Shape property of the node's associated IShapeNodeStyle object.
IImageNodeStyle ImageNodeStyleRenderer Renders a node using the image returned by the Image property of the node's associated IImageNodeStyle object.
IGeneralPathNodeStyle GeneralPathNodeStyleRenderer Renders a node using the GeneralPath object returned by the Path property of the node's associated IGeneralPathNodeStyle object.
IBevelNodeStyle BevelNodeStyleRenderer Renders a node using the properties of the node's associated IBevelNodeStyle object.
IPanelNodeStyle PanelNodeStyleRenderer Renders a node using the properties of the node's associated IPanelNodeStyle object.
IShinyPlateNodeStyle ShinyPlateNodeStyleRenderer Renders a node using the properties of the node's associated IShinyPlateNodeStyle object.

Abstract generic class AbstractNodeStyleRenderer<TStyle> provides default implementations for most of the abstract methods from its base type AbstractStyleRenderer. Inheriting types only need to implement the CreateVisual method (see below) appropriately. Also, the type parameter, which is constrained to be of type INodeStyle, needs to be bound to the style type that this renderer type is actually complementing.

abstract FrameworkElement CreateVisual(IRenderContext ctx)
Description Abstract method in class AbstractNodeStyleRenderer for creating the Visual (in fact a FrameworkElement) for a node.

Edge Style Renderers

Edge style renderers are the complementary counterparts to the edge styles. They provide the actual logic that is invoked by the canvas for rendering an item. Interface IEdgeStyleRenderer is the common base type for actual implementations.

The predefined edge style renderer implementations together with the respective styles they correspond to are listed in Table 4.9, “Predefined edge style renderer implementations”.

Table 4.9. Predefined edge style renderer implementations

Style Type Complementing Renderer Type Description
IEdgeSegmentControlEdgeStyle EdgeSegmentControlEdgeStyleRenderer The renderer uses the styled EdgeSegmentControl instance of the node's associated IEdgeSegmentControlEdgeStyle object to render the segments of the edge and the edge's tag.
IArcEdgeStyle ArcEdgeStyleRenderer Renders an edge as an arc using the properties of the edge's associated IArcEdgeStyle object.
IPolylineEdgeStyle PolylineEdgeStyleRenderer Renders an edge as a poly-line path using the properties of the edge's associated IPolylineEdgeStyle object.

Abstract generic class PathBasedEdgeStyleRenderer<TStyle> provides default implementations for most of the methods from its base types, which in particular include AbstractStyleRenderer. The remaining methods that need to be implemented by inheriting types are listed below. Also, the type parameter, which is constrained to be of type IEdgeStyle, needs to be bound to the style type that this renderer type is actually complementing.

protected abstract GeneralPath CreatePath()
protected abstract Pen GetPen()
Description Abstract methods from class PathBasedEdgeStyleRenderer.

Port Style Renderers

Port style renderers are the complementary counterparts to the port styles. They provide the actual logic that is invoked by the canvas for rendering an item. Interface IPortStyleRenderer is the common base type for actual implementations.

The predefined port style renderer implementations together with the respective styles they correspond to are listed in Table 4.10, “Predefined port style renderer implementations”.

Table 4.10. Predefined port style renderer implementations

Style Type Complementing Renderer Type Description
IPortControlPortStyle PortControlPortStyleRenderer The renderer uses the styled PortControl instance of the node's associated IPortControlPortStyle object to render the port and its tag.
ISimplePortStyle SimplePortStyleRenderer Renders a port as a circle using the Brush property of the port's associated ISimplePortStyle.

Note that the port style renderers directly inherit from abstract generic class AbstractStyleRenderer. The type parameters are bound to the IPort and IPortStyle types, and to the IPortControlPortStyle, and ISimplePortStyle type, respectively.

Label Style Renderers

Label style renderers are the complementary counterparts to the label styles. They provide the actual logic that is invoked by the canvas for rendering an item. Interface ILabelStyleRenderer is the common base type for actual implementations.

The predefined label style renderer implementations together with the respective styles they correspond to are listed in Table 4.11, “Predefined label style renderer implementations”.

Table 4.11. Predefined label style renderer implementations

Style Type Complementing Renderer Type Description
ILabelControlLabelStyle LabelControlLabelStyleRenderer The renderer uses the styled LabelControl instance of the node's associated ILabelControlLabelStyle object to render the label and its tag.
ISimpleLabelStyle SimpleLabelStyleRenderer Renders a label's text within the oriented rectangle that describes the label's layout. The renderer uses the properties of the label's associated ISimpleLabelStyle object.
IIconLabelStyle IconLabelStyleRenderer Renders a label's text (see description of SimpleLabelStyleRenderer) and the icon that the label's associated IIconLabelStyle object returns.

Note that IconLabelStyleRenderer directly inherits from abstract generic class AbstractStyleRenderer. The type parameters are bound to the ILabel, ILabelStyle, and IconLabelStyle types.

Abstract generic class AbstractLabelStyleRenderer<TStyle> provides default implementations for most of the abstract methods from its base type AbstractStyleRenderer. The remaining methods that need to be implemented by inheriting types are listed below. Also, the type parameter, which is constrained to be of type ILabelStyle, needs to be bound to the style type that this renderer type is actually complementing.

protected abstract SizeD GetPreferredSize()
abstract FrameworkElement CreateVisual(IRenderContext ctx)
Description Abstract methods from class AbstractLabelStyleRenderer.

Port Support

The location of a port at a node or along an edge path, in both static as well as dynamic scenarios, is determined by a so-called port location model. It is responsible for finding the proper location for a port with respect to a port location model parameter. The port location model parameter is associated with a port and encodes its desired position. It is created by a port location model and is valid only in the context of this port location model.

In a dynamic scenario, where a user resizes or moves a node, changes the path of an edge, or moves a port, port location models are queried to determine the proper location of the port(s).

Figure 4.18. IPortLocationModel

IPortLocationModel.

Although a port location model determines the position of a port, it cannot be associated with one. Instead, a port location model parameter that is created by a port location model instance is associated with a given port. Then, to determine the port's position, this port location model instance is found via the model parameter's Model property.

Interface IPortLocationModel provides the basis for port location model implementations. Table 4.12, “Predefined port location model implementations” lists the predefined port location model implementations.

Table 4.12. Predefined port location model implementations

Model Type Description
AnchoredPortLocationModel Specifies the location of a port in world coordinates.
NodeScaledPortLocationModel The location of a port is specified relative to the center of the node and in relation to its geometry. See the description below.
BendAnchoredPortLocationModel Specifies the location of a port to be at the location of a bend.
SegmentRatioPortLocationModel The location of a port is specified on an edge segment at a specified ratio.

The port location models that support ports along an edge path are presented in tutorial demo application EdgeToEdgeWindow.

Class NodeScaledPortLocationModel defines a port location model where the location of a port is specified relative to the center of the node and in relation to the geometry of the node's bounding rectangle.

The center of the node is interpreted as the origin of a "coordinate system" where going up/to the left means negative sign and going down/to the right means positive sign. The distances to the borders of the node are interpreted relative to the node's width and height, respectively, so that the distance to the left (right) border, for example, is always half the width. Thus, the top left corner of the node corresponds to the relative location (-0.5, -0.5), the bottom right corner to (0.5, 0.5).

When the width or height of a node changes, the location of a port is appropriately updated according to the node's new geometry. In effect, this means that the distances to the center of the node are scaled using the same factor that the width/height have changed.

NodeScaledPortLocationModel provides convenient support for nine predefined port locations. Example 4.21, “Creating a model parameter for a custom port location” shows how a model parameter for a custom port location can be created. It uses a relative offset that defines the port to be anchored at a discrete location right of the node's center, half way to the node's border.

Example 4.21. Creating a model parameter for a custom port location

// 'node' is of type yWorks.yFiles.UI.Model.INode

NodeScaledPortLocationModel nsplm = NodeScaledPortLocationModel.Instance;
IPortLocationModelParameter plmp =
  nsplm.CreateOffsetParameter(node, new PointD(0.25, 0.0));

When the node is resized, this custom port location will be scaled accordingly so that the port is always located right of the node's center, half way to the node's border.

Port Location Model Parameter

A port location model parameter is an implementation of interface IPortLocationModelParameter that is created by a port location model. It encodes a possible port position that is valid in the context of its corresponding port location model.

To create a model parameter, IPortLocationModel defines the following method which can be used to obtain a model parameter that best matches a given (world coordinates) port location. The available port location models provide further model-specific methods to create valid model parameters.

IPortLocationModelParameter CreateParameter(IPortOwner portOwner,
                                            PointD location)
Description Method for creating the default port location model parameter.

The following methods provided by interface IGraph can be used to specify the port location model parameter for a port.

Note

When a port is added to a model item of the graph using the AddPort method that lacks the port location model parameter, class DefaultGraph associates the port location model parameter that it finds as part of the default settings for the model item's kind. Technically, this means that DefaultGraph queries the LocationModelParameter property of the IPortDefaults implementation which is part of the default settings of either nodes or edges.

IPort AddPort(IPortOwner owner,
              IPortLocationModelParameter locationModelParameter,
              IPortStyle portStyle,
              object tag)
IPort AddPort(IPortOwner owner,
              IPortLocationModelParameter locationModelParameter,
              IPortStyle portStyle)
Description Port location model parameter (extension) methods from interface IGraph for setting the port location model parameter when adding a new port to the graph model.
void SetLocationModelParameter(IPort port,
                               IPortLocationModelParameter locationParameter)
Description ... for setting the port location model parameter for an existing port.

Label Support

The geometry of both node labels and edge labels is determined by so-called label models. Specifically, a label model is responsible for finding the proper location for a label with respect to a "label model parameter." The label model parameter is associated with a label and encodes its desired position. It is created by a label model and is valid only in the context of this label model.

Figure 4.19. ILabelModel

ILabelModel.

Although a label model determines the position of a label, it cannot be associated with one. Instead, a label model parameter that is created by a label model instance is associated with a given label. Then, to determine the label's position, this label model instance is found via the model parameter's Model property.

Interface ILabelModel provides the basis for both node label model and edge label model implementations described below. In addition to these label models that determine the location of a label relative to its respective owner, class FreeLabelModel can be used to place labels freely in world coordinate space.

ILabelModel is also the basis for class GenericLabelModel q which can be used to create user-defined node label and edge label models.

Label Model Parameter

A label model parameter is an implementation of interface ILabelModelParameter that is created by a label model. It encodes a possible label position that is valid in the context of its corresponding label model.

Most often, the position that a model parameter encodes is a predefined position that is defined relative to the owner of the label. For example, the node label model ExteriorLabelModel uses model parameters that encode eight distinct logical positions around a node, named North, NorthEast, East, SouthEast, etc., which define positions outside the bounds of a node.

To create a model parameter, the following method defined by interfade ILabelModel can be used to obtain the so-called "default model parameter" for a given label model. The available node label models and edge label models provide further model-specific methods to create valid model parameters.

ILabelModelParameter CreateDefaultParameter()
Description Method for creating the default label model parameter.

The following methods provided by interface IGraph can be used to specify the label model parameter for a label.

Note

When a label is added to a model item of the graph using the AddLabel method that lacks the label model parameter, class DefaultGraph associates the label model parameter that it finds as part of the default settings for the model item's kind. Technically, this means that DefaultGraph queries the LabelModelParameter property of the ILabelDefaults implementation which is part of the default settings of either nodes or edges.

ILabel AddLabel(ILabeledItem item, ILabelModelParameter labelModelParameter,
                SizeD preferredSize, ILabelStyle style, string text, object tag)
ILabel AddLabel(ILabeledItem item, ILabelModelParameter labelModelParameter,
                string text)
Description Label model parameter (extension) methods from interface IGraph for setting the label model parameter when adding a new label to the graph model.
void SetLabelModelParameter(ILabel label, ILabelModelParameter parameter)
Description ... for setting the label model parameter for an existing label.

Node Label Models

Node label models are responsible for determining the geometry of labels where the actual owner is an INode. Figure 4.20, “Hierarchy of node label model types” depicts the type hierarchy of available node label models.

Figure 4.20. Hierarchy of node label model types

Hierarchy of node label model types.

Label model type FreeNodeLabelModel allows to freely define a node label's position relative to its owner. The predefined node label positions used with all other node label models are depicted in Figure 4.21, “Possible node label positions” and Figure 4.22, “Possible node label positions with InteriorStretchLabelModel”. The latter figure illustrates the predefined label positions of label model type InteriorStretchLabelModel .

Figure 4.21. Possible node label positions

Possible node label positions.

It is important to note that the set of valid positions varies with the model types, i.e., not all possible positions are valid for each node label model. Table 4.13, “Valid node label positions for each model type” lists the sets of valid positions for each node label model.

Table 4.13. Valid node label positions for each model type

Model Type Valid Positions
InteriorLabelModel NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
ExteriorLabelModel NorthWest, North, NorthEast, West, East, SouthWest, South, SouthEast
SandwichLabelModel North, South

The predefined node label positions of InteriorStretchLabelModel consist of the following five positions: North, East, South, West, and Center as shown in Figure 4.22, “Possible node label positions with InteriorStretchLabelModel”.

When a label is at the East or West position, it is also rotated so that the base line of the label text is near the center of the node. In addition, InteriorStretchLabelModel also changes the length of the labels to match the node's height (East and West positions) or its width (North and South positions). Labels at Center position use the entire geometry of the node.

Figure 4.22. Possible node label positions with InteriorStretchLabelModel

Possible node label positions with InteriorStretchLabelModel.
Center node label position of InteriorStretchLabelModel.
Non-Center label positions. Note the label orientation at East and West positions. Labels at Center position are stretched to use the entire geometry of the node.

Example 4.22, “Adding a node label using a specific label model parameter” demonstrates how to set a label model parameter that describes a position valid in the context of ExteriorLabelModel.

Example 4.22. Adding a node label using a specific label model parameter

// 'graph' is of type yWorks.yFiles.UI.Model.IGraph.
// 'node' is of type yWorks.yFiles.UI.Model.INode.

graph.AddLabel(node, ExteriorLabelModel.NorthEast, "I am NorthEast.");

Setting up and using node label models is extensively presented in tutorial demo application GraphEditorWindow.

Edge Label Models

Edge label models are responsible for determining the geometry of labels where the actual owner is an IEdge. Figure 4.23, “Hierarchy of edge label model types” depicts the type hierarchy of available edge label models.

Figure 4.23. Hierarchy of edge label model types

Hierarchy of edge label model types.

The following example code demonstrates how to set an edge label model parameter that describes a position valid in the context of RotatedSliderEdgeLabelModel:

Example 4.23. Sharing a label model parameter instance among a set of edge labels

// 'graph' is of type yWorks.yFiles.UI.Model.IGraph.

RotatedSliderEdgeLabelModel rselm = new RotatedSliderEdgeLabelModel();
rselm.Distance = 10;
// The label will be placed at the side of the edge near the first bend.
ILabelModelParameter rselmp = rselm.CreateParameterFromSource(0, 1.0);

foreach (ILabel edgeLabel in myEdgeList) {
  graph.SetLabelModelParameter(edgeLabel, rselmp);
}

Setting up and using edge label models is extensively presented in tutorial demo applications GraphEditorWindow and EdgeLabelingWindow.

Rotated Edge Label Models

Edge labels can be rotated, and with a suitable edge label model, the text of an edge label can be oriented in parallel to its corresponding edge segment and stay in parallel even when the slope of an edge segment is changed interactively by the user. This feature is called "Auto Rotation". It is supported by the models SmartEdgeLabelModel, RotatedSliderEdgeLabelModel, and RotatedSideSliderEdgeLabelModel.

Figure 4.24. Candidates for different configurations of the RotatedSideSliderEdgeLabelModel

Edge label candidates by RotatedSideSliderEdgeLabelModel.
Edge label candidates by RotatedSideSliderEdgeLabelModel.
Possible label candidates along the edge's path as found by RotatedSideSliderEdgeLabelModel for an auto-rotated edge label. ...the same, but for a non-auto-rotated edge label.
Edge label candidates by RotatedSideSliderEdgeLabelModel.
...the same, but for a non-auto-rotated edge label that has a preset rotation angle (of 315 degrees counterclockwise).

With RotatedSliderEdgeLabelModel or RotatedSideSliderEdgeLabelModel the label placement can be controlled by a number of properties:

bool AutoRotationEnabled { get; set; }
Description Specifies whether or not edge labels are automatically rotated according to the angle of the corresponding reference edge segment. By default, this feature is enabled.
double Angle { get; set; }
Description The angle of the label. If auto-rotation is enabled, the angle is measured counterclockwise relative to the edge. If not, it is relative to the x-axis. The default angle is 0.0.
double Distance { get; set; }
Description The distance between the label's box and the edge's path. A distance of 0.0, which is the default value, means label placement on the edge's path. In conjunction with the DistanceRelativeToEdge property, positive and negative values can be used to place labels to the sides of the edge path or above/below the edge path.
bool DistanceRelativeToEdge { get; set; }
Description Determines whether the value of the Distance property is interpreted to mean label placement to the sides of the edge path or above/below the edge path.

The parameters for both label models place the label on a specific segment of the edge with a given ratio where 0.0 places the label at the start of the segment and 1.0 at the end. For RotatingSliderEdgeLabelModel, the CreateParameterFromSource and CreateParameterFromTarget methods create a parameter with a segment index and ratio counting from the source or the target node of the edge, respectively. For RotatedSideSliderEdgeLabelModel, the corresponding CreateParameterFromSource and CreateParameterFromTarget methods additionally allow to determine whether the label should be placed to the left or to the right of the edge.

The following example demonstrates how to place labels using the RotatedSliderEdgeLabelModel and the RotatedSideSliderEdgeLabelModel:

Example 4.24. Using the Rotated(Side)SliderEdgeLabelModel

// 'graph' is of type yWorks.yFiles.UI.Model.IGraph.
// 'edge1' and 'edge2' are of type yWorks.yFiles.UI.Model.IEdge.

// Create a label model which auto-rotates and places the label on the edge path.
RotatedSliderEdgeLabelModel centerModel =
  new RotatedSliderEdgeLabelModel();
centerModel.Distance = 0;
centerModel.AutoRotationEnabled = true;

// Create a parameter for this model which places the label in the middle
// (ratio 0.5) of the second (0-based index 1) segment counting from the source
// node.
ILabelModelParameter centerParam =
  centerModel.CreateParameterFromSource(1, 0.5);

// Add the label.
graph.AddLabel(edge1, "Center", centerParam);

////////////////////////////////////////////////////////////////////////////////

// Create a label model which places the labels at either side of the edge.
RotatedSideSliderEdgeLabelModel sideModel =
  new RotatedSideSliderEdgeLabelModel();

sideModel.Distance = 10; // 10 units away from the edge
sideModel.AutoRotationEnabled = false; // auto rotation disabled
sideModel.Angle = 0; // parallel to the x-axis

// Create a parameter which places the label at the beginning of the first edge
// segment seen from the target node.
ILabelModelParameter sideParam =
  sideModel.CreateParameterFromTarget(0, 0.0, true);

// Add the label.
graph.AddLabel(edge2, "Side", sideParam);

Generic Label Model

Class GenericLabelModel provides a generic means to create user-defined label models for either node labels or edge labels. It allows to compose a custom blend of arbitrary label positions that are available with the predefined label models using the methods below:

API Excerpt 4.1. Methods for creating user-defined label models

// Determines the default label model parameter.
GenericLabelModel(ILabelModelParameter defaultParameter)

ILabelModelParameter AddParameter(ILabelModelParameter parameter)

For example, it is easily possible to create a node label model that uses the NorthEast label positions of the InteriorNodeLabelModel and the ExteriorNodeLabelModel together with a Center position as the default parameter. See Example 4.25, “Composing a node label model”.

Example 4.25. Composing a node label model

GenericLabelModel glm = new GenericLabelModel(InteriorLabelModel.Center);
glm.AddParameter(InteriorLabelModel.NorthEast);
glm.AddParameter(ExteriorLabelModel.NorthEast);