Chapter 7. API Concepts

Table of Contents

Look-up Mechanism
Benefits
Look-up Chaining
Customizing Look-up Behavior
Binding Data to Graph Elements
Mapping Data to Graph Elements
Tutorial Demo Code
User Tags

This chapter introduces some concepts used with the yFiles for Silverlight Viewer types. These include purpose-specific, i.e., graph model-related topics as well as practical programming aspects.

Look-up Mechanism

Interface ILookup lays the foundation for a lean yet powerful look-up mechanism that is used with a number of yFiles for Silverlight Viewer types. This look-up mechanism provides the means that allow for context-aware dynamic behavior on the instance level. It is heavily used for the inner workings of the yFiles for Silverlight Viewer framework. Among the types that implement ILookup are the central graph structure types, i.e., in particular IGraph, and also the model item types INode, IEdge, IPort, etc.

Benefits

Compared to classic object-oriented concepts, the look-up idiom enables a dynamism that would be hard (or at least tedious) to achieve using either subclassing or interface implementations. In particular, it can be used to easily add new "aspects" to the model items in a graph without having to subclass and implement yet another set of interfaces.

Also, the behavior of model items can be modified at runtime by simply changing their look-up to return either customized logic for some requested type, or the default, or nothing at all. Example 7.1, “Dynamically changing the behavior of model items” presents this use-case building on the general support for look-up customization described below.

The same technique can also be observed in the section called “Customizing User Interaction Behavior”, which discusses customization of user interaction behavior by decorating the look-up of model items. More importantly, this section also lists the type requests that are supported by model item types in the context of user gesture handling.

Example 7.1. Dynamically changing the behavior of model items

void EnableCustomLookupBehavior(IGraph graph, bool enable) {
  // Get the graph's ILookupDecorator that enables manipulation of all look-up 
  // operations for the contained graph elements.
  ILookupDecorator decorator = graph.Get<ILookupDecorator>();
  if (decorator != null) {
    // Test whether decoration of look-up operations initiated on type INode is 
    // supported.
    if (decorator.CanDecorate(typeof (INode))) {
      if (enable) {
        // Prepend a custom look-up provider.
        decorator.AddLookup(typeof (INode), CustomReshapeableChainLink);
      }
      else {
        // Remove the custom look-up provider.
        decorator.RemoveLookup(typeof (INode), CustomReshapeableChainLink);
      }
    }
  }
}

IContextLookupChainLink CustomReshapeableChainLink {
  get { return customReshapeableChainLink; }
}

Following is the single method defined by ILookup. The type that is given as the parameter is used by the object that is queried as the key in a look-up operation. The result of this operation is either an instance that implements the given type or null.

object Lookup(Type type)
Description Provides the means to query the current object (this) for an instance of the given type.

Types that implement the ILookup interface typically support only a set of specific types with their look-up operations. In particular those types are supported that provide well-defined aspects of the object that is queried. For example, when a node is resized or moved, its corresponding INode implementation is queried via the Lookup method to return an IReshapeable instance, which can then be used to change either or both of the node's location and bounds.

Interface IContextLookup defines a similar method which can be used to provide identical functionality for classes that do not implement ILookup:

object Lookup(object item, Type type)
Description Provides the means to query 'item' for an instance of the given type.

In addition to using the Lookup method defined in ILookup, Example 7.2, “Equivalent ways of querying the look-up” presents further equivalent ways how the look-up of an instance can be queried for a specific type.

Example 7.2. Equivalent ways of querying the look-up

// 'item' is a subtype/an implementation of yWorks.Support.ILookup.

// The following statements for querying the look-up for an implementation of 
// type T are equivalent:
// 1) Lookup method defined in interface ILookup.
T result = item.Lookup(typeof (T)) as T;

// 2) Available via .NET extension methods for interface ILookup (and subtypes).
T result = item.Get<T>();

Static class Lookups serves as a factory class that returns a variety of ILookup implementations. Class DictionaryLookup is a convenient default ILookup implementation that is backed by a generic System.Collections.Generic.IDictionary<TKey, TValue>.

