Skip to content

Control Adapters

Gary edited this page Aug 27, 2014 · 1 revision

Table of Contents

Control adapters give you an easy way to add abilities to certain controls without changing the control at all. ATF has a large number of control adapters performing a variety of functions that you can use on your controls.

These adapters and their interfaces are in the Sce.Atf.Controls.Adaptable namespace.

Using Control Adapters

To understand how control adapters work, consider how they are used.

Control adapters are used on adaptable controls, which derive from AdaptableControl. ATF provides two: AdaptableControl itself and D2dAdaptableControl, which is the one that is most used, because many of the non-Direct2D classes are deprecated.

Consider this simple example from the Editor class's constructor in ATF Circuit Editor Sample. It sets up a control that is displayed when the cursor hovers over a subcircuit in a circuit:

m_d2dHoverControl = new D2dAdaptableControl();
m_d2dHoverControl.Dock = DockStyle.Fill;
var xformAdapter = new TransformAdapter();
xformAdapter.EnforceConstraints = false;//to allow the canvas to be panned to view negative coordinates
m_d2dHoverControl.Adapt(xformAdapter,
    new D2dGraphAdapter<Module, Connection, ICircuitPin>(m_circuitRenderer, xformAdapter));

After the D2dAdaptableControl is constructed and configured, the control adapter TransformAdapter is created to provide transform information about the control. Next, AdaptableControl.Adapt() is called with a list of the adapters to add to the control. This adds the TransformAdapter plus a D2dGraphAdapter constructed inside the Adapt() call. This is all you need to do to get the benefits of the adapters for the control.

For further discussion of adaptable controls and control adapters, see Adaptable Controls.

ControlAdapter Class

Control adapters all derive directly or indirectly from the abstract ControlAdapter class, which is never used as a control adapter itself. ControlAdapter implements IControlAdapter, which contains the following property and methods:

  • AdaptedControl: Get the adapted control.
  • Bind(AdaptableControl control): Bind the adapter to the adaptable control. It is called in the order that the adapters were defined on the control.
  • BindReverse(AdaptableControl control): Bind the adapter to the adaptable control. It is called in the reverse order that the adapters were defined on the control.
  • Unbind(AdaptableControl control): Unbind the adapter from the adaptable control.
Control adapters override these methods.

Note that the binding related methods get the control they are binding to as a parameter, so it is available for the AdaptedControl property. Binding occurs when AdaptableControl.Adapt() is called. What binding accomplishes is discussed in the next section.

AdaptableControl Class

AdaptableControl augments Control, from which it derives. It has events before and after when adapters are adapted. It also overrides the base Control OnMouseDown() and OnMouseMove() methods. It overrides the WndProc() method so that it can call its overridden OnPainting() method.

Adapt Method

The main thing the Adapt() method does is bind the control adapters to the control:

public void Adapt(params IControlAdapter[] adapters)
{
    OnAdapting(EventArgs.Empty);
    Adapting.Raise(this, EventArgs.Empty);

    // allow existing adapters to detach from context
    Context = null;

    foreach (IControlAdapter adapter in m_adapters)
    {
        adapter.Unbind(this);
        Dispose(adapter);
    }
    m_adapters.Clear();
    m_adapterCache.Clear();
    m_contextAdapterPreferred.Clear();

    m_adapters.AddRange(adapters);

    foreach (IControlAdapter adapter in m_adapters)
    {
        adapter.Bind(this);
    }

    for (int i = m_adapters.Count - 1; i >= 0; i--)
    {
        m_adapters[i].BindReverse(this);
    }

    OnAdapted(EventArgs.Empty);
    Adapted.Raise(this, EventArgs.Empty);

    Invalidate();
}

The first section of this method removes any previous binding, calling Unbind() for each control adapter. It calls Bind() for each adapter in the order they were specified, and then calls BindReverse() for each adapter in the reverse order specified.

