Table of Contents
This chapter introduces some concepts used with the yFiles WPF Viewer types. These include purpose-specific, i.e., graph model-related topics as well as practical programming aspects.
Interface ILookup
lays the
foundation for a lean yet powerful look-up mechanism that is used with a number
of yFiles WPF 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 WPF 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.
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 5.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 5.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 5.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 5.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 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.
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) |
|
| 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 5.3, “Instantiating LookupDecorator using a given ILookupDecorator” presents how to create a
LookupDecorator instance for the nodes in a graph.
Example 5.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 5.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 5.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:
|
Copyright ©2008-2011, yWorks GmbH. All rights reserved. |