1
votes

I have a scenario where I would like to register a single instance of a component in the container, but unfortunately it cannot be created at application startup. This component could only be instantiated passing some objects which are only available a bit later in application lifecycle (they are not other IoC registered services, however) [see note below].

  • Is registering a component in a IoC container after the initial configuration (run in app startup) a bad practice?
  • How to accomplish it without directly referencing the container? Should I abstract a registration service?
  • There is a better approach to support the scenario?

NOTE about the actual scenario
The component I would like to put in the container is initialized with a particular instance of an UI control (it is basically an adapter), hence I have to manually create the component instance and register it in the container.
I would have done this at application startup, but unfortunately I don't have the UI control instance available yet (nor can I create it by myself).
Even at later time, I cannot reach the UI control instance from the surface of other components without knowing their concrete class.
For this reason I thought I could put the responsibility for the adapter registration into the class which owns the UI control.

My initial scenario:

public interface IDockManager { ... }
public class AcmeDockManagerAdapter : IDockManager  {
    public AcmeDockManager(DockControl control) { ... }
    ...
}

public class ShellViewModel { ... }
public class ShellView : Window { 
    internal DockControl theDockControl;
} 

public class AnotherViewModel {
     AnotherViewModel(IDockManager dockManager) { ... }
}

The solution I'm unconfortable with:

public class ShellView : Window { 
    internal DockControl theDockControl;
    public ShellView () {
        InitializeComponents();
        var dockManager = new AcmeDockManagerAdapter(theDockControl);
        //registration in the container
    }
} 
2

2 Answers

1
votes

You could register a "lazy wrapper" instead. Such a wrapper implements the same interface and can be instantiated immediately, but will internally postpone the creation of the actual component that does the work. Take a look at ploeh's example of a LazyOrderShipper or LazyOrderShipper2.

edit: If I understand correctly, you're just trying to connect your views to your viewmodels, MVVM-style. I prefer to let the container handle viewmodel construction, but to do the view construction and viewmodel wiring myself. My start-up code woul look like this:

var mainViewModel = container.Get<MainViewModel>();
var mainView = new MainView(mainViewModel);
Application.Run(mainView);

And inside the MainView constructor I'd take care of child controls which require their own viewmodel:

   public MainView(MainViewModel viewModel)
   {
       // link "subviews" to "subviewmodels"
       this.SomeChildControl.ViewModel = viewModel.SomeChildViewModel;

       // normal MVVM property wiring
       viewModel.TitleChanged += delegate { this.Text = viewModel.Title; };
       ...
   }

If you strictly follow the MVVM approach, then you should not have to register any view with the container. Anything that "needs to talk to the view" really needs to talk to underlying viewmodel instead. (Things get more interesting when you want to allow for pluggable views in tabbed interface or docked window GUI, but that's another story.)

1
votes

The solution the way I understand the question, is relatively simple - provide theDockControl from outside. I know that's messing with autogenerated WinForms/WPF/whatever-you're-using crap, but I'm afraid there's no pretty solutions here.