The desired order depends on whether the control adapter handles painting or mouse events. For painting events, the order is bottom up, because a higher layer covers lower layers. For mouse events, the order is top up, because higher layers handle events before layers below them. Typically, a painting type adapter overrides only the Bind() method, while a mouse event adapter overrides only the BindReverse() method. It's just a matter of convention, however. The benefit of offering both Bind() and BindReverse() that are called in reverse orders is that you can put both kinds of control adapters in a single list and order them the way you want. For instance, you could put all the painting control adapters first in the list in the order that you want, followed by the mouse control adapters in their desired order.

AdaptableControl Adapter Methods

The ControlAdapters property gets a list of all the control adapters in their defined order.

Control adapters are also retrieved and handled by these methods:

  • T As<T>(): Get a control adapter of the specified type, or null if none is available.
  • T Cast<T>(): Get a control adapter of the specified type, throwing an exception if none is available.
  • bool Is<T>(): Return whether the control has the specified control adapter of type T.
  • IEnumerable<T> AsAll<T>() where T : class: Get all control adapters of the specified type, or null if none.
These convenience methods are implemented using utility private methods, like As(), that looks through the adapter list to find what's desired:
private object As(Type type)
{
    // try cache
    object adapter;
    if (m_adapterCache.TryGetValue(type, out adapter))
        return adapter;

    // try adapters (from top to bottom)
    for (int i = m_adapters.Count - 1; i >= 0; i--)
    {
        adapter = m_adapters[i];
        if (type.IsAssignableFrom(adapter.GetType()))
        {
            m_adapterCache.Add(type, adapter);
            return adapter;
        }
    }

    // finally, try the adaptable control itself
    if (type.IsAssignableFrom(GetType()))
    {
        m_adapterCache.Add(type, this);
        return this;
    }

    return null;
}

The m_adapterCache Dictionary<Type, object> caches the adapters for quick access by type. If not yet cached, the method goes through the list and uses the IsAssignableFrom() method previously mentioned to find a suitable adapter. Failing this, it tries the control itself, and returns what it finds.

AdaptableControl Context Methods

Adaptable controls have a Context property so their users can specify a desired context for using the control. For instance, the Editor class in ATF Circuit Editor Sample sets a context:

control.Context = editingContext;

In addition, a preferred adapter for a context can be specified with the RegisterContextAdapter(Type type, object adapter) method. An adapter for a specified context can be obtained with these methods:

  • T ContextAs<T>(): Get an adapter to the context of the given type, or null if none is available.
  • T ContextCast<T>(): Get an adapter to the context of the given type. If none is available, throw an AdaptationException.
  • bool ContextIs<T>(): Return whether the context can be adapted to the given type.

Control Adapter Examples

Consider MouseWheelManipulator, a simple adapter that scales the adaptable control viewing area when the user turns the mouse wheel. The constructor for this adapter takes an ITransformAdapter:

public MouseWheelManipulator(ITransformAdapter transformAdapter)

MouseWheelManipulator's binding methods are:

protected override void BindReverse(AdaptableControl control)
{
    control.MouseWheel += control_MouseWheel;
}
...
protected override void Unbind(AdaptableControl control)
{
    control.MouseWheel -= control_MouseWheel;
}

BindReverse() simply subscribes to the MouseWheel event; Unbind() unsubscribes. Note that MouseWheelManipulator does not override IControlAdapter.Bind(): control adapters typically override one but not both. This is a mouse event adapter, so it overrides BindReverse() rather than Bind().

The event handler control_MouseWheel changes the view's scale by manipulating the ITransformAdapter implementer it was given in its constructor.

TransformAdapter implements ITransformAdapter and is a special case adapter. It does not override either Bind() or BindReverse(), but is instead used by other adapters, such as MouseWheelManipulator. Note that an adapter does not need to have an ITransformAdapter given in its constructor to access it. It can obtain it from the AdaptableControl with adapter methods, as D2dAnnotationAdapter does:

m_transformAdapter = control.As<ITransformAdapter>();

For details on these adapter methods like As(), see AdaptableControl Adapter Methods.

For an example of a painting control adapter, consider CanvasAdapter, which adjusts the viewing window when its bounds change. It has a Bind() override, rather than BindReverse:

