tags:

views:

432

answers:

2

Edit: Matt, that does indeed solves some (most) of my problems, thank you. Now the only lingering issue of how do I do this in WPF? I have a custom part based off of a UserControl but there is no way in WPF to do :

[Import]<my:SomeCustomControl>

so the cascade doesn't work in this instance.

/Edit


I am having an issue [Import]ing various MEF components in my project. Do I have to use a CompositionContainer in every class I use? In the code below, a null reference exception is thrown in the method Helper.TimesTwo() but when I call logger.Log() in the Program class, everything works. Any help would be greatly appreciated.

(this will compile and run as a console app).

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var p = new Program();
            p.Run();
        }

        [Import]
        private ILog logger { get; set; }

        public void Run()
        {
            var catalog = new DirectoryCatalog(".");
            var container = new CompositionContainer(catalog);
            var batch = new CompositionBatch();
            batch.AddPart(this);
            container.Compose(batch);

            logger.Log("hello");
            var h = new Helper();
            logger.Log(h.TimesTwo(15).ToString());
            Console.ReadKey();
        }
    }

    class Helper
    {
        [Import]
        private IDouble doubler { get; set; }

        private Helper()
        {
            // do I have to do all the work with CompositionContainer here again?
        }

        public double TimesTwo(double d)
        {
            return doubler.DoubleIt(d);
        }
    }

    interface ILog
    {
        void Log(string message);
    }

    [Export(typeof(ILog))]
    class MyLog : ILog
    {
        public void Log(string message)
        {
            Console.WriteLine("mylog: " + message);
        }
    }

    interface IDouble
    {
        double DoubleIt(double d);
    }

    [Export(typeof(IDouble))]
    class MyDoubler : IDouble
    {
        public double DoubleIt(double d)
        {
            return d * 2.0;
        }
    }
}
+4  A: 

I think the trick is to make use of the fact that MEF will cascade its imports. So if you import your Helper instance rather than declaring it as a local variable, any imports that the Helper requires will be satisfied.

    [Import]
    public Helper MyHelper { get; set; }

    public void Run()
    {
        var catalog = new DirectoryCatalog(".");
        var container = new CompositionContainer(catalog);
        var batch = new CompositionBatch();
        batch.AddPart(this);
        container.Compose(batch);
        logger.Log("hello");
        logger.Log(MyHelper.TimesTwo(15).ToString());
        Console.ReadKey();
    }

I'm sure there's a way to have it satisfy any imports in a local variable, but I like using the "cascaded imports" feature like that.

Matt Hamilton
+1  A: 

No you can't do that. You could look into using attached properties though for this. With an attached property you can have the container compose the element that the attached property is added to. Another option would be markup extensions.

Glenn

Glenn Block
I wondered if you'd seen this, Glenn, given your recent spate of tweets about MEF+WPF. :)
Matt Hamilton
I have the same problem, that I cannot [Import] in a class other than the MainWindow... but the answer here seems to be an answer to something else?
TimothyP