Skip to content

Creating MEF Components

gstaas edited this page Oct 22, 2014 · 3 revisions

Table of Contents

Components are a fine way to add capabilities to your applications, taking advantage of the modularity that MEF components offer. Many of the ATF samples create components that perform special functions, as seen in ATF Samples Components.

In some cases, adding a component is the easiest or only way to interface with an existing ATF component. For instance, if your application uses the ATF StandardFileCommands component, you need to provide code that exports the IDocumentClient interface. The most straightforward way to do this is to follow the example of the ATF samples and create a component that implements and exports IDocumentClient. The ATF DOM Tree Editor Sample imports StandardFileCommands and creates an Editor component that implements IDocumentClient.

This section describes what you need to provide in a component that works with ATF. You can also look at code for some of the many components in ATF and the ATF samples. For some key components in ATF, see Important ATF Components.

Class Requirements

Your component is a C# class that is decorated with attributes so that components can discover each other.

For instance, here's the class declaration for StandardFileCommands:

public class StandardFileCommands : IDocumentService, ICommandClient, IInitializable

Your class should be public.

Class Decorators

Here are the decorators for the StandardFileCommands class:

[Export(typeof(IDocumentService))]
[Export(typeof(StandardFileCommands))]
[Export(typeof(IInitializable))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class StandardFileCommands : IDocumentService, ICommandClient, IInitializable

Note that there are several [Export(typeof(xxx))] attributes. ATF components often provide a variety of export attributes on a component, one for each important interface that the component implements, and sometimes for whatever base classes there might be, too. This is a good model to follow. Try to anticipate how client code might use the component, so provide more exports than your own code imports.

Exports fall in several categories:

  • Implemented Interfaces ([Export(typeof(IDocumentService))]). You may want to export whatever important interfaces the component implements.
  • Base classes ([Export(typeof(StandardFileCommands))]). This includes the class itself and perhaps others.
  • IInitializable ([Export(typeof(IInitializable))]). Export this if the component needs initialization. For more details, see Component Initialization
Your class must have at least one export.

Component Initialization

Your component may need to finish its initialization after it has been instantiated or MEF has completed composition of the components. This is often the case when the component depends on other components.

If the component requires initialization that its constructor cannot perform, you should implement the IInitializable interface. You must also export it, decorating the class:

[Export(typeof(IInitializable))]

Place the necessary initialization code in the implementation of the IInitializable.Initialize() method in the component.

Note that an application's MEF set up code must call the InitializeAll() extension method of CompositionContainer to actually perform this initialization.

For more information, see Initializing Components.

Part Creation Policy

How many instances of the component need to be created? Generally, only one is needed, shared between the importers, so the creation policy should be CreationPolicy.Shared, which is what nearly all ATF components use. If a component is required for each importer, use CreationPolicy.NonShared. If the part can be shared or not, use CreationPolicy.Any; this is used when no policy is specified.

Imports

If your component depends on other components, you need to import items.

Sometimes you can import at least some of what you need in the constructor, as in this example from StandardFileCommands:

[ImportingConstructor]
public StandardFileCommands(
    ICommandService commandService,
    IDocumentRegistry documentRegistry,
    IFileDialogService fileDialogService)

Recall that using [ImportingConstructor] imports each constructor parameter. Create the appropriate fields for the imported parameters' values. For more on this constructor, see ImportingConstructor.

You can also import directly into a field if it is not a constructor parameter.

Be sure to use [ImportMany] on some kind of collection variable if you can import several instances of something, such as an IDocumentClient interface, one for each document client the application supports.

Deriving from Components

You can create a component by deriving from an existing component and supplying a similar set of Export attributes.

You can also create a component that derives from another class. A common example of deriving a component from a class is used by many samples in creating a schema loader component derived from XmlSchemaTypeLoader:

[Export(typeof(SchemaLoader))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class SchemaLoader : XmlSchemaTypeLoader

Topics in this section

Clone this wiki locally