protected override void Bind(AdaptableControl control)
{
    m_windowBounds = control.ClientRectangle;

    m_transformAdapter = control.As<ITransformAdapter>();
    if (m_transformAdapter != null)
        m_transformAdapter.TransformChanged += transformAdapter_TransformChanged;

    control.ClientSizeChanged += control_ClientSizeChanged;
}

This method, too, obtains an ITransformAdapter using AdaptableControl.As(). As would be expected, it subscribes to the ClientSizeChanged event; its Unbind unsubscribes.

Control Adapter Interfaces

ATF provides a set of interfaces control adapters can use for various purposes. This table describes these interfaces and lists the control adapters that use them.

Control adapter interface Description Used by control adapters
IAutoTranslateAdapter For auto-translate adapters, which track the mouse when enabled and adjust an ITransformAdapter. AutoTranslateAdapter, D2dAnnotationAdapter, D2dGraphEdgeEditAdapter, D2dGraphNodeEditAdapter, GraphEdgeEditAdapter, MouseLayoutManipulator, RectangleDragSelector
ICanvasAdapter For adapters that define a rectangular canvas, with a rectangular window for viewing it. CoordinateAxisAdapter, ScrollbarAdapter
IDragSelector For adapters that select objects by dragging. Uses DragSelectionEventArgs for drag selection event data. RectangleDragSelector, SelectionAdapter
IItemDragAdapter For control adapters that can drag items. This interface provides a mechanism for any control adapter that can drag items to initiate drags of items in other layers at the same time. This way, all selected items may be dragged together even if they are managed by different adapter layers. D2dAnnotationAdapter, D2dGraphNodeEditAdapter, SelectionAdapter
ILabelEditAdapter For adapters that can do label editing operations. LabelEditAdapter
IPickingAdapter2 For control adapters that perform picking. Note: Use this instead of the obsolete IPickingAdapter. ContextMenuAdapter, D2dAnnotationAdapter, D2dGraphAdapter, KeyboardGraphNavigator, KeyboardIOGraphNavigator, HoverAdapter, SelectionAdapter, ViewingAdapter
ISelectionAdapter For control adapters that perform selection. LabelEditAdapter, SelectionAdapter
ITransformAdapter For adapters that define a transform for the adapted control consisting of a scale, followed by a translation. The TransformAdapters class adds extension methods on ITransformAdapter to transform coordinates and so on. AutoTranslateAdapter, CanvasAdapter, CoordinateAxisAdapter, D2dAnnotationAdapter, D2dGridAdapter, D2dGraphAdapter, D2dGraphEdgeEditAdapter, D2dGraphNodeEditAdapter, D2dSubgraphAdapter, KeyboardGraphNavigator, KeyboardIOGraphNavigator, LabelEditAdapter, MouseLayoutManipulator, MouseTransformManipulator, MouseWheelManipulator, RectangleDragSelector, ScrollbarAdapter, TransformAdapter, ViewingAdapter

Control Adapter Descriptions

The table lists the control adapters, what they derive from (if not ControlAdapter) and implement, and their description.

Some of these adapters, such as D2dGraphEdgeEditAdapter, operate on graphs in an adaptable control. For more information on graphs, see Graphs in ATF. Also see the discussions of samples that use graphs, such as Circuit Editor Programming Discussion.

