views:

85

answers:

2

I am interested in implementing an IoC container in my project but I haven't seen an example out there that does what I need.

Here is the situation, my application is built in WPF and uses the MVVM pattern to create a player for an infrared video format. In this format, each video is actually made up of multiple "subframes" (think of it as capturing at multiple shutter speeds simultaneously in order to increase the dynamic range of the data) that are each displayed in a TabControl. I would like to use an IoC container to help me create the ViewModels for each of the tabs, but I have two concerns.

  1. I need a way to pass in the object that represents the subframe I'm creating a ViewModel for so at least one of the ViewModel's dependencies can't be created by the IoC container because it already exists.

  2. I have user controls inside the view for a subframe that have their own ViewModels so those ViewModels need to be created by the IoC container as well. The problem with this is that while the controls (and their ViewModels) have separate concerns, they are not completely indpendent so they use some coordination objects. Unfortunately in all the examples I've seen you can either have your IoC container create a new instance of a dependency or have a singleton, but what I need is a single instance during the controlled scope of time when I'm creating the subframe ViewModel.

That's a lot of text so here's some code that shows what I'm doing and what I'd like to do.

What I have now

In the open a movie code:

foreach (var subframe in movieFile)
{
    // Subframes is the ObservableCollection that the TabControl is bound to
    Subframes.Add(new SubframeViewModel(subframe));
}

In the SubframeViewModel:

public SubframeViewModel(ISubframe subframe)
{
    _subframe = subframe;

    // FrameController tracks the current frame number and fires an event 
    // when it changes
    var frameController = new FrameController(subframe); 

    // ImageViewModel represents the actual movie image. Uses FrameController
    // to know when to raise PropertyChanged for the property that represents
    // the image
    ImageViewModel = new ImageViewModel(subframe, frameController);

    // FrameControlViewModel represents the playback controls.  Uses
    // FrameController to implement actions
    // Play, Pause, Jump to frame, Jump to time...
    FrameControlViewModel = new FrameControlViewModel(subframe, frameController);
}

What I would like to have without changing the existing sematics

In the open a movie code:

foreach (var subframe in movieFile)
{
    Subframes.Add(container.Resolve<SubframeViewModel>());
}

In the SubframeViewModel:

public SubframeViewModel(ISubframe subframe, ImageViewModel imageModel, 
                            FrameControlViewModel frameModel)
{
    _subframe = subframe;

    ImageViewModel = imageModel;

    FrameControlViewModel = frameModel;
}

In reality, there are more coordination and ViewModel objects involved but the patterns are the same. That said, I think you can see why I'm interested in an IoC container here.

I think my scenario should be rather common but I'm not sure and I don't want to waste my time trying to fit a square peg into a round hole so here are my questions. Can any / all IoC containers do this? If not, can you point me towards a refactoring that would improve my code and make IoC work?

+1  A: 

Hi there,

Autofac definitely does what you require - parameters provided explicitly can be combined with those autowired by the container:

vm = container.Resolve<SubFrameViewModel>(new NamedParameter("subframe", subframe));

(Matching by type rather than name is also possible.)

You can even have the container inject a Func or custom delegate into the calling component, so that the container dependency (i.e. the call to Resolve) is unnecessary:

Func<ISubFrame, SubFrameViewModel> _vmFactory; // Injected
vm = _vmFactory(subframe);

See http://code.google.com/p/autofac/wiki/DelegateFactories for info on the latter.

Nick

Nicholas Blumhardt
Hmm might have misinterpreted your question on a second reading - HTH anyway!
Nicholas Blumhardt
Whether or not this completely answers all of my questions, now I'm pointed in the right direction. I can definitely see how I can make everything work more cleanly with this. Thanks for the help.
Jon Norton
Looking at the Autofac docs, it looks to me like if I created a nested container for each subframe and mark FrameController as ContainerScoped, all I should need to do is use what you have there to specify the subframe and all my requirements are met. Does that seem like a reasonable thing to do?
Jon Norton
Sounds entirely reasonable, all the best with it.
Nicholas Blumhardt
+1  A: 

It looks like you want to handle the extra constructor parameters in your IoC scenario, and I see two options:

1 - Creating a simple constructor which takes in a subframe, and expose the ImageViewModel and FrameControlViewModel as public settable properties like so:

// assumes frameControlVM and imageVM have been constructed and are valid
foreach (var subframe in movieFile)
{
    var subframeVM = container.Resolve<SubframeViewModel>(subframe);
    subframeVM.ImageVM = imageVM;
    subframeVM.FrameControlVM = frameControlVM ;
    Subframes.Add(subframeVM);
}

2 - Pass the arguments you need into the IoC container, which simply passes the parameters into the SubframeVM constructor:

// assumes frameControlVM and imageVM have been constructed and are valid
foreach (var subframe in movieFile)
{
    var subframeVM = container.Resolve<SubframeViewModel>(subframe, imageVM, frameControlVM);
    Subframes.Add(subframeVM);
}

Ultimately which one you choose depends on how tightly coupled you want your SubframeViewModel IoC resolver to be with the rest of your VMs. If your imageVM and frameControlVMs need IoCs of their own, you can simply chain them together:

// assumes frameControlVM and imageVM have been constructed and are valid
foreach (var subframe in movieFile)
{
    var frameControlVM = container.Resolve<FrameControlViewModel >(subframe);
    var imageVM = container.Resolve<ImageViewModel>(subframe, frameControlVM);
    var subframeVM = container.Resolve<SubframeViewModel>(subframe, imageVM, frameControlVM);
    Subframes.Add(subframeVM);
}
micahtan
Thanks Micah, that really helps. Which IoC container are you using here?
Jon Norton
I don't. :)I rolled a custom one a few years ago when I needed to do also do dynamic assembly loading in addition to factory-style class instantiation.However, for most small-ish projects that I do, the benefit of pluggable instantiation via external config is less valuable to me than compile-time checking -- and I don't like to guess at interfaces and abstract classes ahead of time; that's something I save for refactoring.
micahtan