For chapter three, I will be introducing a framework from Microsoft called the Managed Extensibility Framework, or MEF (MEF on CodePlex, MEF on MSDN ). If you are not familiar with MEF, you will certainly get more out of this post if you read the MEF overview and programming guide (MEF Overview, MEF Guide).
Now I will give a synopsis of what has changed in my code since the last revision. If you would like to look at my code, please download the attached file: Updated Sample with MEF
First, notice the Imports statements at the top of ContactClass.vb:
Imports System.ComponentModel.Composition
Imports System.ComponentModel.Composition.Hosting
Imports System.ComponentModel.Composition.ImportAttribute
Imports InterfaceLibrary
The top three statements import parts of the MEF library, and the bottom one is for the Interface Library, which we will get to a little later.
These next two methods are where ContactClass utilizes MEF:
'Imports the an Instance of the IAddIn interface.
<Import(AllowDefault:=True, AllowRecomposition:=True)> _
Public IContactAddIn As IAddIn
'Called by the ContactApp class's ContactApp_Load() method; asks if the user wants to run the App in script mode. The method then stores the user's response in a boolean variable,
'and, if the user's answer was yes, the method calls the Connect Method on the IContactAddIn Interface and passes in ContactClass.
Public Sub AppLoad()
UpdateDataset()
Dim MBResult = MsgBox _
("Would you like to run this program from Script?" + _ Environment.NewLine + "(If Yes, the Interface will be hidden.)", _ MsgBoxStyle.YesNo)
Select Case MBResult
Case MsgBoxResult.Yes
ScriptBool = True
Compose()
If Not IContactAddIn Is Nothing Then
IContactAddIn.Connect(Me)
Else
MessageBox.Show("No script to run.")
End If
ContactApp.Close()
Case MsgBoxResult.No
ScriptBool = False
End Select
End Sub
'Creates a Catalog from Assemblies in the AddIns directory, then creates a Container and creates a batch to add parts, and Composes these.
Public Sub Compose()
Dim Catalog As New DirectoryCatalog("AddIns")
Dim Container As New CompositionContainer(Catalog)
Dim Batch As New CompositionBatch()
Batch.AddPart(Me)
Container.Compose(Batch)
End Sub
The top <Import> statement tells MEF that ContactClass wants an IContactAddIn, and the AllowDefault parameter prevents an exception if no Add-In exists. The AllowRecomposition parameter allows MEF to look for an AddIn whenever the container is composed or refreshed.
The Appload method now calls a method called Compose. The Compose method creates a catalog, a container and a batch and composes them. (For a description of these, please see the MEF Overview and the wiki section on Composition Batch).
If MEF finds a suitable AddIn, AppLoad calls its Connect method and passes in ContactClass. If MEF doesn’t find an AddIn, the program closes without issue. Remember that the program only uses an AddIn if the user selects to run the program in script mode.
You may be wondering how the App and the AddIn communicate. The answer is found in the Interface Library:
'The Interface for ContactClass.
Public Interface IContactClass
Property ContactArray()
Delegate Sub ContactEventHandler(ByVal ca As IContactEventArgs)
Delegate Sub ActionEventHandler()
Event AddedContact As ContactEventHandler
Event DeletedContact As ContactEventHandler
Event EditedContact As ContactEventHandler
Event ContactsPrinted As ActionEventHandler
Event AddError As ContactEventHandler
Event DeleteError As ContactEventHandler
Event EditOldError As ContactEventHandler
Event EditNewError As ContactEventHandler
Event ContactsPrintedError As ActionEventHandler
Function CreateNewContactObj() As IContactObj
Sub AddContact(ByVal Contact As IContactObj)
Sub DeleteContact(ByVal Contact As IContactObj)
Sub EditContact(ByVal OldContact As IContactObj, _
ByVal NewContact As IContactObj)
Sub PrintList()
End Interface
'The interface for the ContactEventArgs in ContactClass.
Public Interface IContactEventArgs
Property Contact() As IContactObj
End Interface
'The Interface for ContactObj.
Public Interface IContactObj
Property FirstName()
Property LastName()
Property HomePhone()
Property CellPhone()
Property WorkPhone()
Property Email()
End Interface
'The Interface for the AddIn.
Public Interface IAddIn
Sub Connect(ByRef TheContact As IContactClass)
End Interface
The Interface Library provides the language with which the App and the Add-In speak to each other. It acts as a blueprint for what is contained in the App and the AddIn, and allows them both to communicate without holding design-time references to each other.
Now let’s look at the AddIn itself.
Imports System.IO
Imports System.ComponentModel.Composition
Imports InterfaceLibrary
'This class is only used when the user runs the App in script mode.
<Export(GetType(IAddIn))> Public Class ContactAuto
Implements IAddIn
Private MyContactClass As IContactClass
Private MyArray As Array
Private stwr As New StreamWriter("Log.txt", True)
Private Event AllContactsNormalized()
Private Event ContactsNormalizedError()
'Gets an instance of ContactClass and creates an instance of an array that is in that class. It also creates the log file.
Public Sub Connect(ByRef TheContact As IContactClass) Implements IAddIn.Connect
MyContactClass = TheContact
MyArray = MyContactClass.ContactArray
stwr.AutoFlush = True
Run()
End Sub
Notice the Export statement, and how its specified type matches that of the Import statement in ContactClass. When MEF looks in the AddIns folder, it will add this to the catalog, and the container will match it with the Import statement in ContactClass.
Here is a diagram of how the whole Application works:

ContactAuto references the Interface Library, as does ContactClass, but The Interface Library does not reference either of them,. If ContactAuto exists, ContactClass calls its Connect method, which it knows about through the Interface library.
This design allows the AddIn to be free of the Application, and allows a programmer to alter the AddIn without altering the main program. All any new Add-Ins would have to do is reside in the right folder, have a Connect method, and specify an export statement. MEF provides this excellent way of discovering AddIns, and allows for a great amount of Application extensibility. There is a possibility for MEF use in a VSTA integration, so I would encourage you to experiment with this new concept.
Posted
Jul 02 2009, 01:20 PM
by
BillL