I have built a modular program using
http://www.codeproject.com/Articles/258681/Windows-Forms-Modular-App-using-MEF as a base and I have several of my modules working.
It is a MDI Windows Forms application and I need to call back to the host module for some stuff.
a) Location information for the MDI host window
b) Write to the status bar in the host window.
I have managed to get the application to compile but when I call the host functions it always gives me a null exception
I did look at Using MEF with C#, how do I call methods on the host, from the plugin?
which is where I got my line
public Exec.Core.Interfaces.IHost Host;
But host is always null so I get exceptions trying to access the members of MDIForm which is the host.
Even if I do public Exec.Core.Interfaces.IHost Host {get;set;}
This is the Host.
NameSpace Exec
{
[Export(typeof(Exec.Core.Interfaces.IHost))]
// MDIForm is the host.
public partial class MDIForm : Form, Exec.Core.Interfaces.IHost
{
///other stuff not related to the problem
// defined in public interface IHost
public Point myLocation()
{
return this.Location; // need the window location
}
// defined in public interface IHost
public IHost GetHost()
{ // is this what GetHost Should Return? Not sure
return this;
}
// defined in public interface IHost
public void SendMessage(string message)
{
SetStatusBar(message); // print a message to MDIForm status bar
}
}
}
Then is the IHosts.cs
namespace Exec.Core.Interfaces
{
public interface IHost
{
IHost GetHost();
void SendMessage(string message);
Point myLocation();
// MDIForm GetThis( ); /* this gives error. Can't resolve MDIForm
I don't know why and can't resolve.*/
}
}
This is one of the modules where I am trying to get stuff from the host
namespace Exec.Modules.Tasks
{
[Export]
public partial class frmTasks : Form
{
[Import(typeof (Exec.Core.Interfaces.IHost))]
public Exec.Core.Interfaces.IHost Host;
// unfortunately Host == NULL at this point
private void SendMessage (string message)
{
try
{
Host.SendMessage(message); <Throws System.NullReferenceException
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private IHost WhichHost()
{
try
{ /// not really sure what will be returned here
return GetHost();<Throws System.NullReferenceException
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private Point Location()
{
try
{
return mylocation(); <Throws System.NullReferenceException
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
And finally this is how I put all of the objects together in ModuleHandler.cs This is pretty much taken from the codeproject above with some seperation of some method calls into 2 pieces so I could see why it was dying.
namespace Exec.Core
{
[Export(typeof(IModuleHandler))]
public class ModuleHandler : IDisposable, IModuleHandler
{
[ImportMany(typeof(IModule), AllowRecomposition = true)]
// The ModuleList will be filled with the imported modules
public List<Lazy<IModule, IModuleAttribute>> ModuleList
{ get; set; }
[ImportMany(typeof(IMenu), AllowRecomposition = true)]
// The MenuList will be filled with the imported Menus
public List<Lazy<IMenu, IModuleAttribute>> MenuList { get; set; }
[Import(typeof(IHost))]
// The imported host form
public IHost Host { get; set; }
AggregateCatalog catalog = new AggregateCatalog();
public void InitializeModules()
{
// Create a new instance of ModuleList
ModuleList = new List<Lazy<IModule, IModuleAttribute>>();
// Create a new instance of MenuList
MenuList = new List<Lazy<IMenu, IModuleAttribute>>();
// Foreach path in the main app App.Config
foreach (var s in ConfigurationManager.AppSettings.AllKeys)
{
if (s.StartsWith("Path"))
{
// Create a new DirectoryCatalog with the path loaded from the App.Config
DirectoryCatalog cataloglist = new DirectoryCatalog(ConfigurationManager.AppSettings[s], "jobexe*.dll");
catalog.Catalogs.Add(cataloglist);
}
}
// Create a new catalog from the main app, to get the Host
catalog.Catalogs.Add( new AssemblyCatalog(System.Reflection.Assembly.GetCallingAssembly()));
// Create a new catalog from the ModularWinApp.Core
DirectoryCatalog catalogExecAssembly = new DirectoryCatalog( System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location ), "exe*.dll");
catalog.Catalogs.Add(catalogExecAssembly);
// Create the CompositionContainer
CompositionContainer cc = new CompositionContainer(catalog);
try
{
cc.ComposeParts(this);
}
catch (ReflectionTypeLoadException e)
{ MessageBox.Show(e.ToString()); }
catch (ChangeRejectedException e)
{ MessageBox.Show(e.ToString()); }
}
}
}
So again, the modules work independently but are unable to call back to the host. Wondering what I am doing wrong.
Thanks in advance for any help
One final thing that may have something to do with the issue.
Here is the code that starts the program
public static ModuleHandler _modHandler = new ModuleHandler();
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Initialize the modules. Now the modules will be loaded.
_modHandler.InitializeModules();
// this goes straight to class MDIForm() constructor
Application.Run(_modHandler.Host as Form);
}
Colin