views:

448

answers:

2

I'm starting out with MVVM, and I'm starting to understand things. I'm currently experimenting with the Cinch framework, though I'm not committed to it as of yet.
I was injecting the ViewModels into the Views using by having a reference to the ViewModel in the codebehind of the view, with the property having a [Dependency] on it, and in the setter it sets the DataContext to the right view, using Unity. Neat trick, I thought.

I'm trying to get my app to work as a single Window, with injected views (As opposed to multiple windows and dealing with opening\closing them) I changed my views from Windows to UserControls, and added a to the main window. That worked, but the ViewModel was never injected, presumably because the XAML doesn't use Container.Resolve to create the view, as when I created the view and added it manually in the code-behind using Resolve, the [Dependency] was created.

How can I set up my window, so that if I add a view through XAML, or the view gets changed as a result of a UI action etc, it gets it through Unity, so that it can work its magic?

+1  A: 

The way to solve your problem is to make your window to have a ViewModel as well, with ViewModels of UserControls exposes as properties on it. Then in your XAML for a window you'd simply use Binding mechanism to bind UserControl's DataContexts to proper properties of your your main ViewModel. And since that main ViewModel is resolved from Unity container it would have all other ViewModel-s injected as needed.

PL
Thanks, I think I have it now. I have a <User Control Content={Binding MainViewModel}"/>with a DataTemplate that applies the appropriate View to display it.Putting a ViewModel class as content of a UserControl was confusing me ;D
Kage
+1  A: 

This problem is normally solved using Regions and the RegionManager. In the main window ViewModel, a set of Regions is created and added to the RegionManager. Then ViewModels can be Resolved and added to the Region.Views collection.

In XAML, the Region is normally injected by having the ItemsSource property of an ItemsControl bound to the region property of the main ViewModel.

So, in the main screen ViewModel you would have something like this:

    public class TestScreenViewModel
{
 public const string MainRegionKey = "TestScreenViewModel.MainRegion";

 public TestScreenViewModel(IUnityContainer container, IRegionManager regionManager)
 {
  this.MainRegion = new Region();
  regionManager.Regions.Add(MainRegionKey, this.MainRegion);
 }

 public Region MainRegion { get; set; }
}

This would be Resolved normally in your IModule

     #region IModule Members

 public void Initialize()
 {
  RegisterViewsAndServices();

  var vm = Container.Resolve<SelectorViewModel>();
  var mainScreen = Container.Resolve<TestScreenViewModel>();
  mainScreen.MainRegion.Add(vm);

  var mainView = ContentManager.AddContentView("Test harness", mainScreen);
 }

 #endregion

And the XAML representation of your template looking something like

    <DataTemplate DataType="{x:Type TestModule:TestScreenViewModel}">
    <ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto">
        <StackPanel>
            <ItemsControl ItemsSource="{Binding Path=MainRegion.Views}" />
        </StackPanel>
    </ScrollViewer>
</DataTemplate>
Mark Green