Feature Providers and Feature Connectors
Any designable object in a Windows Presentation Foundation (WPF) or Silverlight application can expose an arbitrary number of designer extensions. The following topic discusses the capabilities of feature providers, and the role of feature connectors in providing design-time features.
The Basics of Feature Providers and Feature Connectors
A feature provider is a lightweight type that provides additional design-time functionality for an object. Some feature providers may just provide information about sub-components, and require no action on behalf of the designer. Other feature providers may add menu items to the object's designer context menu, create custom adorners for resizing or editing, or redefine dragging and dropping of objects on the designer.
Feature providers are implemented either by deriving directly from the FeatureProvider class, or from a class which is itself derived from FeatureProvider, for example AdornerProvider and ContextMenuProvider. Feature providers are designed to be lightweight, and should not try to save state between instances.
A feature connector manages one or more feature providers for a given object. Feature connectors can publish new services through a service provider and listen for designer events, such as selection changes. Feature connectors are implemented by deriving a new class from the FeatureConnector<TFeatureProviderType> or PolicyDrivenFeatureConnector<TFeatureProviderType> classes.
Feature Providers
A feature provider is intended to be a lightweight point of extensibility. Common scenarios involving designer extensibility favor deriving from existing feature provider classes, instead of creating new feature connectors. Feature providers must not save state, because they are created and destroyed multiple times.
Feature providers are completely managed by feature connectors and are associated to objects through metadata, specifically FeatureAttribute. The feature connector discovers FeatureProvider types from this metadata. The FeatureManager class identifies the required feature connector for every feature provider discovered.
Common feature provider implementations include selection adorners, verbs, and property editors. For more information, see Walkthrough: Creating a Design-time Adorner.
Examples of Feature Providers
The following illustration shows a visual designer and the types of features that might fall in the feature provider or feature connector categories. Note that the illustration is only an example of how some of the features of a designer could be implemented.
The following table lists the feature providers that you can derive from.
Feature Provider |
Description |
---|---|
Adds adorners to the designer's surface. |
|
Adds adorners that are shown for the primary selection. |
|
Defines a set of menu items that are shown in a context menu. |
|
Defines a set of menu items that are shown for the current selection. |
|
PolicyDrivenFeatureConnector<TFeatureProviderType>.ItemFeatureProvider |
Identifies the feature providers that belong to a particular item. |
Add tasks to the active tool. |
|
Provides a set of tasks that are available from the selection tool when a class is in the primary selection. |
|
Captures property changes that are made by the user in the designer and provides new values at design time. |
How Feature Providers are Created at Design Time
In many cases, you will not have to create your own custom feature connectors. You can create a feature provider, and associate it directly with a class using the FeatureConnectorAttribute. The FeatureManager class is the component of the WPF Designer that activates feature providers. FeatureManager examines the metadata of an object as soon as it is placed on the design surface. If it finds a FeatureConnectorAttribute for an object, it creates the associated feature provider.
For example, if you want to create an adorner that allows you to edit a custom control's header text directly on the designer surface, you would implement your adorner by deriving it from PrimarySelectionAdornerProvider, which is itself derived from FeatureProvider. Next, you would associate the custom adorner with your control by using the FeatureConnectorAttribute.
Associating Feature Providers with Types
A design-time tool associates feature providers to types through metadata attributes. There must be a declarative way to bind metadata to the actual runtime objects or instances. This binding is required at the tool-level and is not the responsibility of a design-time framework. The following illustration shows how a design-time tool attaches feature providers to different types.
There are two ways to associate feature providers with types in the WPF Designer architecture: the FeatureAttribute and FeatureConnectorAttribute types. The FeatureAttribute type is used on types or instances of editable objects and exposes a property representing a FeatureProvider.
The FeatureConnectorAttribute decorates a FeatureProvider class and indicates which type of feature connector is required to host the specified feature provider. The FeatureManager class creates the required FeatureConnector<TFeatureProviderType> instances when you change objects that are passed to it.
Consider a WPF-specific designer in which the editable objects are of type UIElement. Such a tool may use one or more "grab handles" to adorn boundaries of individual items. These adorners visually represent selection and to enable manipulation of these editable objects. The designer associates a grab handle feature to all objects of type UIElement by way of metadata. The following illustration shows how a designer can attach a grab handle provider to a UIElement on the design surface.
If a UIElement exists and is passed to the feature manager for analysis, the GrabHandleProvider is discovered through the metadata associated to the UIElement. The GrabHandleProvider type itself, or its base type, is inspected by way of metadata to determine its required feature connector.
Feature Connectors
Feature connectors are the lowest level of extensibility exposed by this architecture. Feature connectors are created given a reference to an EditingContext. Feature connectors can subscribe to global services and can add their own services. The abstract FeatureConnector<TFeatureProviderType> class implements the IDisposable interface, which promotes a simple cleanup strategy.
Feature connectors are demand-created. When the FeatureManager discovers a FeatureConnectorAttribute on a FeatureProvider, it creates the specified FeatureConnector<TFeatureProviderType> if its type does not already exist. Feature connectors are only created when they are discovered, instead of having a designer session create a default set at startup. If there are more than one FeatureConnectorAttribute types for a FeatureProvider, all feature connectors are initialized. This allows a third-party to derive from an existing FeatureProvider, add a custom feature connector, and have both sets of functionality initialized.
Most of the functionality of the FeatureConnector<TFeatureProviderType> class is implemented in the protected CreateFeatureProviders method. Passing an object into this method causes the feature connector to search for FeatureAttribute types on the object. If these attributes are found, the FeatureProvider instance associated with each attribute is created and returned in a list.
The FeatureConnector<TFeatureProviderType> base class is generic and consumes the type of the feature provider that the FeatureConnector<TFeatureProviderType> hosts. Feature providers use a FeatureConnectorAttribute to indicate the associated feature connector. Protected base class methods of FeatureConnector<TFeatureProviderType>, principally the CreateFeatureProviders method, can return typed collections of feature providers to the derived feature connectors without the derived classes being concerned with casting and type-safety.
Creating Custom Feature Connectors
You will have to create your own feature connector when you want to listen to designer events, such as selection. Derive from the FeatureConnector<TFeatureProviderType> or PolicyDrivenFeatureConnector<TFeatureProviderType> classes to implement your custom designer logic.
Feature connectors are supplied a reference to the designer's global EditingContext, through which the feature connector can access designer services, as well as publish its own custom services. For example, a chart control vendor may want to publish a service that allows a custom type editor in the designer to enumerate all of the widgets currently on the chart.
Once you have created a class derived from FeatureConnector<TFeatureProviderType>, you can associate it to a feature provider by applying the FeatureConnectorAttribute to your feature provider. Each time an instance of that feature provider is created, the FeatureManager examines the metadata for the FeatureConnectorAttribute. If the attribute is defined and an instance of the associated FeatureConnector<TFeatureProviderType> is not currently running, the FeatureManager will create it and associate all future instances of that feature provider with the FeatureConnector<TFeatureProviderType>.
For example code that shows how to implement a feature connector see How to: Create a Custom Feature Connector.