views:

903

answers:

1

Hello,

I'm fairly new to the Silverlight and the MVVM / Prism pattern so this may be a stupid question.

I have a View which has custom controls within it. These custom controls are actually Views too and have ViewModels to drive them.

Currently to add these 'child' Views to the View I'm using (see Fig.1) and then in the ViewModel I have an Initialise() method which resolves the child View and injects it (see Fig.2).

Fig.1

<UserControl
x:Class="ProjectA.Module.Screens.Views.PlatformScreenView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Regions="clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly=Microsoft.Practices.Composite.Presentation"
>
<Grid x:Name="LayoutRoot">
    <ContentControl
        Regions:RegionManager.RegionName="FeaturesSelectionControl"
        />
</Grid>

Fig.2

public void Initialise()
{
    IRegion featuresRegion = _regionManager.Regions["FeaturesSelectionControl"];
    featuresRegion.Add(_container.Resolve<IFeatureSelectionControlViewModel>().View);
}

My question is do I have to do this for every control I want to add? I understand why it works this way but it seems like quite a bit of code and also I need to keep track of all the region names and ensure I don't have any clashes etc. Is there a simpler way of doing this without regions and just in XAML?

I've seen a snippet of XAML on StackOverflow here but not sure how it works and if it's what I want -

<ContentControl Content="{Binding SmartFormViewModel}"/>

Any help is much appreciated.

James

+1  A: 

Edit after clarification:

It appears you don't want to use RegionManager at all, which is fine. What I would suggest then is this:

Create an interface for your Modules to use to register view creation methods:

public interface IViewRegistry
{
     void RegisterMainView(Func<object> ViewCreationDelegate);
}

Your modules will use this like this:

public MyModule : IModule
{
     IViewRegistry _registry;
     public MyModule(IViewRegistry registry)
     {
          _registry = registry;
     }

     public void Initialize()
     {
          _registry.RegisterMainView(() =>
          {
               var vm = new MyViewModel();
               var view = new MyView();
               var view.DataContext = vm;
               return view;
          });
     }
}

Then in your shell, you first implement the view registry (this is a simplification... you'd probably want something more robust)

public ViewRegistry : IViewRegistry
{
     public static List<Func<object>> ViewFactories
     {
           get { return _viewFactories; }
     }
     static List<Func<object>> _viewFactories = new List<Func<object>>();
     public void RegisterMainView(Func<object> viewFactory)
     {
          _viewFactories.Add(viewFactory);
     }
}

And lastly, here's how your shell would show that stuff. Here's its ViewModel first:

public ShellViewModel : ViewModel
{
     public ObservableCollection<object> MainViews
     {
          ...
     }

     public ShellViewModel()
     {
          MainViews = new ObservableCollection<object>(ViewRegistry.Select(factory => factory()));
     }
}

And here's your View (look ma, no RegionManager!):

<UserControl ...>
   <Grid>
     <ItemsControl ItemsSource="{Binding MainViews}" />
   </Grid>
</UserControl>

The region manager sort of attempts to give you everything I've written here, plus a lot of extensibility points, but if you don't like RegionManager or you find it doesn't fit your needs for some reason, this is how you would do this in Silverlight without it.


Further Edits:

After some more commentary from the OP, I think I understand that the OP just wants to show a view within another view without having to use RegionManager. It appears the OP is using RegionManager to show every view on the screen, which is probably overkill.

The scenario I was given included an Address View and associated ViewModel being used from a different parent control. This is what I do (whether right or wrong):

<UserControl x:Class="Views.MyParentView" ...>
<StackPanel>
     <TextBlock>Blah blah blah some other stuff... blah blah</TextBlock>
     <myViews:AddressView DataContext="{Binding AddressVM}" />
</StackPanel>
</UserControl>

And here's the parent view's viewModel:

public ParentViewModel : ViewModel
{
     public AddressViewModel AddressVM
     {
          ...
     }

     public ParentViewModel()
     {
          AddressVM = new AddressViewModel();
     }
}

That's it. This prevents you from having to work too hard to show these views.

RegionManager is really appropriate for decoupling the parent container view from the subview, but if both of these live in the same place (same module / assembly) there is no reason to use RegionManager to show these views.

Anderson Imes
I didn't think that type specific DataTemplate's work in Silverlight? However I haven't checked Sliverlight 4.0. yet.
David Padbury
Crap, you got me. I didn't notice the word Silverlight plastered *all over your post* somehow. My mistake. You will likely then want to use a presenter... the method you presented in your post <ContentControl Content="{Binding SmartFormViewModel}" /> would only work with DataTemplating, which is why I went that direction in my answer. I'll think about this a bit and repost.
Anderson Imes
JestriK
You certainly don't have to use regions. You can just have a collection of Views available and bind your master view to that. Is that what you want? I'll post an example. I didn't understand that you didn't want to use RegionManager.
Anderson Imes
Yes, I _think_ that's what I want. Maybe an example might help me further articulate what I'm trying to acheieve - say I had a very simple 'postal address' user control (consisting of a Model, ViewModel and View) that I used on various Silverlight screens. Every time I wanted to use this control would I have to define a region in XAML and then inject it with the RegionManager in C#? Is this the recommended way of adding user controls in MVVM?
JestriK
It's really up to you. There are a lot of ways to skin this cat. I think for your particular case that's probably overkill. For simple things like that, there's no reason not to statically declare your usercontrol on your view and bind it to a viewmodel that is a property of your bigger viewmodel for the view. If that sounds more like what you want, I can post a short sample on that too.
Anderson Imes
Brilliant! Your last edit with the AddressView answered my problem perfectly. Sorry it took so many attempts for me to clearly explain what I wanted!Your comment about it being overkill to use regions for just user controls is exactly why I raised this question and the solution is actually very simple, but as I'm sure you know it's difficult finding your way around when you're starting out in a new technology / pattern.Thank you **so** much for your time, it's really appreciated.
JestriK
Glad I could help! You were right to think that was too complicated. Hopefully I explained simply enough when to use each strategy, but let me know if it's not clear enough.
Anderson Imes