Control adapter Derived from, Implements Description
AutoTranslateAdapter IAutoTranslateAdapter, IDisposable Auto-translate adapter that tracks the mouse when enabled and adjusts an ITransformAdapter.
CanvasAdapter ICanvasAdapter, ILayoutConstraint Canvas adapter that defines the canvas bounds and visible window size.
ContextMenuAdapter Adapter to run a context menu on a MouseUp event with the right button.
CoordinateAxisAdapter Adapter that renders horizontal and vertical coordinate axes.
D2dAnnotationAdapter DraggingControlAdapter
IPickingAdapter2, IItemDragAdapter, IDisposable
Adapter that allows displaying and editing diagram annotations (comments).
D2dGraphAdapter IPickingAdapter2, IDisposable Adapter to reference and render a graph diagram. Also provides hit testing with the Pick() method, and viewing support with the Frame() and EnsureVisible() methods.
D2dGraphEdgeEditAdapter DraggingControlAdapter Adapter that adds graph edge dragging capabilities with a graph adapter.
D2dGraphNodeEditAdapter DraggingControlAdapter
IItemDragAdapter
Adapter that adds graph node dragging capabilities with a graph adapter. The Shift key can be pressed to constrain dragging to be parallel to either the x-axis or y-axis.
D2dGridAdapter ILayoutConstraint Adapter to draw a grid on a diagram and to perform layout constraints using that grid.
D2dSubgraphAdapter D2dGraphAdapter Control adapter to reference and render a subgraph diagram. Also provides hit testing with the Pick() method, and viewing support with the Frame() and EnsureVisible() methods.
DragDropAdapter Adapter to add drag and drop support.
DraggingControlAdapter Abstract base class for control adapters that drag. It tracks the mouse and handles the logic of determining when the mouse has moved enough to be considered a drag.
GroupPinEditor DraggingControlAdapter
IItemDragAdapter, IDisposable
Adapter for adding floating group pin location and label editing capabilities to a subgraph control.
HoverAdapter IDisposable Adapter that uses an IPickingAdapter and a timer to generate events when the user hovers over items. For a discussion of how to use this adapter, see Hover Adapter.
KeyboardGraphNavigator Allows for navigating a graph using the arrow keys. It requires that the adaptable control's context be adaptable to ISelectionContext and IGraph. Optionally, this context should be adaptable to IViewingContext. It requires that the adaptable control can be adapted to IPickingAdapter2. Consider using KeyboardIOGraphNavigator instead, if the graph has inputs and outputs on specific sides of the nodes.
KeyboardIOGraphNavigator Allows for navigating an "input-output" graph using the arrow keys. This kind of graph has inputs on only one side of a node and outputs on the other side. It requires that the adaptable control's context be adaptable to ISelectionContext and IGraph. Optionally, this context should be adaptable to IViewingContext. It requires that the adaptable control can be adapted to IPickingAdapter2.
LabelEditAdapter ILabelEditAdapter, IDisposable Adapter for adding in-place label editing.
MouseLayoutManipulator DraggingControlAdapter Adapter to add a layout adorner to complex states, such as marking states as selected.
MouseTransformManipulator Adapter that converts mouse drags into translation and scaling of the adapted control using an ITransformAdapter.
MouseWheelManipulator Adapter that converts mouse wheel rotation into scaling the adapted control using an ITransformAdapter.
RectangleDragSelector DraggingControlAdapter
IDragSelector
Adapter that allows a user to drag-select rectangular regions on the adapted control to modify the selection. It uses DragSelectionEventArgs for drag selection event data.
ScrollbarAdapter Adapter that adds horizontal and vertical scrollbars to the adapted control. It requires an ITransformAdapter and ICanvasAdapter.
SelectionAdapter ISelectionAdapter, IItemDragAdapter, ISelectionPathProvider Adapter that adds mouse click and drag selection to the adapted control. The associated context must be convertible to ISelectionContext. It uses DragSelectionEventArgs for drag selection event data.
TransformAdapter ITransformAdapter Adapter that defines a transform for the adapted control consisting of a scale followed by a translation. This transform affects how the adapted control is viewed. The TransformAdapters class adds extension methods on ITransformAdapter to transform coordinates and so on.
ViewingAdapter IViewingContext Adapter that defines a viewing context on the adapted control. It requires an ITransformAdapter.

The following control adapters are considered obsolete and are replaced by the given Direct2D classes.

Obsolete control adapter Derived from, Implements Direct2D version to use
AnnotationAdapter DraggingControlAdapter
IPickingAdapter, IPrintingAdapter, IItemDragAdapter, IDisposable
D2dAnnotationAdapter
GraphAdapter IGraphAdapter, IPickingAdapter, IPrintingAdapter, IDisposable D2dGraphAdapter
GraphEdgeEditAdapter DraggingControlAdapter D2dGraphEdgeEditAdapter
GraphNodeEditAdapter DraggingControlAdapter
IItemDragAdapter
D2dGraphNodeEditAdapter
GridAdapter ILayoutConstraint D2dGridAdapter

Topics in this section

Clone this wiki locally