Look-up Chaining

Look-up chaining describes the technique of combining an arbitrary number of look-up providers in a daisy-chain manner: implementations of interface IContextLookupChainLink are linked together via their SetNext method.

Whenever such a look-up chain is queried for a specific type and a look-up provider would fail to return a proper instance, instead of returning null, it delegates to its successor in the look-up chain. Thus, the successor's look-up facility is queried for the given type, and so on until either a look-up provider is able to satisfy the request or the last element in the look-up chain returns null.

Most prominently, class DefaultGraph, which presents the default graph structure implementation, uses look-up chaining.

Customizing Look-up Behavior

Look-up chaining paves the way for convenient manipulation of look-up operations, respectively their return values. By simply prepending a look-up provider to an existing look-up chain, default behavior can be easily customized: a prepended chain link element is able to handle specific types while letting pass those that it is not interested in. For the type requests it handles, it can return modified instances that provide custom logic.

Adding a look-up chain link to a model item type, for example, can be seen as dynamically subclassing the model item type in question, but only for the instances used in this context and then overriding just the Lookup(Type) method of the INode, IEdge, ILabel, etc. type. The difference is that the "this" is passed in as a parameter in the Lookup method of the chain link.

The added look-up chain link effectively overrides the implementations present in the look-up of the model item type for a specific type request. Adding more look-up chain links to a type is like subclassing more and more, so the order in which chain links are added matters.

Interface ILookupDecorator defines the functionality that enables manipulating look-up chains related to given types. The following methods of ILookupDecorator can be used to modify a type's look-up chain:

bool CanDecorate(Type t)
Description Determine whether the given type's look-up chain can be decorated.
void AddLookup(Type t, IContextLookupChainLink lookup)
void RemoveLookup(Type t, IContextLookupChainLink lookup)
Description Add/remove a chain link to/from the given type's look-up chain.

Generic utility class LookupDecorator<TDecoratedType, TInterface> enables convenient decoration of look-up chains on top of a given ILookupDecorator. The type parameters specify the context for which a LookupDecorator instance is created: first the type whose look-up chain will be customized, then the interface type in the look-up chain that will be affected. Example 7.3, “Instantiating LookupDecorator using a given ILookupDecorator” presents how to create a LookupDecorator instance for the nodes in a graph.

Example 7.3. Instantiating LookupDecorator using a given ILookupDecorator

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

ILookupDecorator decorator = graph.Get<ILookupDecorator>();
LookupDecorator<INode, IHandleProvider> handleDecorator = 
    new LookupDecorator<INode, IHandleProvider>(decorator, true, false);

The following methods from class LookupDecorator enable instant modifications to the look-up chain of the decorated type:

Most prominently, class DefaultGraph, which presents the default graph structure implementation, uses look-up chaining. It supports decoration of the look-up chains it is maintaining for model item types through an ILookupDecorator implementation that can be requested via its Lookup method as well as through the model item-specific decorators that are made available by the GraphDecorator class.

Example 7.4, “Look-up chain modification using class GraphDecorator” shows an excerpt from the GraphEditorWindow demo application where the GraphDecorator class is used to conveniently decorate the look-up chain of nodes.

Example 7.4. Look-up chain modification using class GraphDecorator

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

var graphDecorator = Graph.GetDecorator();
var nodeDecorator = graphDecorator.NodeDecorator;
// We use the size of the labels for group node insets.
nodeDecorator.InsetsProviderDecorator.SetImplementationWrapper(
    (node, baseImplementation) => 
        new LabelInsetsProvider<INode>(baseImplementation)
);

// Add custom node size constraints that consider labels that are stretched 
// inside the node.
nodeDecorator.SizeConstraintProviderDecorator.SetImplementationWrapper(
    (item, implementation) => 
        new LabelSizeConstraintProvider<INode>(implementation)
);

The tutorial demo applications that present user interaction customization, like, e.g., PositionHandlerWindow, show how interactive behavior of model items in a DefaultGraph can be customized using the services of ILookupDecorator. Further demos that use an ILookupDecorator include: